python ubuntu subprocess sudo ubuntu-15.10

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.