setns
C コードは正常に動作し、名前空間に正しく入りますが、Go コードは、名前空間に入る呼び出しから常に EINVAL を返すようmnt
です。.so
Go1.2
でいくつかの順列 ( cgo および external を使用した埋め込み C コードを含む) と現在のヒントを試しました1.3
。
コードをステップ実行するgdb
と、両方のシーケンスがまったく同じ方法で呼び出さsetns
れlibc
ていることがわかります (または、私にはそう見えます)。
問題と思われるものを以下のコードに落とし込みました。私は何を間違っていますか?
設定
クイック ビジーボックス コンテナーを開始するためのシェル エイリアスがあります。
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