Отсоединение терминала родительского процесса

Я экспериментирую с пространствами имен в 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)). Я постараюсь написать тестовый пример, чтобы подтвердить это и отчитаться.


person Cristiano Paris    schedule 16.01.2015    source источник


Ответы (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, повторяющийся несколько раз. Это обеспечит отсоединение от tty.

person mauzel    schedule 16.01.2015
comment
Взгляните на функцию init_main(). svn.savannah.nongnu.org/ viewvc/sysvinit/trunk/src/ - person Cristiano Paris; 16.01.2015
comment
Да, поток кода выглядит следующим образом: init_main, fork(), setsid(), spawn(), fork(), setsid(), fork()... Много разветвлений и setsid, что, скорее всего, обеспечит отсоединение от tty. - person mauzel; 16.01.2015
comment
Да, но я не понимаю, почему родитель тоже отсоединен. - person Cristiano Paris; 16.01.2015

Ладно, я смог успешно отследить проблему.

См. эту суть для примера рабочего кода (замените /dev/pts/17 выводом команды tty).

Проблема связана со следующим шагом в функции spawn() sysvinit:

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

ioctl() на самом деле крадет управляющий tty /dev/console, который, в моем случае, является монтированием привязки pty текущего процесса.

person Cristiano Paris    schedule 21.01.2015