2010-11-23 46 views
12

Je suis emballage une application FastCGI dans un script bash comme ceci:Tuer script bash enfants avant-plan lorsqu'un signal vient

#!/bin/bash 
# stuff 
./fastcgi_bin 
# stuff 

Comme bash exécute uniquement des pièges pour les signaux lorsque le script se termine premier plan, je ne peux pas simplement kill -TERM scriptpid parce que l'application fastcgi sera maintenue en vie.
J'ai essayé d'envoyer le fichier binaire à l'arrière-plan:

#!/bin/bash 
# stuff 
./fastcgi_bin & 
PID=$! 
trap "kill $PID" TERM 
# stuff 

Mais si je le fais comme ça, apparemment le stdin et stdout ne sont pas correctement redirigés car il ne se connecte pas avec lighttpds mod_fastgi, la version d'avant-plan fonctionne. J'ai regardé le problème et cela arrive parce que bash redirige/dev/null vers stdin quand un programme est lancé en arrière-plan, donc toute façon d'éviter cela devrait aussi résoudre mon problème.

Un indice sur la façon de résoudre ce problème?

Répondre

15

Il y a quelques options qui me viennent à l'esprit:

  • Lorsqu'un processus est lancé à partir d'un script shell, les deux appartiennent au même groupe de processus. Tuer le processus parent laisse les enfants en vie, donc tout le groupe de processus devrait être tué. Ceci peut être réalisé en passant le PGID (ID du groupe de processus) à tuer, qui est le même que le PID du parent. ej: kill -TERM -$PARENT_PID

  • Ne pas exécuter le binaire comme un enfant, mais en remplaçant le processus de script avec exec. Vous perdez la capacité pour exécuter des choses après cependant, car exec complètement remplace le processus parent. Ne pas supprimer le processus de script shell, mais le binaire FastCGI. Ensuite, dans le script, examinez le code retour et agissez en conséquence. par exemple: ./fastcgi_bin || exit -1

Selon la traite mod_fastcgi processus de travail, seule la deuxième option pourrait être viable.

+0

c'était aussi ma première pensée: 'killpg'. – tchrist

+2

'exec' était exactement ce dont j'avais besoin! Merci –

+0

Vous pouvez également envisager l'approvisionnement de l'autre script. Cela lui permet de fonctionner dans le même processus sans éliminer le reste du script en cours. (par exemple ./fastcgi.bin) (c'est-à-dire ) – GinoA

-1

Vous pouvez le faire avec un coprocess.

Éditer: bien, les coprocessus sont des processus d'arrière-plan qui peuvent avoir stdin et stdout ouverts (parce que bash prépare des fifos pour eux). Mais vous avez toujours besoin de lire/écrire sur ces fifos, et la seule primitive utile pour cela est read de bash (avec éventuellement un timeout ou un descripteur de fichier); rien de suffisamment robuste pour un CGI. Donc, à la réflexion, mon conseil serait de ne pas faire cette chose en bash. Faire le travail supplémentaire dans le fastcgi, ou dans un wrapper http comme WSGI, serait plus pratique.

+0

Pourriez-vous élaborer à ce sujet? –

1

Je ne sais pas si c'est une option pour vous ou non, mais puisque vous avez une prime, je suppose que vous pourriez aller pour des idées qui sont en dehors de la boîte.

Pourriez-vous réécrire le script bash en Perl? Perl dispose de plusieurs méthodes de gestion des processus enfants. Vous pouvez lire perldoc perlipc et plus de détails dans les modules de base IPC::Open2 et IPC::Open3.Je ne sais pas comment cela interfèrera avec lighttpd etc. ou s'il y a plus de fonctionnalités dans cette approche, mais au moins cela vous donne plus de flexibilité et encore plus à lire dans votre chasse.

0

Je ne suis pas sûr que je reçois bien votre point, mais voici ce que j'ai essayé et le processus semble être en mesure de gérer le piège (appeler trap.sh):

#!/bin/bash 

trap "echo trap activated" TERM INT 
echo begin 
time sleep 60 
echo end 

démarrer: Le

./trap.sh & 

Et jouer avec (une seule de ces commandes à la fois):

kill -9 %1 
kill -15 %1 

Ou commencer au premier plan:

./trap.sh 

Et d'interrompre avec le contrôle-C.

Cela semble fonctionner pour moi. Qu'est-ce qui ne fonctionne pas exactement pour vous?

+0

Tant «kill» avec un spécificateur de tâche et les signaux de terminal envoient à l'ensemble du groupe de processus, donc à la fois le shell et «sleep». Au fait, pourquoi essayez-vous d'attraper 'SIGKILL'? – jilles

+0

@jilles: assez bien, j'ai modifié le post pour correspondre à votre commentaire. – asoundmove

0

Vous pouvez remplacer le </dev/null implicite d'un processus d'arrière-plan en redirigeant vous stdin, par exemple:

sh -c 'exec 3<&0; { read x; echo "[$x]"; } <&3 3<&- & exec 3<&-; wait' 
1

J'ai écrit ce script il y a quelques minutes pour tuer un script bash et tous ses enfants ...

#!/bin/bash 
# This script will kill all the child process id for a given pid 
# based on http://www.unix.com/unix-dummies-questions-answers/5245-script-kill-all-child-process-given-pid.html 

ppid=$1 

if [ -z $ppid ] ; then 
    echo "This script kills the process identified by pid, and all of its kids"; 
    echo "Usage: $0 pid"; 
    exit; 
fi 

for i in `ps j | awk '$3 == '$ppid' { print $2 }'` 
do 
    $0 $i 
    kill -9 $i 
done 

Assurez-vous que le script est exécutable, ou vous obtiendrez une erreur sur le $ 0 $ i

0

Essayez de garder la stdin originale usin g ./fastcgi_bin 0<&0 &:

#!/bin/bash 
# stuff 
./fastcgi_bin 0<&0 & 
PID=$!./fastcgi_bin 0<&0 & 
trap "kill $PID" TERM 
# stuff 


# test 
#sh -c 'sleep 10 & lsof -p ${!}' 
#sh -c 'sleep 10 0<&0 & lsof -p ${!}'