setns -
スレッドに名前空間を関連付けしなおす
#define _GNU_SOURCE /* feature_test_macros(7) 参照 */
#include <sched.h>
int setns(int fd, int nstype);
名前空間を参照するファイルディスクリプターを指定すると、
呼び出したスレッドにその名前空間を関連付けしなおす。
fd 引き数は、
/proc/[pid]/ns/
ディレクトリ内の名前空間エントリー
のいずれかを参照するファイルディスクリプターである。
/proc/[pid]/ns/ の詳細は
namespaces(7)
を参照。
nstype
引き数で指定された制限の範囲内で、
呼び出したスレッドに
fd
に対応する名前空間を関連付けしなおす。
nstype
引き数は、呼び出したスレッドがどのタイプの名前空間を
関連付けしなおすことができるかを指定する。
この引き数には以下のいずれかの値を指定できる。
- 0
- どのタイプの名前空間も関連付けることができる。
-
CLONE_NEWIPC (Linux 3.0 以降)
-
fd は IPC
名前空間を参照していなければならない。
-
CLONE_NEWNET (Linux 3.0 以降)
-
fd
はネットワーク名前空間を参照していなければならない。
-
CLONE_NEWNS (Linux 3.8 以降)
-
fd
はマウント名前空間を参照していなければならない。
-
CLONE_NEWPID (Linux 3.8 以降)
-
fd は子孫の PID
名前空間を参照していなければならない。
-
CLONE_NEWUSER (Linux 3.8 以降)
-
fd
はユーザー名前空間を参照していなければならない。
-
CLONE_NEWUTS (Linux 3.0 以降)
-
fd は UTS
名前空間を参照していなければならない。
呼び出し側が
fd
がどのタイプの名前空間を参照しているかを知っている
(もしくは気にする必要がない)
場合には、
nstype に 0
を指定すれば十分
である。呼び出し側が
fd
がどのタイプの名前空間を参照しているかを
知っておらず、かつ、特定のタイプの名前空間であることを保証したい場合、
nstype に 0
以外の値を指定するとよい。
(ファイルディスクリプターが別の
プロセスによりオープンされ、例えば、UNIX
ドメインソケット経由で呼び出し
側に渡された場合などでは、呼び出し側が
fd
がどのタイプの名前空間を
参照しているかを知らない可能性がある。)
CLONE_NEWPID は他の
nstype
値の場合と少し違った動作をする。
呼び出し元スレッドを
PID
名前空間に関連付けし直すと、
呼び出し元の子プロセスが作成される
PID
名前空間が変更されるだけである。
呼び出し元自身の PID
名前空間は変更されない。
PID
名前空間を関連付けし直すことができるのは、
fd で指定された PID
名前空間が呼び出し元の
PID 名前空間の子孫
(子プロセス、孫プロセスなど)
の場合だけである。 PID
名前空間の詳細は
pid_namespaces(7) を参照。
プロセスが自分自身をユーザー名前空間に再関連付けするには、
そのプロセスは変更後のユーザー名前空間において
CAP_SYS_ADMIN
ケーパビリティを持っていなければならない。
ユーザー名前空間への参加に成功すると、
そのユーザー ID
やグループ ID
に関わらず、
プロセスにはその名前空間におけるすべてのケーパビリティが認められる。
マルチスレッドのプロセスは
setns()
でユーザー名前空間を変更できない。
setns()
を使って、呼び出し元が現在のユーザー名前空間に再度入ることは認められていない。
これにより、
いくつかのケーパビリティを外した呼び出し元が
setns()
を呼び出すことでそれらのケーパビリティを再度得ることを防ぐことができる。
セキュリティ上の理由から、
ファイルシステム関連の属性
(共有が
clone(2) CLONE_FS
フラグで制御される属性)
を別のプロセスと共有している場合、
プロセスは新しいユーザー名前空間に参加できない。
ユーザー名前空間の詳細は
user_namespaces(7) を参照。
プロセスがマルチスレッドの場合、そのプロセスを新しいマウント名前空間に関連付けし直すことは許可されていない。
マウント名前空間を変更するには、呼び出し元のプロセスが、
自分自身のユーザー名前空間において
CAP_SYS_CHROOT と
CAP_SYS_ADMIN
の両方のケーパビリティを持っており、
変更後のマウント名前空間で
CAP_SYS_ADMIN
ケーパビリティを持っていなければならない。
ユーザー名前空間とマウント名前空間の関係の詳細は
user_namespaces(7) を参照。
成功すると
setns() は 0
を返す。
失敗すると、 -1
が返され、
errno
にエラーを示す値が設定される。
- EBADF
-
fd
が有効なファイルディスクリプターではない。
- EINVAL
-
fd が nstype
で指定されたタイプと一致しない名前空間を参照している。
- EINVAL
- スレッドを指定された名前空間に関連付けし直す際に問題が発生した。
- EINVAL
- 呼び出し元が先祖
(親や親の親など) の PID
名前空間に参加しようとした。
- EINVAL
- 自分がすでにメンバーとなっているユーザー名前空間に参加しようとした。
- EINVAL
- 呼び出し元が他のプロセスとファイルシステム状態
(特に root ディレクトリ)
を共有していて (
CLONE_FS)、
新しいユーザー名前空間に参加しようとした。
- EINVAL
- 呼び出し元プロセスがマルチスレッドで、新しいユーザー名前空間に参加しようとした。
- ENOMEM
- 指定された名前空間に変更するのに必要なメモリーが割り当てられない。
- EPERM
- 呼び出し元スレッドはこの操作を行うのに必要なケーパビリティを持っていなかった。
setns()
システムコールはカーネル
3.0 で Linux
に初めて登場した。
ライブラリによるサポートは
glibc バージョン 2.14
を追加された。
setns() システムコールは
Linux 固有である。
新しいスレッドが
clone(2)
を使って作成された際に共有できる全ての属性を、
setns()
を使って変更できるわけではない。
以下のプログラムは 2
つ以上の引き数を取る。
最初の引き数には、
既存の
/proc/[pid]/ns/
ディレクトリの名前空間ファイルのパス名を指定する。
残りの引き数は、コマンドとその引き数を指定する。
このプログラムは名前空間ファイルをオープンし、
setns()
を使って名前空間に参加し、
指定されたコマンドをその名前空間内で実行する。
以下のシェルセッションでは、
このプログラム (
ns_exec
という名前のバイナリとしてコンパイルされている)を、
clone(2)
のマニュアルページの
CLONE_NEWUTS
のサンプルプログラムと組み合わせて使っている。
まず、
clone(2)
のサンプルプログラムをバックグラウンドで実行する。
このプログラムは、
別の UTS
名前空間で子プロセスを作成する。
子プロセスは自分の名前空間内でホスト名を変更する。
それから、
親プロセスと子プロセスの両方でそれぞれの
UTS
名前空間のホスト名を表示し、
2
つのホスト名が違うことが確認できる。
$ su # 名前空間の操作には特権が必要
Password:
# ./newuts bizarro &
[1] 3549
clone() returned 3550
uts.nodename in child: bizarro
uts.nodename in parent: antero
# uname -n # シェルでホスト名を確認
antero
次に、以下のプログラムを使ってシェルを実行する。
このシェルの中では、ホスト名が最初のプログラムで作成された子プロセスが設定したホスト名になっていることを確認できる。
# ./ns_exec /proc/3550/ns/uts /bin/bash
# uname -n # ns_exec で起動されたシェル内で実行
bizarro
#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);
}
fd = open(argv[1], O_RDONLY); /* 名前空間のディスクリプターを取得 */
if (fd == -1)
errExit("open");
if (setns(fd, 0) == -1) /* 名前空間に参加 */
errExit("setns");
execvp(argv[2], &argv[2]); /* 名前空間内でコマンドを実行 */
errExit("execvp");
}
clone(2),
fork(2),
unshare(2),
vfork(2),
namespaces(7),
unix(7)
この man ページは Linux
man-pages
プロジェクトのリリース
3.79 の一部
である。プロジェクトの説明とバグ報告に関する情報は
http://www.kernel.org/doc/man-pages/
に書かれている。