How to set process group of a shell script - linux

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 &).

Related

PID of all child processes of a command

In a bash script, I want to launch a process in the foreground, then print a list of all the process names and PIDs that were started as children of that process. For example, suppose I have the following scripts, but I can only modify the first one:
A.sh:
#!/bin/bash
B.sh
B.sh:
#!/bin/bash
C.sh
C.sh:
#!/bin/bash
echo "Running C.sh"
Without modifying B.sh, C.sh or the echo command, and without starting any of the child processes in the background, I would like A.sh to print the following:
B.sh 1208
C.sh 1210
echo 1211
Can A.sh fork a process that records this information while the child processes are running in the foreground of A.sh?
Update: In the comments below my answer it turned out that:
I need something that observes the creation of all child processes during a span of time. Given that, filtering to isolate my subtree will not be difficult.
... was the intention behind the question and it was for debugging purposes.
In that case I'd recommend to use strace like this:
strace -f command
-f will track child processes - recursively. Since forking and exec-ing requires system calls, strace will list any child creation plus the pids.
Original answer:
You can use pgrep for that:
run_process &
pid=${!}
pgrep --parent "${pid}"
wait # wait for run_process to finish
Btw, you may want to use the pstree command, it is nice to use:
run_process &
pid=${!}
pstree -p "${pid}"
wait # wait for run_process to finish
Anyhow, you'll need to install pstree.
You can try doing this with A.sh
#!/usr/bin/env bash
./B.sh &
b_PID=$!
./C.sh &
c_PID=$!
echo "B.sh $b_PID"
echo "C.sh $c_PID"
The output will look something like this
B.sh 22802
C.sh 22803
Running C.sh

Prevent process from killing itself using pkill

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

How to run a program and know its PID in Linux?

How to run a program and know its PID in Linux?
If I have several shells running each other, will they all have separate PIDs?
Greg's wiki to the rescue:
$! is the PID of the last backgrounded process.
kill -0 $PID checks whether $PID is still running. Only use this for processes started by the current process or its descendants, otherwise the PID could have been recycled.
wait waits for all children to exit before continuing.
Actually, just read the link - It's all there (and more).
$$ is the PID of the current shell.
And yes, each shell will have its own PID (unless it's some homebrewed shell which doesn't fork to create a "new" shell).
1) There is a variable for that, often $$:
edd#max:~$ echo $$ # shell itself
20559
edd#max:~$ bash -c 'echo $$' # new shell with different PID
19284
edd#max:~$ bash -c 'echo $$' # dito
19382
edd#max:~$
2) Yes they do, the OS / kernel does that for you.
the top command in linux(Ubuntu) shows the memory usage of all running programs in linux with their pid. Kill pid can kill the process.

linux: suspend process at startup

I would like to spawn a process suspended, possibly in the context of another user (e.g. via sudo -u ...), set up some iptables rules for the spawned process, continue running the process, and remove the iptable rules when the process exists.
Is there any standart means (bash, corutils, etc.) that allows me to achieve the above? In particular, how can I spawn a process in a suspended state and get its pid?
Write a wrapper script start-stopped.sh like this:
#!/bin/sh
kill -STOP $$ # suspend myself
# ... until I receive SIGCONT
exec $# # exec argument list
And then call it like:
sudo -u $SOME_USER start-stopped.sh mycommand & # start mycommand in stopped state
MYCOMMAND_PID=$!
setup_iptables $MYCOMMAND_PID # use its PID to setup iptables
sudo -u $SOME_USER kill -CONT $MYCOMMAND_PID # make mycommand continue
wait $MYCOMMAND_PID # wait for its termination
MYCOMMAND_EXIT_STATUS=$?
teardown_iptables # remove iptables rules
report $MYCOMMAND_EXIT_STATUS # report errors, if necessary
All this is overkill, however. You don't need to spawn your process in a suspended state to get the job done. Just make a wrapper script setup_iptables_and_start:
#!/bin/sh
setup_iptables $$ # use my own PID to setup iptables
exec sudo -u $SOME_USER $# # exec'ed command will have same PID
And then call it like
setup_iptables_and_start mycommand || report errors
teardown_iptables
You can write a C wrapper for your program that will do something like this :
fork and print child pid.
In the child, wait for user to press Enter. This puts the child in sleep and you can add the rules with the pid.
Once rules are added, user presses enter. The child runs your original program, either using exec or system.
Will this work?
Edit:
Actually you can do above procedure with a shell script. Try following bash script:
#!/bin/bash
echo "Pid is $$"
echo -n "Press Enter.."
read
exec $#
You can run this as /bin/bash ./run.sh <your command>
One way to do it is to enlist gdb to pause the program at the start of its main function (using the command "break main"). This will guarantee that the process is suspended fast enough (although some initialisation routines can run before main, they probably won't do anything relevant). However, for this you will need debugging information for the program you want to start suspended.
I suggest you try this manually first, see how it works, and then work out how to script what you've done.
Alternatively, it may be possible to constrain the process (if indeed that is what you're trying to do!) without using iptables, using SELinux or a ptrace-based tool like sydbox instead.
I suppose you could write a util yourself that forks, and wherein the child of the fork suspends itself just before doing an exec. Otherwise, consider using an LD_PRELOAD lib to do your 'custom' business.
If you care about making that secure, you should probably look at bigger guns (with chroot, perhaps paravirtualization, user mode linux etc. etc);
Last tip: if you don't mind doing some more coding, the ptrace interface should allow you to do what you describe (since it is used to implement debuggers with)
You probably need the PID of a program you're starting, before that program actually starts running. You could do it like this.
Start a plain script
Force the script to wait
You can probably use suspend which is a bash builitin but in the worst case you can make it stop itself with a signal
Use the PID of the bash process in every way you want
Restart the stopped bash process (SIGCONT) and do an exec - another builtin - starting your real process (it will inherit the PID)

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