Bash daemon doesn't collect its chilidren - linux

I've written this bash daemon that keeps an eye on a named pipe, logs everything it sees on a file named $LOG_FILE_BASENAME.$DATE, and it also creates a filtered version of it in $LOG_FILE_BASENAME.$DATE :
while true
do
DATE=`date +%Y%m%d`
cat $NAMED_PIPE | tee -a "$LOG_FILE_BASENAME.$DATE" | grep -P -v "$EXCEPTIONS" >> "$ACTIONABLE_LOG_FILE"
done
When the daemon is running, this is how the process table looks:
/bin/sh the_daemon.sh
\_ cat the_fifo_queue
\_ tee -a log_file.20150807
\_ grep -P -v "regexp" > filtered_log_file
The problem is that when I kill the deamon (SIGTERM), the cat, the tee, and the grep processes that where spawned by the daemon are not collected by the parent. Instead, they become orphans and keep on waiting for input on the named pipe.
Once the FIFO receives some input, then they process that input as instructed and die.
How can make the daemon kill it's children before dying?
I know I could have the daemon manually collect the PIDs of its children and kill them before kicking the bucket, but isn't there an automated way to do this?
Thanks

Related

How to extract a column value from a command output in shell script

I am trying to write a shell script where I want to kill a list of processes given by fuser command.
The output of fuser is given. I want to kill the pids listed
kill -9 157909 1504107 1504111 1504112 2690311 3206490
How do I do that?
-k, --kill
Kill processes accessing the file. Unless changed with -SIGNAL,
SIGKILL is sent. An fuser process never kills itself, but may
kill other fuser processes. The effective user ID of the
process executing fuser is set to its real user ID before
attempting to kill.
fuser -k will do it.
ps -u beadm | awk '{print $2}' | while read line; do kill -9 $line; done

Pass bash command a comment to see from pkill

