Как отправить управляющий символ Ctrl-C или сообщение о зависании терминала дочернему процессу?

У меня есть дочерний процесс, который работает в псевдотерминале. Родительский процесс не запускается от имени root, а дочерний процесс запускается через su или sudo. Из-за этого невозможно послать сигнал дочернему процессу, чтобы принудительно завершить его. Я хочу заставить его выйти одним из следующих способов:

  • эмуляция Ctrl-C.
  • эмуляция зависания терминала.

Как мне сделать что-либо из этого? У меня уже есть pty master fd, и я пробовал что-то вроде этого:

write(master, &termios.c_cc[VINTR], 1)

но это ничего не делает.


person Hongli    schedule 03.02.2010    source источник
comment
Я не знаю, будет ли это работать с вашей программой/pty, но то, что я делаю, так как я запускаю bash, а bash по умолчанию принимает ^C как SIGINT [я думаю?], - это создание символа, содержащего шестнадцатеричный код: ( 0x03 для ^C ), а затем запишите его на мой pty с помощью: write( m_nMaster, &ctrlC, sizeof(ctrlC ) );   -  person    schedule 08.02.2010


Ответы (6)


Мне кажется, что если у вас действительно есть pty (если вы не имеете в виду что-то другое под псевдотерминалом), все, что вам нужно сделать, это отправить Control-C на этот FD. В доказательство этого я представляю следующий код на Python (но достаточно близкий к требуемому для этого C):

import pty, os, sys, time

pid, fd = pty.fork()
if pid == 0:
   os.execv('/bin/sh', ['/bin/sh', '-c',
         'while true; do date; sleep 1; done'])
   sys.exit(0)
time.sleep(3)
os.write(fd, '^C')
print 'results:', os.read(fd, 1024)

Это разветвляет процесс под pty, который запускает бесконечный цикл, печатающий дату. Затем родитель ждет 3 секунды и отправляет контроль-C.

Это приводит к следующему результату:

guin:/tmp$ time python /tmp/foo
results: Fri Feb  5 08:28:09 MST 2010
Fri Feb  5 08:28:10 MST 2010
Fri Feb  5 08:28:11 MST 2010

python /tmp/foo  0.02s user 0.01s system 1% cpu 3.042 total
guin:/tmp$

Он проработал чуть более 3 секунд, распечатал дату 3 раза и вышел.

person Sean Reifschneider    schedule 05.02.2010
comment
Я почти уверен, что это не работает так, как вы описываете. Запись литеральной строки ^C не отправляет управляющий символ прерывания. Причина, по которой это работает, заключается в том, что когда процесс python умирает, процесс на другом конце pty получает SIGHUP (а не SIGINT). Если вы удалите os.write, он будет вести себя так же. Но если вы os.write(fd,"\x03") получите настоящий сигнал SIGINT. Вы можете убедиться в этом, запустив тест под strace -f. - person Adam Bliss; 08.06.2020

В конце концов я выбрал следующее решение:

После разветвления вместо немедленного выполнения sudo я выполняю exec() вспомогательный дочерний процесс, который, в свою очередь, разветвляется и выполняет sudo и вызывает для него waitpid. Итак, иерархия процессов выглядит так:

original process          <---- runs as user
  |
  +-- helper process      <---- runs as user, session leader,
         |                      has own pty, in pty's foreground process group
         |
         +--- sudo        <---- runs as root

Убив вспомогательный процесс, pty больше не имеет процесса переднего плана. Это заставит ОС отправлять SIGHUP всей группе процессов переднего плана, независимо от пользователя, поэтому sudo также является SIGHUP.

person Hongli    schedule 10.02.2010

Есть два способа добиться этого:

  • Из дочернего процесса перехватите сигнал SIGCHLD и обработайте его, вы можете _exit(0) завершить дочерний процесс
  • Существует программа под названием ptree. Вы можете обмануть это, сделав это так... в псевдокоде:
obtain the parent's pid.
using _popen("ptree %d", parent_pid)
for each entry of child process
system ("kill -1 %d", child_process_pid)

Вот два, которые приходят на ум... извините, если это не поможет вам в дальнейшем,

Надеюсь, это поможет. С уважением, Том.

person t0mm13b    schedule 03.02.2010
comment
Он не может напрямую сигнализировать ребенку, потому что он работает под другим эффективным uid. - person caf; 04.02.2010
comment
@caf: хммм.... можешь ответить? представьте, если дочерний процесс работает на псевдотерминале, возьмите это в примере с linux... если двоичный файл должен быть передан на псевдотерминал, скажем, /dev/pty4, где находится дочерний процесс, приведет ли это к зависание терминала? т. е. двоичный_файл_cat › /dev/pty4 - person t0mm13b; 04.02.2010
comment
Нет, с чего бы это? Он просто доставит эти байты на главную сторону пары псевдотерминалов. - person caf; 04.02.2010
comment
@caf: Хорошо. Я думал, что это приведет к зависанию процесса оболочки терминала и впредь будет вынужден выйти из системы... вот о чем я думал... - person t0mm13b; 04.02.2010

Закрытие ведущего должно сигнализировать о зависании группе процессов, управляющих ведомым.

person caf    schedule 03.02.2010

Я думаю, вам нужно использовать ioctl для вставки символа прерывания вместо write. К сожалению, механизм для этого не кажется портативным. Для Linux это может сработать:

ioctl(master, TIOCSTI, &termios.c_cc[VINTR]);
person Geoff Reedy    schedule 03.02.2010
comment
Я получаю отказ в разрешении/операция не разрешена. Если я запускаю процесс от имени пользователя root, время ожидания операции истекает. - person Hongli; 04.02.2010

Первое, что я бы проверил, это если вам нужно сделать его управляющим терминалом на ведомой стороне. Оказывается, это более сложный чем я помню, с ptys, возможно, не становится управляющим по умолчанию. Эта ссылка предназначена для Linux, другие системы должны делать то или иное в зависимости от их SysV и BSD, но похоже, что TIOCSCTTY — хороший выбор, чтобы попробовать.

Во-вторых, я бы проверил, настраиваете ли вы ISIG в своих терминалах; в противном случае VINTR и VQUIT работать не будут.

Конечно, если другой конец ловит SIGINT и SIGQUIT, у вас будут другие проблемы.

person Arthur Shipkowski    schedule 04.02.2010