TenForward

技術ブログ。はてなダイアリーから移転しました

PID Namespace 内の reboot システムコール

lxc-devel ML をなんとなく眺めていたら "On any recent kernel, reboot syscall from inside a non-init pid-ns will not reboot the host." なんて書いてありました.ほほぅ.

というわけで調べてみると kernel/sys.c にありました.

SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,
		void __user *, arg)
{
	struct pid_namespace *pid_ns = task_active_pid_ns(current);
	char buffer[256];
	int ret = 0;

	/* We only trust the superuser with rebooting the system. */
	if (!ns_capable(pid_ns->user_ns, CAP_SYS_BOOT))
		return -EPERM;

	/* For safety, we require "magic" arguments. */
	if (magic1 != LINUX_REBOOT_MAGIC1 ||
	    (magic2 != LINUX_REBOOT_MAGIC2 &&
	                magic2 != LINUX_REBOOT_MAGIC2A &&
			magic2 != LINUX_REBOOT_MAGIC2B &&
	                magic2 != LINUX_REBOOT_MAGIC2C))
		return -EINVAL;

	/*
	 * If pid namespaces are enabled and the current task is in a child
	 * pid_namespace, the command is handled by reboot_pid_ns() which will
	 * call do_exit().
	 */
	ret = reboot_pid_ns(pid_ns, cmd); /* ←ここ!! */
	if (ret)
		return ret;

	/* Instead of trying to make the power_off code look like
	 * halt when pm_power_off is not set do it the easy way.
	 */
	if ((cmd == LINUX_REBOOT_CMD_POWER_OFF) && !pm_power_off)
		cmd = LINUX_REBOOT_CMD_HALT;

	mutex_lock(&reboot_mutex);
	switch (cmd) {
	case LINUX_REBOOT_CMD_RESTART:
		kernel_restart(NULL);
		break;
  : (略)

reboot_pid_ns って関数がキモですね.pid_ns 内にいる時は,この関数内で然るべき処理をやって,このシステムコールはここで終わり,そうでないなら,普通に reboot の処理へ,ということでしょうね.

reboot_pid_ns() はというと,kernel/pid_namespace.c 内にあって,こんな感じ.

int reboot_pid_ns(struct pid_namespace *pid_ns, int cmd)
{
	if (pid_ns == &init_pid_ns)  /* ←親環境 (システムの init) の PID 名前空間だったらココでは何もしない */
		return 0;

	switch (cmd) {
	case LINUX_REBOOT_CMD_RESTART2:
	case LINUX_REBOOT_CMD_RESTART:
		pid_ns->reboot = SIGHUP;
		break;

	case LINUX_REBOOT_CMD_POWER_OFF:
	case LINUX_REBOOT_CMD_HALT:
		pid_ns->reboot = SIGINT;
		break;
	default:
		return -EINVAL;
	}

	read_lock(&tasklist_lock);
	force_sig(SIGKILL, pid_ns->child_reaper);
	read_unlock(&tasklist_lock);

	do_exit(0);

	/* Not reached */
	return 0;
}

今,init_pid_ns の PID 名前空間 (つまりシステムの init と同じ PID 名前空間) にいれば,この関数は何もせずに return 0 しますね.それ以外の PID 名前空間に入れば,pid_ns->reboot にシグナルを設定して,force_sig で,多分今の名前空間のプロセスたちに SIGKILL を送るのでしょうね.

このコミット] で入ったようですね.Linux 3.4 以降のようです.

へー :-D