I have an aribtrary bash command being run that I want to attach some identifying comment to so that I may pkill it if necessary.
For example:
sleep 1000 #uniqueHash93581
pkill -f '#uniqueHash93581'
... but the #uniqueHash93581 does not get interpreted, so pkill won't find the process.
Any way to pass this unique hash so that I may pkill the process?
Bash removes comments before running commands.
A workaround with Linux and GNU grep:
Prefix your command with a variable with a unique value
ID=uniqueHash93581 sleep 1000
Later search this variable to get the PID and kill the process
grep -sa ID=uniqueHash93581 /proc/*/environ | cut -d '/' -f 3 | xargs kill
exec the command in a subshell, and use the -a option to give it a recognizable name. For example:
$ (exec -a foobar sleep 1000) &
$ ps | grep foobar
893 ttys000 0:00.00 foobar 10
Or, just run the job in the background and save its PID.
$ sleep 1000 & pid=$!
$ kill "$pid"

Daemon won't kill children that are reading from a named pipe

I've written this bash daemon that keeps an eye on a named pipe, logs everything it sees on a file named $LOG_FILE_BASENAME.$DATE, and it also creates a filtered version of it in $ACTIONABLE_LOG_FILE:
while true
do
DATE=`date +%Y%m%d`
cat $NAMED_PIPE | tee -a "$LOG_FILE_BASENAME.$DATE" | grep -P -v "$EXCEPTIONS" >> "$ACTIONABLE_LOG_FILE"
done
pkill -P $$ # Here it's where it should kill it's children
exit 0
When the daemon is running, this is how the process table looks:
/bin/sh the_daemon.sh
\_ cat the_fifo_queue
\_ tee -a log_file.20150807
\_ grep -P -v "regexp" > filtered_log_file
The problem is that when I kill the daemon (SIGTERM), the cat, the tee, and the grep processes that where spawned by the daemon are not collected by the parent. Instead, they become orphans and keep on waiting for input on the named pipe.
Once the FIFO receives some input, then they process that input as instructed and die.
How can I make the daemon kill its children before dying? Why aren't they dying with pkill -P $$?
You want to setup a signal handler for your script which kills all members of its process group (its children) in case the script itself gets signalled:
#!/bin/bash
function handle_sigterm()
{
pkill -P $$
exit 0
}
trap handle_sigterm SIGTERM
while true
do
DATE=`date +%Y%m%d`
cat $NAMED_PIPE | tee -a "$LOG_FILE_BASENAME.$DATE" | grep -P -v "$EXCEPTIONS" >> "$ACTIONABLE_LOG_FILE"
done
handle_sigterm
exit 0
Update:
As per pilcrow's comment replace
cat $NAMED_PIPE | tee -a "$LOG_FILE_BASENAME.$DATE" | grep -P -v "$EXCEPTIONS" >> "$ACTIONABLE_LOG_FILE"
by
cat $NAMED_PIPE | tee -a "$LOG_FILE_BASENAME.$DATE" | grep -P -v "$EXCEPTIONS" >> "$ACTIONABLE_LOG_FILE" &
wait $!

Can i wait for a process termination that is not a child of current shell terminal?

I have a script that has to kill a certain number of times a resource managed by a high avialability middelware. It basically checks whether the resource is running and kills it afterwards, i need the timestamp of when the proc is really killed. So i have done this code:
#!/bin/bash
echo "$(date +"%T,%N") :New measures Run" > /home/hassan/logs/measures.log
for i in {1..50}
do
echo "Iteration: $i"
PID=`ps -ef | grep "/home/hassan/Desktop/pcmAppBin pacemaker_app/MainController"|grep -v "grep" | awk {'print$2'}`
if [ -n "$PID" ]; then
echo "$(date +"%T,%N") :Killing $PID" >> /home/hassan/logs/measures.log
ps -ef | grep "/home/hassan/Desktop/pcmAppBin pacemaker_app/MainController"|grep -v "grep" | awk {'print "kill -9 " $2'} | sh
wait $PID
else
PID=`ps -ef | grep "/home/hassan/Desktop/pcmAppBin pacemaker_app/MainController"|grep -v "grep" | awk {'print$2'}`
until [ -n "$PID" ]; do
sleep 2
PID=`ps -ef | grep "/home/hassan/Desktop/pcmAppBin pacemaker_app/MainController"|grep -v "grep" | awk {'print$2'}`
done
fi
done
But with my wait command i get the following error message: wait: pid xxxx is not a child of this shell
I assume that You started the child processes from bash and then start this script to wait for. The problem is that the child processes are not the children of the bash running the script, but the children of its parent!
If You want to launch a script inside the the current bash You should start with ..
An example. You start a vim and then You make is stop pressing ^Z (later you can use fg to get back to vim). Then You can get the list of jobs by using the˙jobs command.
$ jobs
[1]+ Stopped vim myfile
Then You can create a script called test.sh containing just one command, called jobs. Add execute right (e.g. chmod 700 test.sh), then start it:
$ cat test.sh
jobs
~/dev/fi [3:1]$ ./test.sh
~/dev/fi [3:1]$ . ./test.sh
[1]+ Stopped vim myfile
As the first version creates a new bash session no jobs are listed. But using . the script runs in the present bash script having exactly one chold process (namely vim). So launch the script above using the . so no child bash will be created.
Be aware that defining any variables or changing directory (and a lot more) will affect to your environment! E.g. PID will be visible by the calling bash!
Comments:
Do not use ...|grep ...|grep -v ... |awk --- pipe snakes! Use ...|awk... instead!
In most Linux-es you can use something like this ps -o pid= -C pcmAppBin to get just the pid, so the complete pipe can be avoided.
To call an external program from awk you could try system("mycmd"); built-in
I hope this helps a bit!

how to use kill SIGUSR2 in bash?

I use iptraf to monitor the network traffic in linux, and the shell command is(make iptraf running in background):
iptraf -s eth0 -f -B -L ./traffic.dat
if I want to get the result, I have to stop iptraf first, so I use the shell command:
kill -SIGUSR2 $pid
however, I could not stop iptraf if I move these shell commands into a bash script file(net.sh), and I get an error:
kill: SIGUSR2: invalid signal specification
I use 'kill -l' in the script file(net.sh), and I find there is no parameter which name is SIGUSR2. and I would get nothing if I use USR2 or -9.
the complete script file is:
iptraf -s eth0 -f -B -L ./temp.txt
pid=`ps -ef | grep iptraf | grep -v grep | awk '{print $2}'`
kill -USR2 $pid
cat temp.txt
I get nothing after these commands.
what shoud I do if I want to get the result?
SIGUSR2 is architecture depended and can have a value out of 31, 12 or 17. This is described in man 7 signal. You'll have to find out which value is appropriate for your system. Usually this is done by having a look into:
/usr/include/asm/signal.h
On my system - Ubuntu 12.04 AMD 64 - it has a value of 12:
#define SIGUSR2 12
Once you know the proper numeric value for SIGUSR2 on your system, you can send this signal using:
kill -SIGNO PID
# In this case
kill -12 PID
On my Linux box it works.
I ran an infinite loop (pid = 4574), then I ran
#!/bin/bash
kill -l | grep USR2
kill -SIGUSR2 4574
kill -l has showed the signal and kill -SIGUSR2 has sent the signal (killing the process).
Check if you are running Bash or some other shell (e.g., dash, busybox, etc.)
Cross-platform way to do this: use -s without the SIG prefix. E.g.,:
kill -s USR2 $pid
This seems to work on both MacOS and linux.

Resources