分离父进程的终端

时间:2015-01-16 13:47:22

标签: linux process tty linux-namespaces

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

我可以成功启动sysv-init并获得登录提示但是当我关闭隔离环境时,无论是关闭系统还是杀死-9 init,终端都处于没有控制终端似乎连接到的状态贝壳。具体来说,如果我开始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”的内核源代码我认为问题与内核端的char设备的引用计数有关。实际上,为了将“/ dev / {console | tty0 | tty1}”输出重定向到当前的pty,我将shell的控制终端挂载到容器安装的开发中的那些设备文件中。

编辑:似乎在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);
     ...
}

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

2 个答案:

答案 0 :(得分:1)

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

  • fork()
  • setsid()
  • fork()再次确保所有内容都已分离

修改

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

一般forksetsidfork模式重复多次。这将确保脱离tty。

答案 1 :(得分:1)

好的,我可以成功找到问题。

请参阅this gist以获取有效的代码示例(将/dev/pts/17替换为tty命令的输出。

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

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

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