Я экспериментирую с пространствами имен в Linux, поэтому пишу небольшую программу на C для изоляции среды Debian Wheezy, созданной с помощью debootstrap.
Я мог бы успешно запустить sysv-init и получить запрос на вход в систему, но когда я закрываю изолированную среду, либо выключая систему, либо убивая -9 init, терминал остается в состоянии, когда к оболочке не подключен управляющий терминал. В частности, если я запускаю sudo, он жалуется, что терминал отсутствует.
Я сузил точку отказа в sudo до следующего утверждения:
open("/dev/tty", O_RDWR|O_NOCTTY);
с ошибкой ENXIO (т.е. "Нет такого устройства или адреса").
Я пытаюсь понять, почему это происходит, и у меня есть ощущение, что это связано с системным вызовом setsid() в init, но я не смог воспроизвести точный сценарий, поэтому я не могу предоставить надлежащий тестовый пример.
Что мне кажется действительно странным, так это то, что не только init (разветвленный процесс и, следовательно, дочерний процесс для оболочки) отсоединяется от текущего терминала, но и вся иерархия процессов до терминала с графическим интерфейсом также отсоединяется от 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
Любая подсказка об этой ситуации высоко ценится.
EDIT: Глядя на исходный код ядра для "/dev/tty", я думаю, что проблема связана с подсчетом ссылок на символьное устройство на стороне ядра. Действительно, чтобы перенаправить вывод «/dev/{console|tty0|tty1}» на текущий pty, я привязал управляющий терминал оболочки к этому файлу устройства в смонтированном контейнере dev.
EDIT: кажется, что в ядре 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);
...
}
EDIT: похоже, проблема связана с концепцией «кражи» управляющего tty. Это можно сделать с помощью возможности CAP_SYS_ADMIN
и вызова ioctl()
в файловом дескрипторе tty, используя TIOCSCTTY в качестве команды и 1 в качестве параметра (см. tty_ioctl(4)). Я постараюсь написать тестовый пример, чтобы подтвердить это и отчитаться.