Отключение терминала родительского процесса
Я экспериментирую с пространствами имен в Linux, поэтому пишу небольшую программу на C, чтобы изолировать среду Debian Wheezy, созданную с помощью debootstrap.
Я мог бы успешно запустить sysv-init и получить приглашение на вход в систему, но когда я закрываю изолированную среду, либо выключаю систему, либо убиваю -9 init, терминал остается в состоянии, в котором, похоже, нет контролирующего терминала, подключенного к оболочке. В частности, если я запускаю sudo, он жалуется, что нет терминала.
Я сузил точку отказа в sudo до следующего утверждения:
open("/dev/tty", O_RDWR|O_NOCTTY);
с ошибкой ENXIO (т.е. "Нет такого устройства или адреса").
Я пытаюсь понять, почему это происходит, и я чувствую, что это что-то, связанное с системным вызовом setsid() в init, но я не смог воспроизвести точный сценарий, поэтому я не могу предоставить правильный тестовый пример.
Что мне действительно кажется странным, так это то, что не только init (разветвленный процесс и, следовательно, дочерний элемент оболочки) отсоединяется от текущего терминала, но также и вся иерархия процессов вплоть до 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, я привязал подключенный управляющий терминал оболочки к файлу этих устройств в смонтированном контейнере 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
возможность и вызов ioctl()
в дескрипторе файла tty, используя TIOCSCTTY в качестве команды и 1 в качестве параметра (см. tty_ioctl (4)). Я постараюсь написать контрольный пример, чтобы подтвердить это и доложить.
2 ответа
Я думаю, что ваша догадка о setsid()
вероятно, близко. Здесь fork()
заклинания близко к setsid()
вызов? Потому что обычная техника для превращения процесса в демона состоит в следующем:
fork()
setsid()
fork()
еще раз, чтобы убедиться, что все отделено
редактировать
Исходный код для рассматриваемой функции (init_main
): http://svn.savannah.nongnu.org/viewvc/sysvinit/trunk/src/init.c?root=sysvinit&view=markup
Имеет общее fork
, setsid
, fork
шаблон повторяется несколько раз. Это обеспечит отделение от телетайпа.
Хорошо, я мог бы успешно отследить проблему.
См. Эту суть для примера рабочего кода (замените /dev/pts/17
с выходом tty
команда).
Проблема связана со следующим шагом в sysvinit
"s spawn()
функция:
(void)ioctl(f, TIOCSCTTY, 1);
ioctl()
на самом деле ворует контрольный TTY /dev/console
который, в моем случае, является креплением pty
текущего процесса.