> Hallo,
>
> da ich jetzt schon einige Zeit erfolglos daran rumprobiere, frage ich
> jetzt lieber mal die Experten... Ich ben�tige eine M�glichkeit aus einem
> laufenden Programm ein Shellscript nicht nur anzustarten, sondern bei
> Bedarf auch wieder zu beenden. Der Vorgang muss beliebig wiederholbar
> sein.
>
> Gegeben ist ein Beispiel-Shellscript in dieser Form:
> #!/bin/sh
> echo "Our PID is $$"
> wget -O - http://listen.technobase.fm/tunein-aacplus-pls | cat >
> /dev/null
>
> Es handelt sich nur um ein Beispiel und das "cat" ist absichtlich
> dazwischen. Die PID wird ausgegeben und erlaubt so einige Versuche mit
> "kill". Schon auf der Shell zeigt sich, dass man das Script garnicht mal
> so einfach wieder loswird. Ein einfaches
>
> kill -s INT [PID]
>
> bewirkt garnichts. Das ganze mit "SIGTERM" oder "SIGHUP" stoppt dann nur
> das Shellscript selbst. "wget" l�uft ungehindert weiter. Erst ein
>
> kill -s INT -- -[PID]
>
> sorgt f�r Ordnung und beendet das Script. Es muss also wohl zwingend
> eine Prozessgruppe gebildet und bei Bedarf auch die ganze Gruppe gekillt
> werden.
>
> Um das auszuprobieren habe ich folgendes Testprogramm gebaut:
>
> #include <unistd.h>
> #include <signal.h>
> #include <stdio.h>
> int pid;
> int main(void) {
> pid = fork();
> // Check if child process
> if (pid == 0) {
> setsid();
> if (execl("/bin/sh", "sh", "-c", "./test.sh", NULL) == -1) {
> printf("ERROR: Script execution failed!\n");
> _exit(1);
> }
> _exit(0);
> }
> sleep(10);
> printf("Killing now\n");
> if ( killpg(pid, SIGINT) < 0 ) {
> printf("Error while kill(SIGINT)");
> }
> }
>
> Das Shellscript wird also gestartet und nach 10 Sekunden beendet. Damit
> habe ich jetzt folgende Probleme:
>
> - Was ist mit den "geklonten Filedescriptoren" aus dem Parent-Prozess?
> Muss ich die schlie�en, oder zumindest teilweise schlie�en? Sch�n w�re,
> wenn ich alles zubekomme und nur STDOUT und STDERR stehen bleibt. Ich
> habe verschiedenes probiert, aber entweder ist immer alles zu, oder
> STDOUT und STDERR funktionieren bei einem Scriptaufruf noch, beim
> n�chsten sind sie aber komischerweise verschwunden. Geteset habe ich
> mit:
>
> for (int i = STDERR_FILENO + 1; i < getdtablesize(); i++) close(i);
>
> einmal vor und einmal hinter setsid().
> STDOUT und STDERR *m�ssen* in den Unterprozess weitergereicht werden!
> - Gibt es sowas wie "Unter-Prozessgruppen"? Ein Problem, das ich mir mit
> obiger L�sung geholt habe, ist, dass ein SIGINT auf den urspr�nglichen
> Parent-Prozess meine neue Prozessgruppe nun nicht mehr erreicht. Im
> Beispiel des obigen Testprogramms wird das Testprogramm zwar beendet,
> aber wget l�uft weiter. Das soll so nicht sein! Kann ich die ganze
> Prozessgruppe dem urspr�nglichen "Parent-Prozess" unterordnen, dass ein
> SIGINT, SIGHUP, SIGTERM oder wasauchimmer auch meine neue Gruppe
> erreicht?
>
> Danke vielmals im Voraus
>
> CU
>
> Manuel
Dein Problem hat nichts mit C zu tun. Ich leite Dich mal weiter.
Gru�. Claus
Manuel Reimer <mre...@expires-31-07-2009.news-group.org> writes:
>Es muss also wohl zwingend eine Prozessgruppe gebildet und bei Bedarf
>auch die ganze Gruppe gekillt werden.
>Um das auszuprobieren habe ich folgendes Testprogramm gebaut:
>
>#include <unistd.h>
>#include <signal.h>
>#include <stdio.h>
>int pid;
>int main(void) {
> pid = fork();
> // Check if child process
> if (pid == 0) {
> setsid();
»setsid()« erzeugt ein neues session. Dadurch, dass der Kind-Prozess
nicht mehr im bisherigen session ist, kann er mittels job control nicht
mehr beeinflusst werden: Die Job-Control-Signale vom Terminaltreiber
erreichen ihn nicht mehr, d.h. Ctrl-C und Ctrl-Z tun nicht mehr.
Man könnte den Prozess zwar im selben session lassen, ihm aber ein
eigenes job group geben, indem man statt »setsid()« »setpgrp()«
verwendet. Dann gerät er aber in den Hintergrund. Das hat zur Folge,
dass er gestoppt wird, sobald er vom Terminal lesen will. Je nach
Einstellung des Terminals wird er auch gestoppt, wenn er aufs Terminal
schreiben will. Und natürlich reagiert er als Hintergrundjob nicht auf
Ctrl-C und Ctrl-Z. Ein weiteres Problem damit ist, dass man für das Job
natürlich auch einen Job-Verwalter braucht. Das Shell, von dem man aus
das C-Programm gestartet hat, kann die Verwaltung nicht übernehmen, denn
es kennt das vom C-Programm erzeugte Job nicht.
Ich sehe keine andere Möglichkeit, als darauf zu verzichten, das Job des
C-Programms zu verlassen.
Was bleibt? Da SIGINT, SIGQUIT, SIGHUP Signale sind, die normalerweise
vom Terminaltreiber an ein ganzes job group verschickt werden, müssen das
C-Programm und das Shellscript so geschrieben sein, dass sie damit
zurecht kommen.
Wenn SIGINT auch vom C-Programm an das Shellscript geschickt werden
soll, muss das Shellscript darüber hinaus so geschrieben sein, dass es
auch sinnvoll reagiert, wenn nur es selbst, nicht aber das ganze job
group das Signal erhalten. Das Shellscript kann ja nicht unterscheiden,
ob es allein oder das ganze job group das SIGINT erhalten hat.
Sollte diese Anforderung nicht zu erfüllen sein, ist es vielleicht
besser, die Signale SIGHUP, SIGINT und SIGQUIT vom C-Programm aus nicht
zu verschicken, sondern sie exclusiv für das job control reserviert zu
lassen. Statt dessen könnte man vom C-Programm dafür das Signal
SIGTERM, das vom job control nicht verwendet wird, verschicken.
> execl("/bin/sh", "sh", "-c", "./test.sh", NULL)
Das ist strenggenommen kein direkter Start des Shellscripts »./test.sh«.
Das ist ein Start eines »/bin/sh«, dem die Kommandozeile »./test.sh« zum
Abarbeiten vorgelegt wird. Es ist gut möglich, dass dabei ein weiterer
Prozess entsteht.
Dem Start eines Shellscripts von einem Shell aus (»auf der Shell«), etwa
$ ./test.sh
entspricht
execlp("./test.sh", "test.sh", NULL)
besser: Da gibt es kein zusätzliches Shell, das eine Kommandozeile
abarbeitet.
>Das Shellscript wird also gestartet und nach 10 Sekunden beendet. Damit
>habe ich jetzt folgende Probleme:
>
>- Was ist mit den "geklonten Filedescriptoren" aus dem Parent-Prozess?
>Muss ich die schließen, oder zumindest teilweise schließen?
Im Allgemeinen ist es besser, sie nicht zu schließen. Derjenige, der
das file descriptor erzeugt hat, weiß in der Regel besser, ob das file
descriptor das »execl()« überleben soll oder nicht und legt es deswegen
selbst fest (»FD_CLOEXEC« im Manual-Page »fcntl(2)«).
Ein Beispiel, was das verdeutlichen soll: Das Kommando öffnet ein
»xterm«, nimmt darin ein Passwort entgegen und reicht es weiter an ein
Kommando, das es verarbeitet und mittels »more« anzeigt.
Angenommen, »xterm« käme auf die Idee (tut es nicht; seine Entwickler
wussten, was sie taten), irgendwelche file descriptors zu schließen,
dann wäre z.B. folgende Verwendung nicht möglich:
$ { printf '%s:\n' 'Das verschluesselte Passwort lautet' && xterm -e sh -c \
'"$@" >&2 && IFS= read -r line && printf %s\\n "$line" >&3' sh \
printf '%s: ' 'Bitte ein Passwort' 3>&1 1>&2 |
tr 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' \
'nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM'
} 2>&1 | more
>- Gibt es sowas wie "Unter-Prozessgruppen"?
Es gibt job groups und session groups. Für jedes job group gilt: Alle
Prozesse, die zu ihm gehören, gehören auch zum selben session group.
>Ein Problem, das ich mir mit obiger Lösung geholt habe, ist, dass ein
>SIGINT auf den ursprünglichen Parent-Prozess meine neue Prozessgruppe
>nun nicht mehr erreicht.
Ein Signal, an einen Prozess geschickt, erreicht nie mehr als nur den
einen Prozess. Aber selbst ein Signal an die Prozessgruppe, in der der
Parent-Prozess ist, erreicht die neue Prozessgruppe nicht, weil sie in
einem anderen Session ist.
>Im Beispiel des obigen Testprogramms wird das Testprogramm zwar
>beendet, aber wget läuft weiter. Das soll so nicht sein! Kann ich die
>ganze Prozessgruppe dem ursprünglichen "Parent-Prozess" unterordnen,
>dass ein SIGINT, SIGHUP, SIGTERM oder wasauchimmer auch meine neue
>Gruppe erreicht?
Das gibt es m.W. nicht. (»Kinder haften nicht für ihre Eltern«.) Es
bleibt nichts anderes übrig, als Signalbehandler im Testprogramm
einzurichten, die die gewünschten Signale abfangen und dann
gegebenenfalls ihrerseits Signale an das Shellscript verschicken. Auch
das Shellscript muss dann die gewünschten Signale abfangen und
gegebenenfalls Signale verschicken.
Helmut
--
Wenn Sie mir E-Mail schreiben, stellen | When writing me e-mail, please
Sie bitte vor meine E-Mail-Adresse | precede my e-mail address with
meinen Vor- und Nachnamen, etwa so: | my full name, like
Helmut Waitzmann <x...@example.net>, (Helmut Waitzmann) x...@example.net