No se puede finalizar un proceso de sudo creado con python, en Ubuntu 15.10
subprocess ubuntu-15.10 (1)
TL; DR : sudo
no reenvía las señales enviadas por un proceso en el grupo de procesos del comando desde el 28 de mayo de 2014 commit publicado en sudo 1.8.11
- el proceso de python (padre de sudo) y el proceso de tcpdump (nieto) están en el mismo proceso grupo por defecto y por lo tanto sudo
no reenvía la señal SIGTERM
enviada por .terminate()
al proceso tcpdump
.
Muestra el mismo comportamiento cuando se ejecuta ese código mientras se es el usuario root y mientras se es un usuario regular + sudo
Ejecutar como un usuario normal provoca OSError: [Errno 1] Operation not permitted
en .terminate()
(como se esperaba).
La ejecución como root
reproduce el problema: los procesos sudo
y tcpdump
no se .terminate()
en .terminate()
y el código está bloqueado en .communicate()
en Ubuntu 15.10.
El mismo código elimina ambos procesos en Ubuntu 12.04.
tcpdump_process
name es engañoso porque la variable hace referencia al proceso sudo
(el proceso hijo), no a tcpdump
(nieto):
python
└─ sudo tcpdump -w example.pcap -i eth0 -n icmp
└─ tcpdump -w example.pcap -i eth0 -n icmp
Como señaló @Ee, en los comentarios , no necesita sudo
aquí: ya está en la raíz (aunque no debería hacerlo, puede oler la red sin raíz ). Si suelta sudo
; .terminate()
funciona.
En general, .terminate()
no mata el árbol de procesos completo recursivamente y, por lo tanto, se espera que sobreviva un proceso de nieta. Aunque sudo
es un caso especial, de la página man de sudo (8) :
Cuando el comando se ejecuta como un elemento secundario del proceso
sudo
,sudo
transmitirá las señales que recibe al comando. el énfasis es mío
es decir, sudo
debe retransmitir SIGTERM
a tcpdump
y tcpdump
debe detener la captura de paquetes en SIGTERM
, desde la página man de tcpdump (8) :
Tcpdump, ..., continuará capturando paquetes hasta que sea interrumpido por una señal SIGINT (generada, por ejemplo, escribiendo su carácter de interrupción, típicamente control-C) o una señal SIGTERM (típicamente generada con el comando kill (1)) ;
es decir, el comportamiento esperado es : tcpdump_process.terminate()
envía SIGTERM a sudo
que transmite la señal a tcpdump
que debe detener la captura, y ambos procesos salen y .communicate()
devuelve la salida stderr de tcpdump
al script de python.
Nota: en principio, el comando se puede ejecutar sin crear un proceso hijo, desde la misma página man de sudo (8) :
Como un caso especial, si el complemento de política no define una función de cierre y no se requiere pty,
sudo
ejecutará el comando directamente en lugar de llamar a fork (2) primero
y por .terminate()
tanto .terminate()
puede enviar SIGTERM al proceso tcpdump
directamente, aunque no es la explicación: sudo tcpdump
crea dos procesos en Ubuntu 12.04 y 15.10 en mis pruebas.
Si ejecuto sudo tcpdump -w example.pcap -i eth0 -n icmp
en el shell, entonces kill -SIGTERM
finaliza ambos procesos. No se parece al problema de Python (Python 2.7.3 (usado en Ubuntu 12.04) se comporta de la misma manera en Ubuntu 15.10. Python 3 también falla aquí).
Está relacionado con grupos de procesos ( control de trabajos ): pasar preexec_fn=os.setpgrp
a subprocess.Popen()
para que sudo
esté en un nuevo grupo de procesos (job) donde es el líder, como en el shell hace tcpdump_process.terminate()
trabajo en este caso.
¿Que pasó? Funciona en versiones anteriores.
La explicación está en el código fuente de sudo :
No envíe señales enviadas por un proceso en el grupo de procesos del comando , no lo reenvíe ya que no queremos que el niño se mate indirectamente. Por ejemplo, esto puede suceder con algunas versiones de reinicio que llaman kill (-1, SIGTERM) para matar a todos los demás procesos. el énfasis es mío
preexec_fn=os.setpgrp
cambia el grupo de procesos de sudo
. Los descendientes de sudo
, como el proceso tcpdump
, heredan el grupo. python
y tcpdump
ya no están en el mismo grupo de procesos y, por lo tanto, la señal enviada por .terminate()
es retransmitida por sudo
a tcpdump
y sale.
Ubuntu 15.04 usa Sudo version 1.8.9p5
donde el código de la pregunta funciona como está.
Ubuntu 15.10 usa Sudo version 1.8.12
que contiene el compromiso .
Sudo (8) man página wily (15.10) todavía habla solo sobre el proceso hijo en sí mismo, sin mencionar el grupo de procesos:
Como un caso especial, sudo no transmitirá señales que fueron enviadas por el comando que está ejecutando.
Debería ser en cambio:
Como un caso especial, sudo no transmitirá señales que fueron enviadas por un proceso en el grupo de procesos del comando que está ejecutando.
Podrías abrir un problema de documentación en el rastreador de errores de Ubuntu y / o en el rastreador de errores en sentido ascendente .
Acabo de actualizar a Ubuntu 15.10 y de repente en Python 2.7 no puedo finalizar un proceso que creé al ser root . Por ejemplo, esto no termina tcpdump:
import subprocess, shlex, time
tcpdump_command = "sudo tcpdump -w example.pcap -i eth0 -n icmp"
tcpdump_process = subprocess.Popen(
shlex.split(tcpdump_command),
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
time.sleep(1)
tcpdump_process.terminate()
tcpdump_out, tcpdump_err = tcpdump_process.communicate()
¿Que pasó? Funciona en versiones anteriores.