C 代码工作正常并正确进入命名空间,但 Go 代码似乎总是从setns
调用中返回 EINVAL 以进入mnt
命名空间。我.so
在 Go 上尝试了许多排列(包括带有 cgo 和 external 的嵌入式 C 代码)1.2
,1.3
以及当前的提示。
单步执行代码gdb
表明两个序列都setns
以libc
完全相同的方式调用(或者在我看来)。
我已经将似乎是问题的问题归结为下面的代码。我究竟做错了什么?
设置
我有一个用于启动快速busybox容器的shell别名:
alias startbb='docker inspect --format "{{ .State.Pid }}" $(docker run -d busybox sleep 1000000)'
运行后,startbb
将启动一个容器并输出它的 PID。
lxc-checkconfig
输出:
Found kernel config file /boot/config-3.8.0-44-generic
--- Namespaces ---
Namespaces: enabled
Utsname namespace: enabled
Ipc namespace: enabled
Pid namespace: enabled
User namespace: missing
Network namespace: enabled
Multiple /dev/pts instances: enabled
--- Control groups ---
Cgroup: enabled
Cgroup clone_children flag: enabled
Cgroup device: enabled
Cgroup sched: enabled
Cgroup cpu account: enabled
Cgroup memory controller: missing
Cgroup cpuset: enabled
--- Misc ---
Veth pair device: enabled
Macvlan: enabled
Vlan: enabled
File capabilities: enabled
uname -a
产生:
Linux gecko 3.8.0-44-generic #66~precise1-Ubuntu SMP Tue Jul 15 04:01:04 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux
工作 C 代码
以下 C 代码可以正常工作:
#include <errno.h>
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
main(int argc, char* argv[]) {
int i;
char nspath[1024];
char *namespaces[] = { "ipc", "uts", "net", "pid", "mnt" };
if (geteuid()) { fprintf(stderr, "%s\n", "abort: you want to run this as root"); exit(1); }
if (argc != 2) { fprintf(stderr, "%s\n", "abort: you must provide a PID as the sole argument"); exit(2); }
for (i=0; i<5; i++) {
sprintf(nspath, "/proc/%s/ns/%s", argv[1], namespaces[i]);
int fd = open(nspath, O_RDONLY);
if (setns(fd, 0) == -1) {
fprintf(stderr, "setns on %s namespace failed: %s\n", namespaces[i], strerror(errno));
} else {
fprintf(stdout, "setns on %s namespace succeeded\n", namespaces[i]);
}
close(fd);
}
}
用 编译后gcc -o checkns checkns.c
,输出sudo ./checkns <PID>
为:
setns on ipc namespace succeeded
setns on uts namespace succeeded
setns on net namespace succeeded
setns on pid namespace succeeded
setns on mnt namespace succeeded
失败的 Go 代码
相反,下面的 Go 代码(应该是相同的)不能很好地工作:
package main
import (
"fmt"
"os"
"path/filepath"
"syscall"
)
func main() {
if syscall.Geteuid() != 0 {
fmt.Println("abort: you want to run this as root")
os.Exit(1)
}
if len(os.Args) != 2 {
fmt.Println("abort: you must provide a PID as the sole argument")
os.Exit(2)
}
namespaces := []string{"ipc", "uts", "net", "pid", "mnt"}
for i := range namespaces {
fd, _ := syscall.Open(filepath.Join("/proc", os.Args[1], "ns", namespaces[i]), syscall.O_RDONLY, 0644)
err, _, msg := syscall.RawSyscall(308, uintptr(fd), 0, 0) // 308 == setns
if err != 0 {
fmt.Println("setns on", namespaces[i], "namespace failed:", msg)
} else {
fmt.Println("setns on", namespaces[i], "namespace succeeded")
}
}
}
相反,运行sudo go run main.go <PID>
会产生:
setns on ipc namespace succeeded
setns on uts namespace succeeded
setns on net namespace succeeded
setns on pid namespace succeeded
setns on mnt namespace failed: invalid argument