sethostname系统调用及示例

我们来深入学习 sethostname 系统调用及示例

我们来深入学习 sethostname 系统调用及示例

1. 函数介绍

在 Linux 系统(以及大多数 Unix-like 系统)中,每台计算机都有一个唯一的标识符,叫做 主机名 (hostname)。这个主机名用于在网络中识别这台机器。例如,当你在命令行输入 hostname 时,它会显示当前机器的主机名。

sethostname 系统调用的作用就是设置这台运行着 Linux 内核的计算机的 主机名。这是一个系统级别的设置,会影响整个机器,而不仅仅是调用它的那个进程。

简单来说,sethostname 就是让你用程序来给你的 Linux 电脑“改名字”。

重要提示
1. 需要权限:修改主机名是一个特权操作,通常只有 root 用户(超级用户)才有权限执行 sethostname。普通用户尝试调用它会失败。
2. 影响范围:主机名是系统全局的属性。一旦通过 sethostname 修改,系统中所有查询主机名的地方(如 gethostnameuname 命令)都会返回新的名字。
3. 持久性:通过 sethostname 设置的主机名是临时的。它只在当前的系统运行会话(直到关机或重启)中有效。系统重启后,主机会从配置文件(如 /etc/hostname)中读取并恢复原来的主机名。

2. 函数原型

#include <unistd.h>      // 包含系统调用声明
#include <sys/utsname.h> // 有时也需要,包含主机名长度常量

int sethostname(const char *name, size_t len);

3. 功能

设置内核维护的主机名。这个主机名可以通过 gethostname 系统调用或 uname 系统调用来查询。

4. 参数

  • name:
    • const char * 类型。
    • 一个指向以 null 结尾的字符串的指针,该字符串包含了新的主机名。
  • len:
    • size_t 类型。
    • 指定 name 字符串中实际包含的字符数(不包括末尾的 null 终止符 \0)。通常使用 strlen(name) 来获取这个长度。

5. 返回值

  • 成功: 返回 0。
  • 失败: 返回 -1,并设置全局变量 errno 来指示具体的错误原因。

6. 错误码 (errno)

  • EFAULTname 指向了调用进程无法访问的内存地址。
  • EINVALlen 超过了系统允许的最大主机名长度(通常是 MAXHOSTNAMELEN,在 <sys/utsname.h> 或 <limits.h> 中定义,常见值是 64 或 256)。
  • EPERM: 调用进程没有权限(不是 root 用户)来更改主机名。

7. 相似函数或关联函数

  • gethostname: 用于获取当前的主机名。
  • uname: 系统调用,可以获取包括主机名在内的系统信息(系统名、版本、机器类型等)。对应的命令行工具也叫 uname
  • hostname: 命令行工具,用于显示或设置系统的主机名。它在底层就是调用 sethostname 和 gethostname
  • /etc/hostname: 在许多 Linux 发行版中,系统启动时会从这个文件读取主机名并使用 sethostname 设置。

8. 示例代码

下面的示例演示了如何使用 sethostname 来修改主机名,以及如何使用 gethostname 来查询它。

#define _GNU_SOURCE // 启用 GNU 扩展
#include <stdio.h>
#include <unistd.h>
#include <sys/utsname.h> // 包含 gethostname, uname, MAXHOSTNAMELEN
#include <string.h>
#include <errno.h>
#include <stdlib.h>

#define HOST_NAME_BUFFER_SIZE MAXHOSTNAMELEN // 通常定义在 <sys/utsname.h> 或 <limits.h>

void print_current_hostname(const char* context) {
    char hostname[HOST_NAME_BUFFER_SIZE];
    if (gethostname(hostname, sizeof(hostname)) == 0) {
        printf("[%s] Current hostname is: '%s'\n", context, hostname);
    } else {
        perror("gethostname");
    }
}

