我正在 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”的内核源代码,我认为问题与内核端 char 设备的引用计数有关。实际上,为了将“/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 作为参数调用ioctl()
tty 文件描述符(参见tty_ioctl(4))。我将尝试编写一个测试用例来确认这一点并报告回来。