Newsgroup: it.comp.os.linux.development
Titolo: killare uno script con tutti gli eventuali sottoprocessi

Pensavo che il segnale SIGTERM generato dal comando kill raggiungesse
il processo principale e gli eventuali sotto-processi, ma a quanto
pare sembra di no.
---
user@ubuntu704desktop:~/temp$ cat prova.sh
#!/bin/bash
sleep 100
user@ubuntu704desktop:~/temp$ ./prova.sh &
[1] 9210
user@ubuntu704desktop:~/temp$ ps
  PID TTY          TIME CMD
 9210 pts/2    00:00:00 prova.sh
 9211 pts/2    00:00:00 sleep
 9214 pts/2    00:00:00 ps
25597 pts/2    00:00:00 bash
user@ubuntu704desktop:~/temp$ kill 9210
user@ubuntu704desktop:~/temp$ ps
  PID TTY          TIME CMD
 9211 pts/2    00:00:00 sleep
 9219 pts/2    00:00:00 ps
25597 pts/2    00:00:00 bash
[1]+  Terminato               ./prova.sh
---
Come si può vedere, se killo lo script prova.sh, il comando sleep (che
è un sotto-processo di prova.sh) non viene terminato. Posso vedere con
il comando pstree che prima del kill sleep è effettivamente un sotto-
processo di prova.sh, dopo il kill sleep diventa un sotto-processo di
init.
Come posso fare per killare uno script e gli eventuali comandi
lanciati nello script e ancora in fase di esecuzione?

 lanciati nello script e ancora in fase di esecuzione?
Due possibilita`:
1) usa un'utility in C con "kill( -pid, segnale )"(da provare)
2) usa "pstree -p" e poi digerisci l'output (da scrivere) trovando tutti i PIDs 
da uccidere individualmente
Ciao,
-- 
        Roberto Divia`              Love at first sight is one of the greatest
Dep:PH Bat:53 Mailbox:C02110      labour-saving devices the world has ever seen
Route de Meyrin 385               ---------------------------------------------
Case Postale                                Phone:  
+41-22-767-4994
CH-1211 Geneve 23 CERN                      Fax:    
+41-22-767-9585
Switzerland                                 E-Mail: 
Robert...@cern.ch

 da uccidere individualmente
In effetti ci avevo pensato... peccato che nella mia piccolissima
linux-box
(sono solo 8MB di Flash) non ho il comando pstree, ma solo uno scarso
ps.

 ps.
Anche col ps si fa. Ci vuole un po' ma si fa. Guarda "ps --ppid", oppure un
"ps --no-header -eo'pid,ppid'" seguito da un parsing ricorsivo...


 "ps --no-header -eo'pid,ppid'" seguito da un parsing ricorsivo...
