3

Consider the scenario, when I want to spawn a child process that is intended to run in background, but takes a moment to setup, like the launch-git-daemon. How the calling script can be notified that the script is ready to start serving?

Calling (parent) script:

#!/bin/bash

./launch-git-daemon /srv/git-repos &
pid=$!

wait-until-launch-git-daemon-notifies-us

#do stuff that involves access to the git server

launch-git-daemon:

#!/bin/bash
repopath="$1"

sudo dpkg -s git>/dev/null
if [ $? -eq 0 ]; then
    echo "git already installed!"
else
    sudo apt-get --yes install git
fi

signal-to-parent-that-we-are-ready-to-serve

git daemon --verbose --base-path="$repopath" --export-all --informative-errors --enable=receive-pack

I know that child can always touch some tmp file, and parent can test for its presence, but it looks so awkward and inefficient. I hope there is some sort of inter-process communication mechanism for it built in bash!

Adam Ryczkowski
  • 7,592
  • 13
  • 42
  • 68
  • 1
    Use the `$PPID` variable? – Etan Reisner Oct 23 '14 at 16:16
  • Only way I have used is the file way, perhaps socket programming is another way to do this – jgr208 Oct 23 '14 at 16:16
  • 2
    Solution [here might help](http://stackoverflow.com/questions/10862970/inter-process-communication-without-fifos) – arco444 Oct 23 '14 at 16:16
  • wait i remember now, piping is a way to do this. pipe between the two. – jgr208 Oct 23 '14 at 16:17
  • @jgr208 I am new to in piping in bash. Can you point me to some example (or post an answer)? I can also see, that the solution with alternate FD (file descriptors?) as suggested by arco444 can also work. I wonder which is simpler to use. – Adam Ryczkowski Oct 23 '14 at 16:21
  • @EtanReisner Can you be more specific? How does a knowledge of parent PID can help me here? I need a mechanism to give the parent a nudge. – Adam Ryczkowski Oct 23 '14 at 16:24
  • 1
    http://www.linuxjournal.com/content/using-named-pipes-fifos-bash – jgr208 Oct 23 '14 at 16:24
  • 3
    You need to signal to the parent that the child is ready, correct? So send a signal from the child to the parent and trap it in the parent. – Etan Reisner Oct 23 '14 at 16:28
  • @EtanReisner Do you suggest catching the signal with `trap`? AFAIK `trap` doesn't wait for a signal to happen, it is a tool for registering an event handler. I need something easy to code that waits until a specific signal comes. – Adam Ryczkowski Oct 23 '14 at 16:34
  • 1
    `trap` does not wait, that's true but you can easily wait in a small sleep loop for a trap to set a variable or run a second script from the trap handler and then exit, etc. – Etan Reisner Oct 23 '14 at 16:36
  • Parent can watch a logfile of its child. – Cyrus Oct 23 '14 at 16:42
  • Unrelated: You can replace `pid=\`jobs -p 1\`` with the simpler, more idiomatic, and less fragile `pid=$!` – rici Oct 23 '14 at 17:31

3 Answers3

2

You can use named pipes for this kind of purpose.

$ mknod testpipe p

$ ls -la
total 20
drwxr-xr-x  2 gp         users 4096 Oct 23 12:31 .
drwxr-xr-x 11 gp         users 4096 Oct 23 12:29 ..
prw-r--r--  1 gp         users    0 Oct 23 12:31 testpipe


$ ( read line <testpipe ; echo "We read this: $line"; ) &
[1] 17065

$ echo "This is a test" >testpipe

$ We read this: This is a test
[1]+  Done                    ( read line < testpipe; echo "We read this: $line" )

So, as soon as the child writes in the named pipe the parent gets the 'read' statement satisfied. (You could read multiple lines if you read in a loop.)

If you think about it, you can realize that the child can do more than write just anything. It can pass information back to the parent, tell it if things are going well, or failed, etc. And you are not limited to just one child. The parent could create one named pipe, and spawn multiple child which would each use the same pipe to tell the parent when they are ready.

Just keep in mind that normally you should have only one task reading from the pipe. If there are more than one reader, there is no way of predicting which one will get the lines written by the childs.

cpu
  • 567
  • 4
  • 6
  • Excellent answer. Foe me pipes offer a little too much functionality for the cost of a little more points of failures and letters to type - thats why I accepted the signals version. – Adam Ryczkowski Oct 23 '14 at 18:33
2

Here is something of a fair comparison of both methods.

Named pipe

parent.sh:

#!/bin/bash

#Spawn the child process
pipe=/tmp/mypipe
if [[ ! -p $pipe ]]; then
    mkfifo $pipe
fi
bash -x ./child.sh &
childpid=$!

sleep 10 #Do some work, we will need a child soon after it.

read ans <$pipe
if [[ "$ans" != "ready to serve" ]]; then
    echo "Unknown answer from child!"
    exit 1
fi

#Here we can do stuff that require readiness from the client

sleep 5

#Kill the child

kill $childpid

child.sh:

#!/bin/bash

if [ -n "$1" ]; then
    sleep 20 #Sometimes it takes long to start serving.
fi

pipe=/tmp/mypipe

if [[ ! -p $pipe ]]; then
    echo "Cannot find a parent process. This script is not intended to be run alone."
    exit 1
fi

echo "ready to serve" >$pipe

sleep 10000

Signals

parent.sh:

#!/bin/bash

#Spawn the child process

sigusr1_received=false
catch_sigusr1 () { sigusr1_received=true ;}
trap catch_sigusr1 USR1
bash -x ./child.sh &
childpid=$!

sleep 10 #Do some work, we will need a child soon after it.

while ! $sigusr1_received ; do sleep 1 ; done

#Here we can do stuff that require readiness from the client

sleep 5

#Kill the child

kill $childpid

child.sh:

#!/bin/bash

if [ -n "$1" ]; then
    sleep 20 #Sometimes it takes long to start serving.
fi

kill -USR1 $PPID

sleep 10000

I will go with signals. Named pipes offer just too much flexibility for the cost of terseness and simplicity.

Adam Ryczkowski
  • 7,592
  • 13
  • 42
  • 68
1

Run this script in one terminal:

#! /bin/bash

sigusr1_received=false

catch_sigusr1 () { sigusr1_received=true ;}

trap catch_sigusr1 USR1

echo "My PID is $$"
echo "Waiting for SIGUSR1 ..."

while ! $sigusr1_received ; do sleep 1 ; done

echo "SIGUSR1 received."

exit 0

Now kill -USR1 the running script in another terminal, and it will detect the signal reception in the second.

Edouard Thiel
  • 5,878
  • 25
  • 33