2015-12-17 14 views
5

Ho appena aggiornato a Ubuntu 15.10 e improvvisamente in Python 2.7 non sono in grado di terminare un processo che ho creato in cui essere radice. Per esempio, questo non termina tcpdump:Non può terminare un processo sudo creata con pitone, in Ubuntu 15.10

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() 

Che cosa è successo? Funziona su versioni precedenti.

+3

Se si esegue questo mentre si è root, perché è necessario chiamare sudo? Cosa stai cercando di fare? –

+0

Ok, buon punto. Senza 'sudo' sembra funzionare. Ma perché ha funzionato su tutte le versioni precedenti? –

+0

Non so perché, lo scenario più probabile è che quando hai aggiornato a 15.10 qualche configurazione relativa al tuo sudoer o ai privilegi ad essa relativi o al suo gruppo sia cambiata. Forse stai eseguendo il tuo python senza privilegi di root e non puoi uccidere la shell elevata (causa sudo) –

risposta

8

TL; DR: sudo non trasmettere segnali inviati da un processo nel gruppo di processi del comando since 28 May 2014 commit rilasciato nel sudo 1.8.11 - il processo python (madre di sudo) e il processo tcpdump (nipote) sono nello stesso gruppo processo per impostazione predefinita e pertanto sudo non inoltra il segnale SIGTERM inviato da .terminate() al processo tcpdump.


Essa mostra lo stesso comportamento durante l'esecuzione di tale codice pur essendo root e pur essendo un utente normale + sudo

esecuzione come utente normale solleva OSError: [Errno 1] Operation not permitted eccezione su .terminate() (come previsto).

esecuzione come root riproduce il problema: sudo e tcpdump processi non vengono uccisi sul .terminate() e il codice è bloccato sul .communicate() su Ubuntu 15.10.

Lo stesso codice uccide entrambi i processi su Ubuntu 12.04.

tcpdump_process nome è fuorviante, perché la variabile si riferisce al processo di sudo (il processo figlio), non tcpdump (nipote):

python 
└─ sudo tcpdump -w example.pcap -i eth0 -n icmp 
    └─ tcpdump -w example.pcap -i eth0 -n icmp   

Come @Mr.E pointed out in the comments, non è necessario sudo qui: siete root già (anche se non dovresti essere - puoi sniff the network without root). Se si rilascia sudo; .terminate() funziona.

In generale, .terminate() non uccide l'intero albero del processo in modo ricorsivo e pertanto è previsto che il processo di un nipote sopravviva. Sebbene sudo è un caso speciale, from sudo(8) man page:

Quando il comando viene eseguito come figlio del processo sudo, sudo sarà trasmettono segnali riceve al comando. corsivo è mio

cioè sudo dovrebbe trasmettere SIGTERM a tcpdump e tcpdump should stop capturing packets on SIGTERM, from tcpdump(8) man page:

Tcpdump sarà, ..., continuare a catturare i pacchetti finché non è interrotto da un segnale SIGINT (generato, ad esempio, digitando il proprio carattere di interruzione , in genere control-C) o un segnale SIGTERM (generato in genere con il comando kill (1));

cioè il comportamento previsto è: tcpdump_process.terminate() invia SIGTERM a sudo che trasmette il segnale di tcpdump che dovrebbe impedire l'acquisizione ed entrambi uscita processi e .communicate() ritorni tcpdump stderr 's allo script python.

Nota: in linea di principio il comando può essere eseguito senza creare un processo figlio, from the same sudo(8) man page:

Come caso speciale, se il plugin politica non definisce una stretta funzione ed NO PTY è richiesta, sudo eseguirà il comando direttamente invece di chiamare forcella (2) prima

e quindi .terminate() possono inviare SIGTERM al processo tcpdump direttamente - anche se non è la spiegazione: sudo tcpdump crea due processi su entrambi Ubuntu 12.04 e 15.10 nei miei test.

Se corro sudo tcpdump -w example.pcap -i eth0 -n icmp nella shell, kill -SIGTERM termina entrambi i processi. Non sembra un problema di Python (Python 2.7.3 (usato su Ubuntu 12.04) si comporta allo stesso modo su Ubuntu 15.10. Anche Python 3 ha esito negativo).

È legato per elaborare gruppi (job control): passa preexec_fn=os.setpgrp a subprocess.Popen() modo che sudo sarà in un nuovo gruppo di processo (di lavoro) in cui è leader come nel guscio, consente tcpdump_process.terminate() lavoro in questo caso.

Che cosa è successo? Funziona su versioni precedenti.

La spiegazione è in the sudo's source code:

fare segnali non inoltrare inviati da un processo nel processo del comando gruppo, non in avanti come non vogliamo che il bambino uccidere indirettamente lo stesso . Ad esempio, questo può accadere con alcune versioni del riavvio che chiamano kill (-1, SIGTERM) per uccidere tutti gli altri processi. corsivo è mio

preexec_fn=os.setpgrp cambia gruppo di processi sudo s'. I discendenti di sudo come il processo tcpdump ereditano il gruppo. python e tcpdump non sono più nello stesso gruppo di processi e pertanto il segnale inviato da .terminate() viene inoltrato da sudo a tcpdump ed esce.

Ubuntu 15.04 utilizza Sudo version 1.8.9p5 dove il codice della domanda funziona così com'è.

Ubuntu 15.10 utilizza Sudo version 1.8.12 che contiene the commit.

sudo(8) man page in wily (15.10) parla ancora solo circa il processo figlio in sé - non si parla del gruppo di processi:

Come caso speciale, sudo non trasmettere i segnali che sono stati inviati dal comando è in esecuzione.

Dovrebbe essere invece:

Come caso particolare, sudo non ritrasmetterà i segnali inviati da un processo nel gruppo di processo del comando è in esecuzione.

È possibile aprire un problema di documentazione su Ubuntu's bug tracker e/o su the upstream bug tracker.

Problemi correlati