Prevent process from killing itself using pkill - linux

I'm writing a stop routine for a start-up service script:
do_stop()
{
rm -f $PIDFILE
pkill -f $DAEMON || return 1
return 0
}
The problem is that pkill (same with killall) also matches the process representing the script itself and it basically terminates itself. How to fix that?

You can explicitly filter out the current PID from the results:
kill $(pgrep -f $DAEMON | grep -v ^$$\$)
To correctly use the -f flag, be sure to supply the whole path to the daemon rather than just a substring. That will prevent you from killing the script (and eliminate the need for the above grep) and also from killing all other system processes that happen to share the daemon's name.

pkill -f accepts a full blown regex. So rather than pkill -f $DAEMON you should use:
pkill -f "^"$DAEMON
To make sure only if process name starts with the given daemon name then only it is killed.
A better solution will be to save pid (Proces Id) of the process in a file when you start the process. And for the stopping the process just read the file to get the process id to be stopped/killed.

Judging by your question, you're not hard over on using pgrep and pkill, so here are some other options commonly used.
1) Use killproc from /etc/init.d/functions or /lib/lsb/init-functions (which ever is appropriate for your distribution and version of linux). If you're writing a service script, you may already be including this file if you used one of the other services as an example.
Usage: killproc [-p pidfile] [ -d delay] {program} [-signal]
The main advantage to using this is that it sends SIGTERM, waits to see if the process terminates and sends SIGKILL only if necessary.
2) You can also use the secret sauce of killproc, which is to find the process ids to kill using pidof which has a -o option for excluding a particular process. The argument for -o could be $$, the current process id, or %PPID, which is a special variable that pidof interprets as the script calling pidof. Finally if the daemon is a script, you'll need the -x so your trying to kill the script by it's name rather than killing bash or python.
for pid in $(pidof -o %PPID -x progd); do
kill -TERM $pid
done
You can see an example of this in the article Bash: How to check if your script is already running

Related

Does kill command kill processes specific to a path in linux

I have seen many discussions here on kill command. But my confusion is different. I have many processes with the same name and I have to automate the killing. Hence I can't use the pid. So is it possible that if I go to a specific path and use kill <pname> then only the process related to that path will get killed?
Or is there some way to incorporate the path name in kill command?
Instead of using a pid, you could always use the pkill command and have it check against some regular expression. If you pass it the -f flag, it allows you to check against the entire command line rather than just the process name.
Something like this would probably do the trick:
pkill -TERM -u username -f "mwhome.*weblogic\\.NodeManager"
-f is where you would pass in your regex
-u is also useful so that you only affect pid's running as specific users
No, but you when you start the process with
yourcommand & echo $!
or wrap it in a small script
#!/bin/bash
yourcommand &
echo $! >/path/to/pid.file
you can save the pid. And then kill the process with this pid. This is the normal way how to manage the processes. If you look in the normal init.d scripts of perhaps nginx, they do it the same way. Just saving the pid in a file, and at stopping just read the pid and kill the process.

How to identify a job given from your user account and kill it

I had given a job in a remote server yesterday from my home. The command was
sh run.sh >& out &
The run.sh will excute a program (average.f) more than 1000 times recurssively.
Today, in my office, I found some mistake in my run.sh. So I would like to kill it.
I used top command, but it is not showing the run.sh. It is only showing average.f. So, once, I killed it with kill PID, it is again starting average.f with another PID and producing outputs.
ps -u is not showing either run.sh or average.f.
Can anybody please help me how to kill this job.
find your job id with the process or application name . example is given below - I am killing java process here
ps -aef|grep java
// the above command will give you pid, now fire below command to kill that job
kill -9 pid
// here pid is a number which you get from the first command
ps -ef | grep run.sh | grep -v grep | awk '{print $2}' | xargs kill -9
Use pstree(1) (probably as pstree -p) to list the process tree hierarchy, then kill(1) (first with -TERM, then with -QUIT, and if that does not work, at last with -KILL) your topmost shell process running run.sh (or else the few "higher" processes). Perhaps use killall(1) or pidof(1) (or pgrep(1) or pkill)
You might want to kill the process group, with a negative pid.
You should never at first kill -KILL a process (but only at last resort); some programs (e.g. database servers, sophisticated numerical computations with application checkpointing, ...) have installed a SIGTERM or SIGQUIT signal handler to clean up their mess and e.g. save some state (on the disk) in a sane way. If you kill -KILL them, they could leave the mess uncleaned (since SIGKILL cannot be caught, see signal(7)....)
BTW, you should use ps auxw to list all processes, read ps(1)

