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?

2
  • have you looked into kill options? Such as kill -9? Commented Aug 18, 2014 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). Commented Jul 17, 2017 at 22:45

1 Answer 1

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...

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.