12

I am wrapping a fastcgi app in a bash script like this:

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

As bash only executes traps for signals when the foreground script ends I can't just kill -TERM scriptpid because the fastcgi app will be kept alive.
I've tried sending the binary to the background:

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

But if I do it like this, apparently the stdin and stdout aren't properly redirected because it does not connect with lighttpds mod_fastgi, the foreground version does work.

EDIT: I've been looking at the problem and this happens because bash redirects /dev/null to stdin when a program is launched in the background, so any way of avoiding this should solve my problem as well.

Any hint on how to solve this?

Arkaitz Jimenez
  • 22,500
  • 11
  • 75
  • 105

7 Answers7

16

There are some options that come to my mind:

  • When a process is launched from a shell script, both belong to the same process group. Killing the parent process leaves the children alive, so the whole process group should be killed. This can be achieved by passing the negated PGID (Process Group ID) to kill, which is the same as the parent's PID. ej: kill -TERM -$PARENT_PID

  • Do not execute the binary as a child, but replacing the script process with exec. You lose the ability to execute stuff afterwards though, because exec completely replaces the parent process.

  • Do not kill the shell script process, but the FastCGI binary. Then, in the script, examine the return code and act accordingly. e.g: ./fastcgi_bin || exit -1

Depending on how mod_fastcgi handles worker processes, only the second option might be viable.

kovan
  • 680
  • 1
  • 5
  • 15
  • that was my first thought as well: `killpg`. – tchrist Nov 27 '10 at 00:54
  • You might also consider sourcing the other script. This allows it to run in the same process without eliminating the rest of the current script. (e.g. . ./fastcgi.bin) (i.e. ) – GinoA Dec 02 '10 at 19:01
1

I have no idea if this is an option for you or not, but since you have a bounty I am assuming you might go for ideas that are outside the box.

Could you rewrite the bash script in Perl? Perl has several methods of managing child processes. You can read perldoc perlipc and more specifics in the core modules IPC::Open2 and IPC::Open3.

I don't know how this will interface with lighttpd etc or if there is more functionality in this approach, but at least it gives you some more flexibility and some more to read in your hunt.

Joel Berger
  • 20,180
  • 5
  • 49
  • 104
1

I'm not sure I fully get your point, but here's what I tried and the process seems to be able to manage the trap (call it trap.sh):

#!/bin/bash

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

Start it:

./trap.sh &

And play with it (only one of those commands at once):

kill -9 %1
kill -15 %1

Or start in foreground:

./trap.sh

And interrupt with control-C.

Seems to work for me. What exactly does not work for you?

asoundmove
  • 1,292
  • 3
  • 14
  • 28
  • Both `kill` with a job specifier and terminal signals send to the entire process group, so both the shell and `sleep`. By the way, why do you attempt to catch `SIGKILL`? – jilles Nov 27 '10 at 22:30
  • @jilles: fair enough, I edited the post to match your comment. – asoundmove Dec 06 '10 at 21:49
1

I wrote this script just minutes ago to kill a bash script and all of its children...

#!/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

Make sure the script is executable, or you will get an error on the $0 $i

Andre
  • 11
  • 1
0

Try keeping the original stdin using ./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 ${!}'
nadx
  • 1
0

You can override the implicit </dev/null for a background process by redirecting stdin yourself, for example:

sh -c 'exec 3<&0; { read x; echo "[$x]"; } <&3 3<&- & exec 3<&-; wait'
jilles
  • 10,509
  • 2
  • 26
  • 39
-1

You can do that with a coprocess.

Edit: well, coprocesses are background processes that can have stdin and stdout open (because bash prepares fifos for them). But you still need to read/write to those fifos, and the only useful primitive for that is bash's read (possibly with a timeout or a file descriptor); nothing robust enough for a cgi. So on second thought, my advice would be not to do this thing in bash. Doing the extra work in the fastcgi, or in an http wrapper like WSGI, would be more convenient.

Tobu
  • 24,771
  • 4
  • 91
  • 98