int main(int argc, char *argv[]) {
    char new_hostname[HOST_NAME_BUFFER_SIZE];
    struct utsname uname_info; // 用于 uname 系统调用

    printf("--- Demonstrating sethostname ---\n");

    // 1. 显示当前主机名
    print_current_hostname("Initial");

    // 2. 使用 uname 系统调用获取更详细的系统信息
    if (uname(&uname_info) == 0) {
        printf("[uname] System name: %s\n", uname_info.sysname);
        printf("[uname] Node name (hostname): %s\n", uname_info.nodename);
        printf("[uname] Release: %s\n", uname_info.release);
        printf("[uname] Version: %s\n", uname_info.version);
        printf("[uname] Machine: %s\n", uname_info.machine);
    } else {
        perror("uname");
    }

    // 3. 检查命令行参数
    if (argc != 2) {
        printf("Usage: %s <new_hostname>\n", argv[0]);
        printf("Note: You need to run this program as root to change the hostname.\n");
        exit(EXIT_FAILURE);
    }

    strncpy(new_hostname, argv[1], sizeof(new_hostname) - 1);
    new_hostname[sizeof(new_hostname) - 1] = '\0'; // 确保 null 终止

    printf("\nAttempting to change hostname to: '%s'\n", new_hostname);

    // 4. 调用 sethostname
    // 注意:这需要 root 权限
    if (sethostname(new_hostname, strlen(new_hostname)) == -1) {
        perror("sethostname");
        if (errno == EPERM) {
            printf("Error: Permission denied. You must run this program as root (e.g., using sudo).\n");
        }
        exit(EXIT_FAILURE);
    }

    printf("sethostname('%s') succeeded.\n", new_hostname);

    // 5. 再次显示主机名以验证更改
    print_current_hostname("After sethostname");

    // 6. 再次使用 uname 验证
    if (uname(&uname_info) == 0) {
        printf("[uname after change] Node name (hostname): %s\n", uname_info.nodename);
    }

    printf("\n--- Important Notes ---\n");
    printf("1. The hostname change is TEMPORARY and only lasts until the system is rebooted.\n");
    printf("2. To make the change persistent, you need to update configuration files like /etc/hostname.\n");
    printf("3. You need ROOT privileges to call sethostname.\n");

    return 0;
}

编译和运行:

# 假设代码保存在 sethostname_example.c 中
gcc -o sethostname_example sethostname_example.c

# 1. 不带参数运行 (会报错)
./sethostname_example

# 2. 带一个参数运行,但没有 root 权限 (会失败)
./sethostname_example MyNewTempHostname

# 3. 带一个参数运行,并使用 sudo 获取 root 权限 (应该成功)
# 注意:请将 MyNewTempHostname 替换为你想要的主机名
sudo ./sethostname_example MyNewTempHostname

# 4. 验证更改
hostname
uname -n

预期输出 (使用 sudo 运行):

# 假设原始主机名是 'old-hostname'
$ sudo ./sethostname_example NewTempName
--- Demonstrating sethostname ---
[Initial] Current hostname is: 'old-hostname'
[uname] System name: Linux
[uname] Node name (hostname): old-hostname
[uname] Release: 5.4.0-XX-generic
[uname] Version: #XX-Ubuntu SMP ...
[uname] Machine: x86_64

Attempting to change hostname to: 'NewTempName'
sethostname('NewTempName') succeeded.
[After sethostname] Current hostname is: 'NewTempName'
[uname after change] Node name (hostname): NewTempName

--- Important Notes ---
1. The hostname change is TEMPORARY and only lasts until the system is rebooted.
2. To make the change persistent, you need to update configuration files like /etc/hostname.
3. You need ROOT privileges to call sethostname.

# 验证命令
$ hostname
NewTempName
$ uname -n
NewTempName

重启后:
如果你重启系统,主机会名会恢复到 /etc/hostname 文件中配置的名称。

总结:
sethostname 是一个用于修改系统全局主机名的系统调用。它需要 root 权限,并且修改是临时的。理解它有助于你编写需要动态管理主机名的系统管理工具。在日常使用中,hostname 命令是更常见的设置主机名的方式。

此条目发表在linux文章分类目录。将固定链接加入收藏夹。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注