3

I'm writing a simple web server using nc and bash. It looks like this:

#!/bin/bash
rm -f /var/run/streamman/out
mkfifo /var/run/streamman/out
trap "rm -f /var/run/streamman/out" EXIT

while true
do
    cat /var/run/streamman/out | nc -l 8080 > >( # parse the netcat output, to build the answer redirected to the pipe "out".
    while read line
    do
      line=$(echo "$line" | tr -d '[\r\n]')
      if echo "$line" | grep -qE '^GET /\?action' # if line starts with "GET /"
      then
        action=$(echo "$line" | cut -d '=' -f2 | cut -d ' ' -f1 | cut -d '&' -f1) # extract the request
        query_str=$(echo "$line" | cut -d '&' -f 2-)
        args=$(query_string_to_args $query_str)

        fn_exists $action && eval ${action} ${args} || echo "No route for $action"  
      elif [ "x$line" = x ] # empty line / end of request
      then
            header=$(cat /var/www/header.html)
            footer=$(cat /var/www/footer.html)
            echo  "$header somecontent $footer" > /var/run/streamman/out
      fi
    done
  )
done

There are some extra functions in there but I left them out for brevity.

I start it as a daemon using the following init script:

#!/bin/bash
user=streamman
name=streamman-httpd
prog=/usr/sbin/streamman-httpd

case $1 in
    start)
        start-stop-daemon --start --user $user --chuid $user --background --umask 0000 --exec $prog > /var/log/streamman/streamman.log 2>&1
        ;;
    stop)
        /sbin/start-stop-daemon --stop --user "$user" --name "$name" --retry=TERM/5/KILL/1
        ;;
    restart)
        ;;
    *)
        ;;
esac

Here is the output of ps afx after starting the service:

3023 ?        S      0:00 /bin/bash /usr/sbin/streamman-httpd
3065 ?        S      0:00  \_ nc -l 8080
3066 ?        S      0:00      \_ /bin/bash /usr/sbin/streamman-httpd

As you can see, a child process has started up for some reason. I think it's to do with the way I use nc and the named pipe.

Now when I try to stop the service I get this:

Program streamman-httpd, 1 process(es), refused to die.

And the output of ps afx:

3065 ?        S      0:00 nc -l 8080
3066 ?        Z      0:00  \_ [streamman-httpd] <defunct>

For some reason the child process won't die.

I've tried using pid files, but I can't get my server script to output the child's pid to a file.

How can I make it kill both processes?

  • have you looked into kill options? Such as kill -9? – Simply_Me Aug 18 '14 at 6:42
  • In my experience, shell scripts don't deal well with direct kills because shells don't automatically kill their child processes. If you want robust cleanups with shell scripts, you need to run them in separate process groups and you need to aim the kill at the whole group, not just the topmost process. I'm using a C program that ensures the script runs in a separate proces group and that all signals aimed at the topmost process are forwarded to the whole child group (I don't know how to handle signal forwarding corner cases in shell). – PSkocik Jul 17 '17 at 22:45
1

I think the primary cause is "exec" call in your "start" section. Could you add to trap "rm -f /var/run/streamman/out" EXIT string something like:

; ps ax | kill `awk '$5 ~ /nc -l 8080/ {print $1}`

I understand this is a dirty workaround...

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.