Killing processes automatically - linux

I al running this command :
pgrep -l someprocess
I get some outputs XXXX someprocess
then I kill every process appearing by hand, I would like to write a script to do it automatically, but this doesn(t make sense
kill -9 $(pgrep -l someprocess | grep "^[0-9]{4}")
someone could help ?
You can use either pkill or killall to accomplish exactly that.
I found this short and clear summary explaining the different ways of killing processes.
pkill is as simple as: pkill someprocess.
#ewm already included a detailed explanation about killall in his answer, so I'm not repeating it here.
You might want to look at the 'killall' command:
KILLALL(1) User Commands KILLALL(1)
NAME
killall - kill processes by name
SYNOPSIS
killall [-Z,--context pattern] [-e,--exact] [-g,--process-group] [-i,--interactive] [-q,--quiet]
[-r,--regexp] [-s,--signal signal] [-u,--user user] [-v,--verbose] [-w,--wait] [-I,--ignore-case]
[-V,--version] [--] name ...
killall -l
killall -V,--version
DESCRIPTION
killall sends a signal to all processes running any of the specified commands. If no signal name is
specified, SIGTERM is sent.
Signals can be specified either by name (e.g. -HUP or -SIGHUP ) or by number (e.g. -1) or by option
-s.
If the command name is not regular expression (option -r) and contains a slash (/), processes execut-
ing that particular file will be selected for killing, independent of their name.
killall returns a zero return code if at least one process has been killed for each listed command,
or no commands were listed and at least one process matched the -u and -Z search criteria. killall
returns non-zero otherwise.
A killall process never kills itself (but may kill other killall processes).

How to set process group of a shell script

How to set process group of a shell script ? Also I want all the child process to be in the same process group
I expect something similar to setpgid() in C.
As PSkocik points out, it is possible to run a process in its own process group, in most shells, by activating job control (“monitor mode”).
(set -m; exec process_in_its_own_group)
Linux has a setsid utility, which runs the command passed as argument in its own session (using the eponymous system call). This is stronger than running it in its own process group à la setpgrp, but that may be ok for your purpose.
If you want to place the process in an existing group rather than in its own group (i.e. if you want the full power of setpgid), there's no common shell utility. You have to use C/Perl/…
I'll answer part of what I understand:
How to force current bash shell script to be it self process group:
I put this in the beginning of my bash script:
pgid_from_pid() {
local pid=$1
ps -o pgid= "$pid" 2>/dev/null | egrep -o "[0-9]+"
}
pid="$$"
if [ "$pid" != "$(pgid_from_pid $pid)" ]; then
exec setsid "$(readlink -f "$0")" "$#"
fi
Why do I would need this ?
When launching a program from an interactive bash session, it gets its own new process group. But this is not the case if your program is called from a bash script (non-interactive). If your program relies on being the process group owner in both condition you'll need this.
I don't think Bourne, bash, or zsh will let you do that, but you could do it in perl using the built-in setpgrp (note the slight name difference from POSIX). Pass zero as the PID to modify the group of the perl process itself:
setpgrp(0, 12345) || die "$!"
You might think you could use perl from, say, bash to set the bash process's group (by passing $$ to a perl script, for example), but I don't think the perl process would be able to modify the group of a process that it didn't fork.
Depending on what you're trying to do, the job control features in various shells may give you what you need, in a different way, like if you just want to detach from the terminal.
UPDATE: I think it's strange that this answer has received a couple of down-votes without clear explanation why. My guess is that the downvoters are misunderstanding the question, which is asking how to change the process group of the current shell. Or perhaps they know how to do a setpgrp from the shell but are keeping the secret to themselves.
If you turn set -m on, new processes will be spawned in a new process group, and if they're backgrounded, they won't have SIGINT and SIGQUIT ignored.
if [ $$ = $(ps -o pgid -hp $$) ]; then
echo already a process group leader;
else
set -m
$0 "$#" #optionally with &
set +m
fi
The new processes group of programs run after set -m takes over as the foreground process group of the terminal, unless they're run in the background.
The set -m is apparently semi-standard, required by POSIX if the implementation supports "User Portability Utilities".
In practice it works on bash, dash, ksh, pdksh, sh, yash, and zsh. posh doesn't have it.
Here's a late synthesis, taken from several other good answers here, if your intention is to cleanup any spawned subshell processes (even if the script itself is not directly launched from an interactive shell, but from another process, and therefore doesn't automatically becomes its own process group leader), relaunching the current script as a new process group leader if necessary.
# First, obtain the current PGID, by parsing the output of "ps".
pgid=$(($(ps -o pgid= -p "$$")))
# Check if we're already the process group leader; if not, re-launch ourselves.
# Use setsid instead of set -m (...) to avoid having another subshell in between. This helps that the trap gets executed when the script is killed.
[ $$ -eq $pgid ] || exec setsid --wait "${BASH_SOURCE[0]}" "$#"
# Kill any subshell processes when the script exits.
trap "kill -- -$pgid" EXIT
# Note: If the script only starts background jobs, and that's all you care about, you can replace all of the above with this simple trap:
#trap "jobs -p | xargs kill --" EXIT # Kill remaining jobs when the script exits.
Nested commands
Another complication is introduced when one script that does subshell cleanup is invoked by another such script. The process group leadership does not nest; once a script assumes leadership, its lifetime is not controlled any longer by a parent script, so when the parent script gets interrupted or killed, the nested script will linger on. That's not what the user usually wants.
The following script fragments extend the above implementation with a cooperation model, so that only the toplevel script assumes the process group leadership, indicating this to subshells by exporting $PGID. If a subshell finds an existing leader, it will not assume leadership itself, and limits its own cleanup tasks to remaining jobs. Other subshells will only be killed once the toplevel script exits. (So this cooperation model works best when one script only invokes one or only few other scripts.)
if [ -z "$PGID" ]; then # No parent script has become the process group leader yet.
pgid=$(($(ps -o pgid= -p "$$"))) # By defining this, we'll be killing subshell processes of this process group when we're done or interrupted. Any children with the same ambition will defer to us.
if [ $$ -eq $pgid ]; then
export PGID=$pgid # We are (already / after setsid) in our own process group, announce our leadership to any children, so that they don't become leaders themselves and thereby decouple themselves from our lifetime control.
else
exec setsid --wait "${BASH_SOURCE[0]}" "$#" # Use setsid instead of set -m (...) to avoid having another subshell in between.
fi
fi
if [ -n "$pgid" ]; then
trap "kill -- -$pgid" EXIT # If we're the leader, kill subshell processes when the script exits.
else
trap "jobs -p | xargs kill --" EXIT # Someone else is the leader; killing remaining jobs is all we can do here.
fi
As #Rob Davis pointed out in his answer, setting process group is not what you want for shells.
Instead you want to use their process control mechanisms. This answer covers doing this for sh on linux and borne. In short:
#! /bin/sh
# Kill all opened jobs on exit.
trap 'kill $(jobs -p)' EXIT
This will kill any jobs opened in the backrground (e.g. with &).

