C code works fine and correctly enters the namespace, but Go code always returns EINVAL from the setns call to enter the mnt namespace. I tried a few permutations (including C inline code using cgo and external .so ) on Go 1.2 , 1.3 and the current tip.
Running the code in gdb shows that both sequences call setns in libc exactly the same way (or so it seems to me).
I boiled what seemed to be related to the code below. What am I doing wrong?
Customization
I have a shell alias for running busybox fast containers:
alias startbb='docker inspect --format "{{ .State.Pid }}" $(docker run -d busybox sleep 1000000)'
After starting, startbb will start the container and display its PID.
lxc-checkconfig outputs:
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 produces:
Linux gecko 3.8.0-44-generic
Work Code C
The following C code works fine:
#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); } }
After compiling with gcc -o checkns checkns.c output of 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
Transition Code Error
Conversely, the following Go code (which should be identical) does not work either:
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") } } }
Instead, running sudo go run main.go <PID> calls:
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