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.
Se si esegue questo mentre si è root, perché è necessario chiamare sudo? Cosa stai cercando di fare? –
Ok, buon punto. Senza 'sudo' sembra funzionare. Ma perché ha funzionato su tutte le versioni precedenti? –
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) –