Eh sì, ma quando ti dicevo che è uno scarso ps, volevo dire che non
c'è l'opzione
--ppid, purtroppo :(

 --ppid, purtroppo :(
Prova con "ps -efl".
Hai killall? Ha un'opzione per il kill ricorsivo...


 Probabilmente e` la kill ad essere diversa :-(
verrebbe da dire cosě, ma come puo' essere diversa la kill se la libc č
la stessa?
infatti: sicuro sia la stessa e non, chessň, la microClibc? (di solito
negli embedded c'č quella)
http://www.uclibc.org/about.html

 Hai killall? Ha un'opzione per il kill ricorsivo...
Proverò e ti farò sapere...

 negli embedded c'è quella)
Ho copiato il file 
libc-2.3.5.so
 dal sistema di sviluppo NFS (vedi altro 
post) al mio target.
E si comporta in modo differente anche il mio programma killpg che 
richiama semplicemente la kill() della libc passando come PID in un 
numero negativo. Lavorando con il sistema NFS funziona correttamente, 
lavorando direttamente sul target in modalità stand-alone mi dà errore 
di processo inesistente.
I due sistemi hanno molte differenze tra di loro, quello in NFS è molto 
accessoriato. Però si basano sullo stesso kernel e sulle stesse libc...
Come sarà implementata la kill() nella libc? Dove potrei andare a 
guardare per capire che succede?
Magari è la shell differente (bash nel sistema NFS e msh nel sistema 
standalone) che crea processi in modi differenti e che non riescono ad 
essere visti dalla kill().

 essere visti dalla kill().
potrebbe essere, mai usata msh

 nello script e ancora in fase di esecuzione?
Se usi bash, puoi provare con il comando trap:
#!/bin/bash
trap "cleaner" EXIT
cleaner() {
	for proc in $(jobs -p); do
		kill -15 $proc;
	done
}
sleep 1000 &
sleep 1500 &
sleep 2000 &
wait
-- 
no signature yet

 potrebbe essere, mai usata msh
E sembra essere proprio così. La msh è una shell minimale riscritta in
busybox (
busybox.net
) che non crea i process group. Da quello che ho
capito, rimane una gerarchia dei processi (infatti riesco comunque a
vedere
il parent PID di ogni processo) ma non viene creato il process group
relativo agli script e ai comandi eseguiti all'interno.
Questo vuol dire che l'unico modo per killare il processo padre e
tutti i
processi figli è cercare tra _tutti_ i processi quelli con il parent
PID uguale
al processo padre.
Oppure l'altra soluzione è usare la bash che mi occupa un bel po' di
spazio.

 Oppure l'altra soluzione č usare la bash che mi occupa un bel po' di
ma esattamente cosa devi fare?
perchč al limite fai tutto in c ed i pid dell'albero te li
tieni in un vettore (globale) che puoi svuotare in un signal handler
forse un po' tortuoso ma dovrebbe andare:
insomma per ogni comando che vuoi dare:
int pid_table[MAXCHILD]
main()
{
signal(SIGTERM, Handler);
signal(SIGINT, Handler);
pid=fork()
if(!pid)
execl(blabla)
else
appenti pid alla pid_table	
ciclo di wait sui pid:
se morto elimina dalla pid_table
}
void Handler()
{
for i in pid_table
	kill(i)
}
ovviamente ho scritto tutto in una pseudo_amorfo codifica :)
ma il succo si dovrebbe capire
un robo del genere in python verrebbe di certo molto piu' semplice, ma di
sicuro il python non ce l'hai

 al processo padre.
Hai il /proc ? Se si, un grep in tutti i proc/<ppid>/status per il 'PPid:' del 
padre dovrebbe risolvere il tuo problema...


 padre dovrebbe risolvere il tuo problema...
Infatti ho creato uno script semplice che tramite il comando ps mi
tira
fuori tutti i PID dei processi e tramite il grep mi tira fuori solo i
PID che
hanno un certo PPid.
E' un po' lentino, ma lo devo fare solo qualche volta.
Però questa msh è proprio minimale. Ho un altro problema che credo
sia sempre legato a questa shell.
Ho due script:
---
root:/tmpfs/tmp> cat example.sh
#!/bin/sh
while true; do
  read X
  ./flash.sh &
  FLASHPID=$!
  sleep 5
  kill $FLASHPID
done
---
root:/tmpfs/tmp> cat flash.sh
#!/bin/sh
while true; do
  echo "ON"
  sleep 1
  echo "OFF"
  sleep 1
done
---
In pratica il primo script, sempre in esecuzione, aspetta un evento
(in questo caso, l'input da tastiera) e successivamente mette in
esecuzione
un altro processo (flash.sh).
Quindi example.sh inizia a fare certi compiti che durano un po' (in
questo
caso una semplice sleep) e dopo killa flash.sh per rimettersi in
attesa
dell'evento.
La prima volta tutto funziona e example.sh si rimette in attesa
dell'evento.
Appena premo di nuovo RET, lo script principale example.sh termina
brutalmente (mi compare killed) non appena cerca di eseguire
nuovamente
flash.sh.
Mi sa che la distribuzione dei segnali con msh è alquanto scarsa.

 Mi sa che la distribuzione dei segnali con msh è alquanto scarsa.
mmmmm....
non è che quel killed si riferisce al'ultimo flash che avevi ucciso?
che (ipotesi) tornando dal background uccide il padre perchè la shell è
scarsa nel gestire questo tipo di cose?
ed esempio, prova a mettere una sleep 2 dopo il kill e vedi se comunque ti
fa arrivare alla read o muore

 Process group = il processo + tutti i suoi figli.
Da quello che ho capito, non è detto. Un process group è un gruppo
eterogeneo
di processi che hanno in comune lo stesso identificativo di gruppo,
nient'altro.
Con opportune funzioni come setpgid() è possibile assegnare un
processo qualsiasi
(con alcune restrizioni) ad un gruppo, anche se questi non è un figlio
del leader
del gruppo (ecco perchè, forse, parlano nella documentazione di group
leader e
non semplicemente di parent process).
In genere la shell, almeno la bash, assegna lo stesso identificativo
di gruppo
ai comandi lanciati da uno script. Ma se muore il group leader che
succede?

 spazio.
non esiste mica solo bash... 
ci sono anche tcsh, zsh, ash...
ilSimo
-- 
now playing: 
Le barche mi danno il voltastomaco. 
Piu' delle barche odio soltanto gli elfi.
          -- Gotrek Gurnisson
             da "Sangue di Skaven"

 Cavolo, non ci riesco ancora!
Ho capito l'arcano!
Quando eseguo lo script "startstop.sh start", viene creato un processo
con
un proprio PID, diciamo 100. Viene creato anche un process group in
cui
lo script startstop.sh è il leader, quindi l'identificativo del gruppo
(PGID) è
pari al PID del leader, nel nostro esempio 100.
startstop.sh crea un processo figlio, prova.sh, che ha un altro PID,
diciamo 101.
Questo processo figlio viene assegnato al gruppo PGID=100.
A questo punto, startstop.sh finisce il suo lavoro visto che prova.sh
è stato
lanciato in background e quindi termina. Non esiste più il processo
con PID=100
ma rimane un processo (prova.sh) con PGID=100.
prova.sh lancia "sleep 100" che, a sua volta, avrà PID=102 (esempio) e
PGID=100.
Quindi il comando ps mostrerà
  prova.sh con PID=101 e PGID=100
  sleep 100 con PID=102 e PGID=100
Per terminare il process group bisogna usare
  /bin/kill -s TERM -100
e non
  /bin/kill -s TERM -101
come facevo prima.
Il problema è ora capire qual è il PGID che è il PID del primo
startstop.sh che
ormai non c'è più. Con il comando ps full-features è semplice (ps -o
pgid),
nel mio caso dovrei guardare a /proc, ma non so dove.è l'informazione.

> essere visti dalla kill().
I segnali possono essere trappati ad eccezione del SIGKILL (kill per 
default invia un SIGTERM)
Inoltre quando un processo è in kernel mode probabilmente neanche la 
SIGKILL ha effetto

 ormai non c'č piů.
Fai fare un wait al primo processo (startstop.sh) anziche` farlo uscire...


 Fai fare un wait al primo processo (startstop.sh) anziche` farlo uscire...
In realtà poi ho risolto andando in /proc/<pid>/stat e leggendo il 
quinto numero. Rappresenta proprio il PGID.
Poi mi sono ripreso un po' di spazio cancellando la bash (roba troppo 
pesante per il mio sistema) e mettendo su la ash che gestisce 
correttamente i process group.
Tra l'altro ho scoperto che una variante della ash (la dash, Debian ash) 
viene ampiamente usata in Ubuntu dove /bin/sh è un link simbolico 
proprio a /bin/dash in modo da eseguire gli script in modo più rapido, 
visto che la (d)ash è molto più snella della bash.
Questo mi fa sperare che la ash sia molto simile alla bash, anche se 
alcuni script scritti per bash potrebbero incaxxarsi con la ash.