分离父进程的终端



我正在尝试Linux下的命名空间,所以我正在编写一个小的C程序来隔离使用debootstrap创建的Debian Wheezy环境。

我可以成功启动 sysv-init 并收到登录提示,但是当我关闭隔离环境时,要么关闭系统,要么杀死 -9 init,终端处于似乎没有控制终端连接到 shell 的状态。具体来说,如果我启动 sudo,它会抱怨没有终端存在。

我将 sudo 中的失败点缩小到以下语句:

open("/dev/tty", O_RDWR|O_NOCTTY);

错误 ENXIO(即"没有此类设备或地址")。

试图理解为什么会发生这种情况,我感觉这与 init 中的 setsid() 系统调用有关,但我无法重现确切的场景,所以我无法提供适当的测试用例。

对我来说真的很奇怪的是,不仅 init(一个分叉进程,因此是 shell 的子进程)从当前终端分离,而且直到 GUI 终端的所有进程层次结构也都与 tty 分离,我似乎无法弄清楚它是如何发生的。

此外,不同命令之间存在一些不一致之处:

➜  namespaces  tty
/dev/pts/19
➜  namespaces  sudo -s
sudo: no tty present and no askpass program specified
➜  namespaces  ls -l /proc/$$/fd
total 0
lrwx------ 1 paris paris 64 gen 15 23:24 0 -> /dev/pts/19
lrwx------ 1 paris paris 64 gen 15 23:24 1 -> /dev/pts/19
lrwx------ 1 paris paris 64 gen 15 23:24 10 -> /dev/pts/19
lrwx------ 1 paris paris 64 gen 15 23:24 2 -> /dev/pts/19
➜  namespaces  
关于

这种情况的任何线索

都非常感谢。

编辑:查看"/dev/tty"的内核源代码,我认为问题与内核端字符设备的引用计数有关。实际上,为了将"/dev/{console|tty0|tty1}"输出重定向到当前 pty,我将 shell 的控制终端挂载到容器挂载的 dev 中的那些设备文件。

编辑:似乎在Linux内核中,此步骤中的"tty_open_current_tty()"函数中报告了错误:

static struct tty_struct *tty_open_current_tty(dev_t device, struct file *filp)
{
     struct tty_struct *tty;
     int retval;
     if (device != MKDEV(TTYAUX_MAJOR, 0))
         return NULL;
     tty = get_current_tty();
     if (!tty)
         return ERR_PTR(-ENXIO);
     ...
}

编辑:似乎问题与"窃取"控制tty的概念有关。这可以在CAP_SYS_ADMIN功能下完成,并使用 TIOCSCTTY 作为命令和 1 作为参数在 tty 文件描述符上调用ioctl()(参见 tty_ioctl(4))。我将尝试编写一个测试用例来确认这一点并报告。

我认为你对setsid()的预感可能很接近。是否有接近setsid()调用的fork()调用?因为将进程转换为守护程序的常用技术是:

  • fork()
  • setsid()
  • 再次fork()以确保一切都已分离

编辑

相关函数的源代码 ( init_main ): http://svn.savannah.nongnu.org/viewvc/sysvinit/trunk/src/init.c?root=sysvinit&view=markup

它具有重复多次forksetsidfork模式。这将确保与 tty 分离。

好的,我可以成功地追踪问题。

有关工作代码示例,请参阅此要点(将/dev/pts/17替换为 tty 命令的输出)。

该问题与 sysvinitspawn() 函数中的以下步骤有关:

(void)ioctl(f, TIOCSCTTY, 1);

ioctl()实际上是在窃取控制 tty /dev/console,在我的情况下,它是当前进程pty的绑定挂载。

相关内容

  • 没有找到相关文章

最新更新