Can child process signal an event to the parent? - bash

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!

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.

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.

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.

Related

Bash script that kills other instances of itself if they're running

So, I want to make a bash script, and I'm going to have it run on boot, but I'd like to update the script if I need to and run it without a reboot, so what I want to do is make the script check if there is any other instances of it running when it is loaded, and terninate any instances of the script other than itself. I want it to check instances of bash and get the path of the scripts that are being ran and kill any instances of scripts that have the same path name as it's own. How can I do this?
Example: If I am in directory /foo/bar and I run the script ../tball/script.sh, it will kill any instances of bash that are running the script /foo/tball/script.sh if they exist.
Here's the basis
kill_others() {
local mypid=$$ # capture this run's pid
declare pids=($(pgrep -f ${0##*/} # get all the pids running this script
for pid in ${pids[#]/$mypid/}; do # cycle through all pids except this one
kill $pid # kill the other pids
sleep 1 # give time to complete
done
}
declare -i count=0
while [[ $(pgrep -f ${0##*/}|wc -l) -gt 1 ]]; do
kill_outhers
((++count))
if [[ $count -gt 10 ]]; then
echo "ERROR: can't kill pids" >&2
exit 1
fi
done
The best approach is a file containing the PID of the process in a volatile filesystem like this:
echo $$ > /run/script.pid
You could refine it further by checking if that PID exists with:
if [ ! -d /proc/$(< /run/script.pid) ] ; then
rm /run/script.pid
fi
In your script you should have something like this, to remove the file on exit or if it receives a signal that kills the process:
trap "rm -f /run/script.pid" EXIT INT QUIT TERM
EDIT: Or you could append the PID to a well known pathname and kill all instances of the script with something like this before saving the PID:
kill $(< /run/script.pid) ; sleep 10 ; kill -9 $(< /run/script.pid)

Output of background process output to Shell variable

I want to get output of a command/script to a variable but the process is triggered to run in background. I tried as below and few servers ran it correctly and I got the response. But in few I am getting i_res as empty.
I am trying to run it in background as the command has chance to get in hang state and I don't want to hung the parent script.
Hope I will get a response soon.
#!/bin/ksh
x_cmd="ls -l"
i_res=$(eval $x_cmd 2>&1 &)
k_pid=$(pgrep -P $$ | head -1)
sleep 5
c_errm="$(kill -0 $k_pid 2>&1 )"; c_prs=$?
if [ $c_prs -eq 0 ]; then
c_errm=$(kill -9 $k_pid)
fi
wait $k_pid
echo "Result : $i_res"
Try something like this:
#!/bin/ksh
pid=$$ # parent process
(sleep 5 && kill $pid) & # this will sleep and wake up after 5 seconds
# and kill off the parent.
termpid=$! # remember the timebomb pid
# put the command that can hang here
result=$( ls -l )
# if we got here in less than 5 five seconds:
kill $termpid # kill off the timebomb
echo "$result" # disply result
exit 0
Add whatever messages you need to the code. On average this will complete much faster than always having a sleep statement. You can see what it does by making the command sleep 6 instead of ls -l

Monitoring life time of a process

I have a python script called hdsr_writer.py. I can launch this script in shell by calling
"python hdsr_writer.py 1234"
where 1234 is a parameter.
I made a shell script to increase the number and execute the python script with the number every 1 second
for param from 1 to 100000
python hdsr_writer.py $param &
sleep (1)
Usually, the python script executes its task within 0.5 second. However, there are times at which the python script gets stuck and resides in the system for longer than 30 seconds. I don't want that. So I would like to monitor life time of each python process executed. If it has stayed for longer than 2 second it would be killed and re-executed 2 times at most.
Note: I would like do this in the shell script not python script because I could not change the python script.
Update: More explainations about my question
Please note that: launching a new python process and monitoring python processes are independent jobs. Launching job doesn't care how many python processes are running and how "old" they are, just calls "python hdsr_writer.py $param &" every 1 second after increasing param. On the other hand, monitoring job periodically checks life time of all hdsr_writer python processes. If one has resided more than 2 second in memory, kills it, and re-runs it at most of 2 times.
Not so short answer
#/bin/bash
param=1
while [[ $param -lt 100000 ]]; do
echo "param=$param"
chances=3
while [[ $chances -gt 0 ]]; do
python tst.py $param &
sleep 2
if [[ "$(jobs | grep 'Running')" == "" ]]; then
chances=0
else
kill -9 $(jobs -l | awk '{print $2}')
chances=$(($chances-1))
if [[ $chances -gt 0 ]]; then
echo "one more chance for parameter $param"
fi
fi
done
param=$(($param+1))
done
UPD
This is another answer as requested by OP.
Here is still 2 scripts in one. But they can be spitted in two files.
Please pay attention that $() & is used to run sub-shells in background
#!/bin/bash
# Script launcher
pscript='rand.py'
for param in {1..10}
do
# start background sub-shell, where python with $param is started
echo $(
left=3
error_on_exit=1
# go if any chances left and previous run exits not with code 0
while [[ ( ( $left -gt 0 ) && ( $error_on_exit -ne 0 ) ) ]]; do
left=$(($left-1))
echo "param=$param; chances left $left "
# run python and grab python exit code (=0 if ok)
python $pscript $param
error_on_exit=$?
done
) &
done
# Script controller
# just kills python processes older than 2 seconds
# exits after no python left
# $(...) & can be removed if this code goes to separate script
$(while [[ $(ps | grep -v 'grep' | grep -c python ) != "0" ]]
do
sleep 0.5
killall -9 -q --older-than 2s python
done) &
Use a combination of sleep and nohup commands. After sleep time use kill to finish the execution of python script. You can check if the process is running with ps command.
#!/usr/bin/ksh
for param from {1..100000}
nohup python hdsr_writer.py $param &
pid=$!
sleep(2)
if [ ps -p $pid ]
then
kill -9 $pid
fi
done
Re-answer:
I'd use two scripts, the first one (script1.ksh):
#!/usr/bin/ksh
for param from {1..1000000}
nohup script2.sh $param &
done
And the second (script2.ksh):
#!/usr/bin/ksh
for i from {1..3}
python hsdr_write.py $1 &
pid=$!
sleep(2)
if [ ps -p $pid ]
then
kill -9 $pid
else
echo 'Finalizado'$1 >> log.txt
return
fi
done
The first script will launch all yours processes one after the other. The second one will check his own python process.

How to continue execution of background process in this scenario

I have 3 process a.sh, b.sh, c.sh that are executed in background.
./a.sh &
pid_a=$!
./b.sh &
pid_b=$!
./c.sh &
pid_c=$!
I need to ensure that all three processes run till the longest process terminates. If c.sh takes 10 secs, a.sh takes 3sec, b.sh takes 5sec for individual execution times, I need to execute a.sh, b.sh again to ensure that they exist till c.sh finishes.
I was trying this approach which certainly doesn't work in the above scenario
./a.sh &
while ps -p $! > /dev/null; do
./b.sh &
pid_b=$!
./c.sh &
pid_c=$!
wait $pid_c
done
How do I get this ?
You can use temporary files as flags to indicate when each process completes for the first time. Run each script in a background loop until each of the other two have completed at least once.
flag_dir=$(mktemp -d flagsXXXXX)
flag_a=$flag_dir/a
flag_b=$flag_dir/b
flag_c=$flag_dir/c
( until [[ -f $flag_b && -f $flag_c ]]; do ./a.sh; touch $flag_a; done; ) &
( until [[ -f $flag_a && -f $flag_c ]]; do ./b.sh; touch $flag_b; done; ) &
( until [[ -f $flag_a && -f $flag_b ]]; do ./c.sh; touch $flag_c; done; ) &
# Each until-loop runs until it sees the other two have completed at least one
# cycle. Wait here until each loop finishes.
wait
# Clean up
rm -rf "$flag_dir"
[Note This works for bash only. ksh93 kill behaves differently.]
As long as there's at least one process you are allowed to kill, kill -0 will return success. Tune the interval as needs be.
#! /bin/bash
interval=1
pids= && for t in 2 3; do
(sleep $t && echo slept $t seconds) & pids=${pids:+$pids }$!
done
while (kill -0 $pids) 2>& -; do
sleep $interval
# optional reporting:
for pid in $pids; do
(kill -0 $pid) 2>&- && echo $pid is alive
done
done
Results in:
6463 is alive
6464 is alive
slept 2 seconds
[1]- Done eval sleeper $t
6464 is alive
slept 3 seconds
[2]+ Done eval sleeper $t
Builtin kill is not consistent regarding errors:
$ ksh -c 'kill -0 571 6133 && echo ok || echo no'
kill: 571: permission denied
no
$ bash -c 'kill -0 571 6133 && echo ok || echo no'
bash: line 0: kill: (571) - Operation not permitted
ok
Firstly, you can use kill -0 to test the status of the process for c.sh, rather than using wait to wait for it to terminate.
Second, you can use 2 separate processes to monitor the state of scripts a.sh and b.sh
Third, this assumes that c.sh is the longest running process.
Thus, monitor process 1 does the following:
# I have pid_c
./a.sh &
pid_a=$!
while wait $pid_a; do
if kill -0 $pid_c; then
./a.sh&
pid_a=$!
fi
done
and monitor process 2 does the following:
# I have pid_c
./b.sh &
pid_b=$!
while wait $pid_b; do
if kill -0 $pid_c; then
./b.sh &
pid_b=$!
fi
done
Thus, you're monitoring the 2 processes separately. However, if you need to monitor them as well, then spawn the monitors as 2 background jobs and a simple wait will wait on c.sh as well as the 2 monitors.
Note: kill -0 $PID returns 0 if $PID is running or 1 if $PID has terminated.

Best way to make a shell script daemon?

I'm wondering if there is a better way to make a daemon that waits for something using only sh than:
#! /bin/sh
trap processUserSig SIGUSR1
processUserSig() {
echo "doing stuff"
}
while true; do
sleep 1000
done
In particular, I'm wondering if there's any way to get rid of the loop and still have the thing listen for the signals.
Just backgrounding your script (./myscript &) will not daemonize it. See http://www.faqs.org/faqs/unix-faq/programmer/faq/, section 1.7, which describes what's necessary to become a daemon. You must disconnect it from the terminal so that SIGHUP does not kill it. You can take a shortcut to make a script appear to act like a daemon;
nohup ./myscript 0<&- &>/dev/null &
will do the job. Or, to capture both stderr and stdout to a file:
nohup ./myscript 0<&- &> my.admin.log.file &
Redirection explained (see bash redirection)
0<&- closes stdin
&> file sends stdout and stderr to a file
However, there may be further important aspects that you need to consider. For example:
You will still have a file descriptor open to the script, which means that the directory it's mounted in would be unmountable. To be a true daemon you should chdir("/") (or cd / inside your script), and fork so that the parent exits, and thus the original descriptor is closed.
Perhaps run umask 0. You may not want to depend on the umask of the caller of the daemon.
For an example of a script that takes all of these aspects into account, see Mike S' answer.
Some of the top-upvoted answers here are missing some important parts of what makes a daemon a daemon, as opposed to just a background process, or a background process detached from a shell.
This http://www.faqs.org/faqs/unix-faq/programmer/faq/ describes what is necessary to be a daemon. And this Run bash script as daemon implements the setsid, though it misses the chdir to root.
The original poster's question was actually more specific than "How do I create a daemon process using bash?", but since the subject and answers discuss daemonizing shell scripts generally, I think it's important to point it out (for interlopers like me looking into the fine details of creating a daemon).
Here's my rendition of a shell script that would behave according to the FAQ. Set DEBUG to true to see pretty output (but it also exits immediately rather than looping endlessly):
#!/bin/bash
DEBUG=false
# This part is for fun, if you consider shell scripts fun- and I do.
trap process_USR1 SIGUSR1
process_USR1() {
echo 'Got signal USR1'
echo 'Did you notice that the signal was acted upon only after the sleep was done'
echo 'in the while loop? Interesting, yes? Yes.'
exit 0
}
# End of fun. Now on to the business end of things.
print_debug() {
whatiam="$1"; tty="$2"
[[ "$tty" != "not a tty" ]] && {
echo "" >$tty
echo "$whatiam, PID $$" >$tty
ps -o pid,sess,pgid -p $$ >$tty
tty >$tty
}
}
me_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
me_FILE=$(basename $0)
cd /
#### CHILD HERE --------------------------------------------------------------------->
if [ "$1" = "child" ] ; then # 2. We are the child. We need to fork again.
shift; tty="$1"; shift
$DEBUG && print_debug "*** CHILD, NEW SESSION, NEW PGID" "$tty"
umask 0
$me_DIR/$me_FILE XXrefork_daemonXX "$tty" "$#" </dev/null >/dev/null 2>/dev/null &
$DEBUG && [[ "$tty" != "not a tty" ]] && echo "CHILD OUT" >$tty
exit 0
fi
##### ENTRY POINT HERE -------------------------------------------------------------->
if [ "$1" != "XXrefork_daemonXX" ] ; then # 1. This is where the original call starts.
tty=$(tty)
$DEBUG && print_debug "*** PARENT" "$tty"
setsid $me_DIR/$me_FILE child "$tty" "$#" &
$DEBUG && [[ "$tty" != "not a tty" ]] && echo "PARENT OUT" >$tty
exit 0
fi
##### RUNS AFTER CHILD FORKS (actually, on Linux, clone()s. See strace -------------->
# 3. We have been reforked. Go to work.
exec >/tmp/outfile
exec 2>/tmp/errfile
exec 0</dev/null
shift; tty="$1"; shift
$DEBUG && print_debug "*** DAEMON" "$tty"
# The real stuff goes here. To exit, see fun (above)
$DEBUG && [[ "$tty" != "not a tty" ]] && echo NOT A REAL DAEMON. NOT RUNNING WHILE LOOP. >$tty
$DEBUG || {
while true; do
echo "Change this loop, so this silly no-op goes away." >/dev/null
echo "Do something useful with your life, young padawan." >/dev/null
sleep 10
done
}
$DEBUG && [[ "$tty" != "not a tty" ]] && sleep 3 && echo "DAEMON OUT" >$tty
exit # This may never run. Why is it here then? It's pretty.
# Kind of like, "The End" at the end of a movie that you
# already know is over. It's always nice.
Output looks like this when DEBUG is set to true. Notice how the session and process group ID (SESS, PGID) numbers change:
<shell_prompt>$ bash blahd
*** PARENT, PID 5180
PID SESS PGID
5180 1708 5180
/dev/pts/6
PARENT OUT
<shell_prompt>$
*** CHILD, NEW SESSION, NEW PGID, PID 5188
PID SESS PGID
5188 5188 5188
not a tty
CHILD OUT
*** DAEMON, PID 5198
PID SESS PGID
5198 5188 5188
not a tty
NOT A REAL DAEMON. NOT RUNNING WHILE LOOP.
DAEMON OUT
# double background your script to have it detach from the tty
# cf. http://www.linux-mag.com/id/5981
(./program.sh &) &
Use your system's daemon facility, such as start-stop-daemon.
Otherwise, yes, there has to be a loop somewhere.
$ ( cd /; umask 0; setsid your_script.sh </dev/null &>/dev/null & ) &
It really depends on what is the binary itself going to do.
For example I want to create some listener.
The starting Daemon is simple task :
lis_deamon :
#!/bin/bash
# We will start the listener as Deamon process
#
LISTENER_BIN=/tmp/deamon_test/listener
test -x $LISTENER_BIN || exit 5
PIDFILE=/tmp/deamon_test/listener.pid
case "$1" in
start)
echo -n "Starting Listener Deamon .... "
startproc -f -p $PIDFILE $LISTENER_BIN
echo "running"
;;
*)
echo "Usage: $0 start"
exit 1
;;
esac
this is how we start the daemon (common way for all /etc/init.d/ staff)
now as for the listener it self,
It must be some kind of loop/alert or else that will trigger the script
to do what u want. For example if u want your script to sleep 10 min
and wake up and ask you how you are doing u will do this with the
while true ; do sleep 600 ; echo "How are u ? " ; done
Here is the simple listener that u can do that will listen for your
commands from remote machine and execute them on local :
listener :
#!/bin/bash
# Starting listener on some port
# we will run it as deamon and we will send commands to it.
#
IP=$(hostname --ip-address)
PORT=1024
FILE=/tmp/backpipe
count=0
while [ -a $FILE ] ; do #If file exis I assume that it used by other program
FILE=$FILE.$count
count=$(($count + 1))
done
# Now we know that such file do not exist,
# U can write down in deamon it self the remove for those files
# or in different part of program
mknod $FILE p
while true ; do
netcat -l -s $IP -p $PORT < $FILE |/bin/bash > $FILE
done
rm $FILE
So to start UP it : /tmp/deamon_test/listener start
and to send commands from shell (or wrap it to script) :
test_host#netcat 10.184.200.22 1024
uptime
20:01pm up 21 days 5:10, 44 users, load average: 0.62, 0.61, 0.60
date
Tue Jan 28 20:02:00 IST 2014
punt! (Cntrl+C)
Hope this will help.
Have a look at the daemon tool from the libslack package:
http://ingvar.blog.linpro.no/2009/05/18/todays-sysadmin-tip-using-libslack-daemon-to-daemonize-a-script/
On Mac OS X use a launchd script for shell daemon.
If I had a script.sh and i wanted to execute it from bash and leave it running even when I want to close my bash session then I would combine nohup and & at the end.
example: nohup ./script.sh < inputFile.txt > ./logFile 2>&1 &
inputFile.txt can be any file. If your file has no input then we usually use /dev/null. So the command would be:
nohup ./script.sh < /dev/null > ./logFile 2>&1 &
After that close your bash session,open another terminal and execute: ps -aux | egrep "script.sh" and you will see that your script is still running at the background. Of cource,if you want to stop it then execute the same command (ps) and kill -9 <PID-OF-YOUR-SCRIPT>
See Bash Service Manager project: https://github.com/reduardo7/bash-service-manager
Implementation example
#!/usr/bin/env bash
export PID_FILE_PATH="/tmp/my-service.pid"
export LOG_FILE_PATH="/tmp/my-service.log"
export LOG_ERROR_FILE_PATH="/tmp/my-service.error.log"
. ./services.sh
run-script() {
local action="$1" # Action
while true; do
echo "### Running action '${action}'"
echo foo
echo bar >&2
[ "$action" = "run" ] && return 0
sleep 5
[ "$action" = "debug" ] && exit 25
done
}
before-start() {
local action="$1" # Action
echo "* Starting with $action"
}
after-finish() {
local action="$1" # Action
local serviceExitCode=$2 # Service exit code
echo "* Finish with $action. Exit code: $serviceExitCode"
}
action="$1"
serviceName="Example Service"
serviceMenu "$action" "$serviceName" run-script "$workDir" before-start after-finish
Usage example
$ ./example-service
# Actions: [start|stop|restart|status|run|debug|tail(-[log|error])]
$ ./example-service start
# Starting Example Service service...
$ ./example-service status
# Serive Example Service is runnig with PID 5599
$ ./example-service stop
# Stopping Example Service...
$ ./example-service status
# Service Example Service is not running
Here is the minimal change to the original proposal to create a valid daemon in Bourne shell (or Bash):
#!/bin/sh
if [ "$1" != "__forked__" ]; then
setsid "$0" __forked__ "$#" &
exit
else
shift
fi
trap 'siguser1=true' SIGUSR1
trap 'echo "Clean up and exit"; kill $sleep_pid; exit' SIGTERM
exec > outfile
exec 2> errfile
exec 0< /dev/null
while true; do
(sleep 30000000 &>/dev/null) &
sleep_pid=$!
wait
kill $sleep_pid &>/dev/null
if [ -n "$siguser1" ]; then
siguser1=''
echo "Wait was interrupted by SIGUSR1, do things here."
fi
done
Explanation:
Line 2-7: A daemon must be forked so it doesn't have a parent. Using an artificial argument to prevent endless forking. "setsid" detaches from starting process and terminal.
Line 9: Our desired signal needs to be differentiated from other signals.
Line 10: Cleanup is required to get rid of dangling "sleep" processes.
Line 11-13: Redirect stdout, stderr and stdin of the script.
Line 16: sleep in the background
Line 18: wait waits for end of sleep, but gets interrupted by (some) signals.
Line 19: Kill sleep process, because that is still running when signal is caught.
Line 22: Do the work if SIGUSR1 has been caught.
Guess it does not get any simpler than that.
Like many answers this one is not a "real" daemonization but rather an alternative to nohup approach.
echo "script.sh" | at now
There are obviously differences from using nohup. For one there is no detaching from the parent in the first place. Also "script.sh" doesn't inherit parent's environment.
By no means this is a better alternative. It is simply a different (and somewhat lazy) way of launching processes in background.
P.S. I personally upvoted carlo's answer as it seems to be the most elegant and works both from terminal and inside scripts
try executing using &
if you save this file as program.sh
you can use
$. program.sh &

Resources