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

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

  • эмуляция Ctrl-C.
  • Эмулирует отключение терминала.

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

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

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

Ответ 1

В конце концов я пошел со следующим решением:

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

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

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

Ответ 2

Мне кажется, что если у вас действительно есть 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 раза и вышел.

Ответ 3

Существует два способа добиться этого:

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

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

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

Ответ 4

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

Ответ 5

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

ioctl(master, TIOCSTI, &termios.c_cc[VINTR]);

Ответ 6

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

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

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