SETNS - Linux手册页
Linux程序员手册 第2部分
更新日期: 2020-08-13
名称
setns-将线程与名称空间重新关联
语法
#define _GNU_SOURCE /* See feature_test_macros(7) */ #include <sched.h> int setns(int fd, int nstype);
说明
setns()系统调用允许调用线程移动到不同的名称空间。 fd参数是以下之一:
- *
- 文件描述符,该文件描述符引用/ proc / [pid] / ns /目录中的魔术链接之一(或对该链接的绑定安装);
- *
- PID文件描述符(请参见pidfd_open(2))。
每种情况下,nstype参数的解释都不同。
fd refers to a /proc/[pid]/ns/ link
如果fd引用/ proc / [pid] / ns /链接,则setns()将调用线程与与该链接关联的名称空间重新关联,但要遵守nstype参数施加的任何约束。在这种用法中,对setns()的每次调用仅更改了调用者的名称空间成员资格之一。
nstype参数指定调用线程可以与之关联的名称空间的类型。此参数可以具有以下值之一:
- 0
- 允许连接任何类型的名称空间。
- CLONE_NEWCGROUP(since Linux 4.6)
- fd必须引用cgroup命名空间。
- CLONE_NEWIPC(since Linux 3.0)
- fd必须引用IPC名称空间。
- CLONE_NEWNET(since Linux 3.0)
- fd必须引用网络名称空间。
- CLONE_NEWNS(since Linux 3.8)
- fd必须引用安装命名空间。
- CLONE_NEWPID(since Linux 3.8)
- fd必须引用后代PID名称空间。
- CLONE_NEWTIME(since Linux 5.6)
- fd必须引用时间名称空间。
- CLONE_NEWUSER(since Linux 3.8)
- fd必须引用用户名称空间。
- CLONE_NEWUTS(since Linux 3.0)
- fd必须引用UTS命名空间。
如果调用者知道(或不关心)fd引用哪种类型的名称空间,则将nstype指定为0就足够了。如果调用者不知道fd引用哪种类型的名称空间,并且想要确保该名称空间是特定类型,则为nstype指定非零值很有用。 (如果文件描述符是由另一个进程打开的,例如通过UNIX域套接字传递给调用者,则调用者可能不知道fd所引用的命名空间的类型。)
fd is a PID file descriptor
从Linux 5.8开始,fd可以引用从pidfd_open(2)或clone(3)获得的PID文件描述符。在这种用法中,setns(2)原子地将调用线程移动到一个或多个与fd引用的线程相同的名称空间中。
nstype参数是一个位掩码,它是通过将上面列出的一个或多个CLONE_NEW *名称空间常量与或在一起而指定的。调用者被移动到nstype中指定的每个目标线程的名称空间中;其余名称空间中的调用方成员身份保持不变。
例如,以下代码会将调用者移动到与PID 1234相同的用户,网络和UTS命名空间中,但将使调用者的其他命名空间成员身份保持不变:
int fd = pidfd_open(1234, 0); setns(fd, CLONE_NEWUSER | CLONE_NEWNET | CLONE_NEWUTS);
Details for specific namespace types
与特定名称空间类型重新关联时,请注意以下详细信息和限制:
- User namespaces
- 将自身与用户名称空间重新关联的进程必须在目标用户名称空间中具有CAP_SYS_ADMIN功能。 (这必然意味着只能加入后代用户名称空间。)成功加入用户名称空间后,将授予该名称空间中所有进程的功能,无论其用户ID和组ID如何。
- 多线程进程可能无法使用setns()更改用户名称空间。
- 不允许使用setns()重新输入调用者的当前用户名称空间。这样可以防止已放弃功能的调用者通过调用setns()重新获得这些功能。
- 出于安全考虑,如果一个进程与另一个进程共享文件系统相关的属性(其共享由clone(2)CLONE_FS标志控制的属性),则该进程无法加入新的用户命名空间。
- 有关用户名称空间的更多详细信息,请参见user_namespaces(7)。
- Mount namespaces
- 更改装入名称空间要求调用者在其自己的用户名称空间中拥有CAP_SYS_CHROOT和CAP_SYS_ADMIN功能,并在拥有目标装入名称空间的用户名称空间中具有CAP_SYS_ADMIN。
- 如果某个进程与另一个进程共享文件系统相关的属性(其共享由clone(2)CLONE_FS标志控制的属性),则该进程无法加入新的装载命名空间。
- 有关用户名称空间和安装名称空间的交互的详细信息,请参见user_namespaces(7)。
- PID namespaces
- 为了使自己与新的PID名称空间关联,调用者必须在其自己的用户名称空间和拥有目标PID名称空间的用户名称空间中都具有CAP_SYS_ADMIN功能。
- 重新关联PID名称空间与其他名称空间类型有些不同。将调用线程与PID名称空间重新关联只会更改随后将创建调用者的子进程的PID名称空间;它不会更改调用者本身的PID名称空间。
- 仅当目标PID名称空间是调用方当前PID名称空间的后代(子代,孙代等)或与之相同时,才允许与PID名称空间重新关联。
- 有关PID名称空间的更多详细信息,请参见pid_namespaces(7)。
- Cgroup namespaces
- 为了将自己与新的cgroup命名空间重新关联,调用者必须在其自己的用户命名空间和拥有目标cgroup命名空间的用户命名空间中均具有CAP_SYS_ADMIN功能。
- 使用setns()更改调用方的cgroup命名空间不会更改调用方的cgroup成员身份。
- Network, IPC, time, and UTS namespaces
- 为了将自己与新的网络,IPC,时间或UTS命名空间重新关联,调用者必须在其自己的用户命名空间和拥有目标命名空间的用户命名空间中均具有CAP_SYS_ADMIN功能。
返回值
成功时,setns()返回0。失败时,返回-1并设置errno以指示错误。
错误说明
- EBADF
- fd不是有效的文件描述符。
- EINVAL
- fd引用其类型与nstype中指定的类型不匹配的名称空间。
- EINVAL
- 将线程与指定的名称空间重新关联存在问题。
- EINVAL调用者尝试加入祖先(父母,祖父母等)PID名称空间。
- EINVAL
- 调用者尝试加入已经是其成员的用户名称空间。
- EINVAL
- 调用方与其他进程共享文件系统(CLONE_FS)状态(尤其是根目录),并尝试加入新的用户名称空间。
- EINVAL
- 调用方是多线程的,并尝试加入新的用户名称空间。
- EINVAL
- fd是PID文件描述符,并且nstype无效(例如,它为0)。
- ENOMEM
- 无法分配足够的内存来更改指定的名称空间。
- EPERM
- 调用线程没有此操作所需的功能。
- ESRCH
- fd是PID文件描述符,但它所引用的进程不再存在(即,它已终止并等待)。
版本
setns()系统调用首先出现在Linux内核3.0中。库支持在版本2.14中添加到glibc。
遵循规范
setns()系统调用是特定于Linux的。
示例
下面的程序带有两个或多个参数。第一个参数指定现有/ proc / [pid] / ns /目录中名称空间文件的路径名。其余参数指定命令及其参数。程序打开名称空间文件,使用setns()加入该名称空间,并在该名称空间内执行指定的命令。
以下shell会话在clone(2)手册页(称为二进制文件,称为newuts)中结合CLONE_NEWUTS示例程序演示了该程序(作为名为ns_exec的二进制文件编译)的使用。
我们从在后台执行clone(2)中的示例程序开始。该程序在单独的UTS名称空间中创建一个子级。子进程在其名称空间中更改主机名,然后两个进程都在其UTS名称空间中显示主机名,以便我们可以看到它们是不同的。
$ su # Need privilege for namespace operations Password: # ./newuts bizarro & [1] 3549 clone() returned 3550 uts.nodename in child: bizarro uts.nodename in parent: antero # uname -n # Verify hostname in the shell antero
然后,我们运行下面显示的程序,并使用它执行外壳程序。在该外壳程序内,我们确认主机名是第一个程序创建的子代设置的主机名:
# ./ns_exec /proc/3550/ns/uts /bin/bash # uname -n # Executed in shell started by ns_exec bizarro
Program source
#define _GNU_SOURCE #include <fcntl.h> #include <sched.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> #define errExit(msg) do { perror(msg); exit(EXIT_FAILURE); \ } while (0) int main(int argc, char *argv[]) { int fd; if (argc < 3) { fprintf(stderr, "%s /proc/PID/ns/FILE cmd args...\n", argv[0]); exit(EXIT_FAILURE); } /* Get file descriptor for namespace; the file descriptor is opened with O_CLOEXEC so as to ensure that it is not inherited by the program that is later executed. */ fd = open(argv[1], O_RDONLY | O_CLOEXEC); if (fd == -1) errExit("open"); if (setns(fd, 0) == -1) /* Join that namespace */ errExit("setns"); execvp(argv[2], &argv[2]); /* Execute a command in namespace */ errExit("execvp"); }
出版信息
这个页面是Linux手册页项目5.08版的一部分。有关项目的说明、有关报告错误的信息以及此页面的最新版本,请访问https://www.kernel.org/doc/man-pages/。