Как передать SIGINT дочернему процессу с помощью Python subprocess.Popen(), используя shell = true
В настоящее время я пытаюсь написать (Python 2.7.3) оболочку для GDB, которая позволит мне динамически переключаться с ввода по сценарию на интерактивное взаимодействие с GDB.
Пока пользуюсь
self.process = subprocess.Popen(["gdb vuln"], stdin = subprocess.PIPE, shell = True)
запустить GDB в моем скрипте. (vuln
это двоичный файл, который я хочу изучить)
Поскольку ключевой особенностью GDB является приостановка выполнения присоединенного процесса и предоставление пользователю возможности проверять регистры и память при получении SIGINT (STRG+C), мне нужен какой-то способ передачи ему сигнала SIGINT.
ни
self.process.send_signal(signal.SIGINT)
ни
os.kill(self.process.pid, signal.SIGINT)
или же
os.killpg(self.process.pid, signal.SIGINT)
работать на меня.
Когда я использую одну из этих функций, нет ответа. Я полагаю, что эта проблема возникает из-за использования shell=True
, Однако на данный момент у меня действительно нет идей. Даже мой старый друг Google не мог мне помочь на этот раз, так что, может быть, вы можете мне помочь. Заранее спасибо.
Ура, Майк
4 ответа
Вот что сработало для меня:
import signal
import subprocess
try:
p = subprocess.Popen(...)
p.wait()
except KeyboardInterrupt:
p.send_signal(signal.SIGINT)
p.wait()
Я посмотрел глубже в проблему и нашел некоторые интересные вещи. Возможно, эти выводы помогут кому-то в будущем.
При звонке gdb vuln
используя suprocess.Popen() он фактически создает три процесса, в которых возвращается pid sh
(5180).
ps -a
5180 pts/0 00:00:00 sh
5181 pts/0 00:00:00 gdb
5183 pts/0 00:00:00 vuln
Следовательно, отправка SIGINT процессу фактически отправит SIGINT в sh
,
Кроме того, я продолжал искать ответ и наткнулся на этот пост https://bugzilla.kernel.org/show_bug.cgi?id=9039
Короче говоря, здесь упоминается следующее:
При регулярном использовании STRG+C при использовании GDB SIGINT фактически отправляется в исследуемую программу (в данном случае vuln
), тогда ptrace перехватит его и передаст в gdb. Что это значит, что если я использую self.process.send_signal(signal.SIGINT)
фактически он никогда не достигнет GDB.
Временное решение:
Мне удалось обойти эту проблему, просто позвонив subprocess.popen()
следующее:
subprocess.Popen("killall -s INT " + self.binary, shell = True)
Это не более чем первый обходной путь. Когда запущено несколько приложений с одинаковым именем, это может привести к серьезным повреждениям. Кроме того, это как-то не получается, если shell=True
не установлен. Если у кого-то есть лучшее решение (например, как получить pid процесса starttet с помощью gdb), пожалуйста, дайте мне знать.
Ура, Майк
РЕДАКТИРОВАТЬ:
Спасибо Марку за указание посмотреть на ppid процесса. Мне удалось сузить процесс, на который отправляется SIGINT, используя следующий подход:
out = subprocess.check_output(['ps', '-Aefj'])
for line in out.splitlines():
if self.binary in line:
l = line.split(" ")
while "" in l:
l.remove("")
# Get sid and pgid of child process (/bin/sh)
sid = os.getsid(self.process.pid)
pgid = os.getpgid(self.process.pid)
#only true for target process
if l[4] == str(sid) and l[3] != str(pgid):
os.kill(pid, signal.SIGINT)
В прошлом я делал что-то вроде следующего, и, если вспомнить, мне показалось, что это сработало:
def detach_procesGroup():
os.setpgrp()
subprocess.Popen(command,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
preexec_fn=detach_processGroup)
Модификация ответа @sirex, которая мне подошла лучше:
import signal
import subprocess
p = subprocess.Popen(...)
while True:
try:
p.wait()
exit(0)
except KeyboardInterrupt:
p.send_signal(signal.SIGINT)
Исходный код прервется после второгоctrl-C
.