Kill bash script foreground children when a signal comes

I am wrapping a fastcgi app in a bash script like this:
#!/bin/bash
# stuff
./fastcgi_bin
# stuff
As bash only executes traps for signals when the foreground script ends I can't just kill -TERM scriptpid because the fastcgi app will be kept alive.
I've tried sending the binary to the background:
#!/bin/bash
# stuff
./fastcgi_bin &
PID=$!
trap "kill $PID" TERM
# stuff
But if I do it like this, apparently the stdin and stdout aren't properly redirected because it does not connect with lighttpds mod_fastgi, the foreground version does work.
EDIT: I've been looking at the problem and this happens because bash redirects /dev/null to stdin when a program is launched in the background, so any way of avoiding this should solve my problem as well.
Any hint on how to solve this?
There are some options that come to my mind:
When a process is launched from a shell script, both belong to the same process group. Killing the parent process leaves the children alive, so the whole process group should be killed. This can be achieved by passing the negated PGID (Process Group ID) to kill, which is the same as the parent's PID. ej: kill -TERM -$PARENT_PID
Do not execute the binary as
a child, but replacing the script
process with exec. You lose the
ability to execute stuff afterwards
though, because exec completely
replaces the parent process.
Do not kill the shell script process, but the FastCGI binary. Then, in the script, examine the return code and act accordingly. e.g: ./fastcgi_bin || exit -1
Depending on how mod_fastcgi handles worker processes, only the second option might be viable.
I have no idea if this is an option for you or not, but since you have a bounty I am assuming you might go for ideas that are outside the box.
Could you rewrite the bash script in Perl? Perl has several methods of managing child processes. You can read perldoc perlipc and more specifics in the core modules IPC::Open2 and IPC::Open3.
I don't know how this will interface with lighttpd etc or if there is more functionality in this approach, but at least it gives you some more flexibility and some more to read in your hunt.
I'm not sure I fully get your point, but here's what I tried and the process seems to be able to manage the trap (call it trap.sh):
#!/bin/bash
trap "echo trap activated" TERM INT
echo begin
time sleep 60
echo end
Start it:
./trap.sh &
And play with it (only one of those commands at once):
kill -9 %1
kill -15 %1
Or start in foreground:
./trap.sh
And interrupt with control-C.
Seems to work for me.
What exactly does not work for you?
I wrote this script just minutes ago to kill a bash script and all of its children...
#!/bin/bash
# This script will kill all the child process id for a given pid
# based on http://www.unix.com/unix-dummies-questions-answers/5245-script-kill-all-child-process-given-pid.html
ppid=$1
if [ -z $ppid ] ; then
echo "This script kills the process identified by pid, and all of its kids";
echo "Usage: $0 pid";
exit;
fi
for i in `ps j | awk '$3 == '$ppid' { print $2 }'`
do
$0 $i
kill -9 $i
done
Make sure the script is executable, or you will get an error on the $0 $i
You can override the implicit </dev/null for a background process by redirecting stdin yourself, for example:
sh -c 'exec 3<&0; { read x; echo "[$x]"; } <&3 3<&- & exec 3<&-; wait'
Try keeping the original stdin using ./fastcgi_bin 0<&0 &:
#!/bin/bash
# stuff
./fastcgi_bin 0<&0 &
PID=$!./fastcgi_bin 0<&0 &
trap "kill $PID" TERM
# stuff
# test
#sh -c 'sleep 10 & lsof -p ${!}'
#sh -c 'sleep 10 0<&0 & lsof -p ${!}'
You can do that with a coprocess.
Edit: well, coprocesses are background processes that can have stdin and stdout open (because bash prepares fifos for them). But you still need to read/write to those fifos, and the only useful primitive for that is bash's read (possibly with a timeout or a file descriptor); nothing robust enough for a cgi. So on second thought, my advice would be not to do this thing in bash. Doing the extra work in the fastcgi, or in an http wrapper like WSGI, would be more convenient.

Resources