howto start a couple piped processes with start-stop-daemon

742 views Asked by At

I've been googling around looking for a way to properly and cleanly start a series of binaries wich are pipeed toghether, and let all the whole stuff be launched normaly as a series of daemons would be... but no luck with this.

In a nutshell, here's my command:

ossrecord -s48000 -b16 -c2 -d/dev/oss/oss_envy24ht0/pcmin0 - | opusenc --bitrate 96 - - | oggfwd 192.168.1.12 7000 mysecret /mystream.opus

Basically it grabs oss v4 pcm audio from a capure device, pipes the output aout to opusenc binary, which in turn encodes it to opus format, and finally, I pipe the result out to a icecast2 server (all this is more or less nicely documented on the opus docs) And it works nicely!

The problem is that all this ocurs on the cli, it captures stdout ans starts yelding output. Being all that stuff a series of piped processes I dont manage to control them. Moreover, it would be nice to have some kind of launching/stopping script, that would make it possible to have all this running in the background.

I have being struggling with start-stop-daemon as I usually do, but my knowledge basically covers handling a single binary and its paramenters (as found on docs), and I think I'm blindly stepping on holy guru's domains.

So, maybe someone may find out how this can be totally or partially acomplished.

2

There are 2 answers

2
Andreas Florath On BEST ANSWER

I came across exactly the same problem and decided for me to solve this once and forever ;-) [One reason was your question here at stackoverflow without an answer or even a comment.]

Therefore I wrote a small program called pipexec. The features in a nutshell:

  1. Starts programs and pipes them together.
  2. When a SIGTERM, SIGINT or SIGQUIT is send to the program, it terminates all it's children and then itself.
  3. When a SIGHUP is sent to pipexec it restarts all the children (including the pipes in between them).
  4. When a child terminates un-normally (e.g. by a signal), all children are terminated and restarted (including the pipes).
  5. Support for pid file.

It's written in plain C99 with Linux in mind. 500 lines of code resulting in about 20k executable. I integrated pipexec into the RHEL6 using /etc/init.d/functions daemon and killproc and it works seamlessly. The pid file feature was used here.

Kind regards - Andreas

0
andrean On

I ran into the same issue, and pipexec seems like the proper way to do it. If someone doesn't want to include additional components, I found this to be a bit hackier, but still working solution (developed in a BuildRoot-built resource constrained environment, with BusyBox - so not all the unix/linux tools are available or fully featured - e.g. ps):

#!/bin/sh
#
# rtl_fm          Stream packets from RTL-SDR with netcat through UDP

NAME=rtl_fm
PIDFILE=/var/run/$NAME.pid
LOGFILE=/var/log/$NAME.log
DAEMON="/usr/bin/rtl_fm -f 144.800M -o 4 - | /usr/bin/nc -u localhost 7355"

find_pgid (){
        local pid="$1"
        local pgid=$(ps -o pid,pgid | awk -v var="$pid" '$1 == var {print $2}')
        echo "$pgid"
}

start(){
        printf "Starting $NAME: "
        start-stop-daemon --start --quiet  --make-pidfile --pidfile $PIDFILE --background \
                          --startas /bin/sh -- -c "exec $DAEMON"

        [ $? = 0 ] && echo "OK" || echo "FAIL"
}

stop(){
        printf "Stopping $NAME: "
        pid=$(cat $PIDFILE)
        kill -TERM -$(find_pgid $pid) && rm $PIDFILE
        [ $? = 0 ] && echo "OK" || echo "FAIL"
}

case "$1" in
        start)
                start
                ;;
        stop)
                stop
                ;;
        restart)
                stop
                start
                ;;
        *)
                echo "Usage: $0 {start|stop|restart}"
                exit 1
                ;;
esac

Two utilities are piped here and ran as a service in the background. The start operation is more-or-less standard start-stop-daemon (but it starts a new shell and runs the piped executables within it), while the stop operation takes the PID of the shell that runs all the tools within it, finds the Process Group ID of it, and then kills the whole process group, essentially terminating the shell and all the subprocesses that run under it.

With BusyBox, there is no pkill or ps with pid filter arguments out of the box, so the custom function with awk etc for finding the process group id was necessary (if I were to keep the tools as-is, without upgrading to something more heavy-weight).