I just written a shell script to control the start and stop of a module. Everything seems normal until I find the stop command result in something unexpected.
I use the command kill -s SIGKILL -- -gpid to kill a group of processes. I use the /bin/sh to run the command like this
/bin/sh -c "kill -s SIGKILL -- -gpid"
which replied the error
/bin/sh: line 0: kill: SIGKILL: invalid signal specification
Then I replaced the /bin/sh with /bin/bash, so the command is
/bin/bash -c "kill -s SIGKILL -- -gpid"
which replied nothing error. so I conclude the explanation that the difference between bash and sh cause the result. However, when I ls the /bin/sh, I found the /bin/sh is a symbolic link to /bin/bash, so the command should be the same.
I found the command syntax kill -s SIGKILL is not in the syntax recommended, kill -s KILL recommended.
so I replaced the SIGKILL with KILL, the command is
/bin/sh -c "kill -s KILL -- -gpid"
which replied nothing error. as described above, anyone could explained this case.
The only truly portable way to write this command is
kill -9 -$gpid
None of the ways to specify a signal name rather than a signal number work on the Unixes that froze their shell utilities in the mid-90s, which is basically all of them except Linux and the open-source BSDs. However, SIGKILL is reliably signal number 9 and has always been so (since V7 if not earlier).
The special argument -- isn't portable either, and is unnecessary in this case.
If you want to be a little more polite about it (sending SIGTERM instead) then use
kill -15 -$gpid
Again, that number is reliable all the way back to V7.
When bash is invoked as sh (e.g. via symlink, as in your case), it uses a sh compatibility mode where most modern features are turned off. I'd bet sh is calling the external binary for kill, and it doesn't recognize SIGKILL, but the bash invocation is using its builtin, and that builtin does.
It's all about bash compatibility. Quick fix to use /bin/bash because sh can't recognize SIGINT or other features.
Related
the -L flag provided in kill does not work in zsh.
When I run the command kill -L Using zsh the result is:
kill: unknown signal: SIGL
kill: type kill -l for a list of signals
Running kill -L Using bash gives the list of signal names as expected.
-L, --table
List signal names in a nice table.
Please help me understand why this inconsistency, and can it be "fixed"?
kill is a shell builtin for both zsh and bash, with different implementations and options on each. The zsh builtin does support the POSIX -l option for listing signals, but not the GNU -L extension.
You can always use /bin/kill to run the freestanding program version if you desire. On OSes with a GNU runtime, that'll also support -L.
The command kill $$ should kill current bash, but it seems that it doesn't work:
$ ps -p $$
PID TTY TIME CMD
18179 pts/4 00:00:00 bash
$ kill $$
$ ps -p $$
PID TTY TIME CMD
18179 pts/4 00:00:00 bash
Why?
I'm not sure why one would like to kill the current shell. Nevertheless...
kill PID would send SIGTERM when no signal is specified. bash ignores SIGTERM and SIGQUIT in the absence of any traps.
You'll achieve the desired effect if you were to say
kill -9 $$
or
kill -SIGKILL $$
Quoting from the manual:
When Bash is interactive, in the absence of any traps, it ignores
SIGTERM (so that ‘kill 0’ does not kill an interactive shell), and
SIGINT is caught and handled (so that the wait builtin is
interruptible). When Bash receives a SIGINT, it breaks out of any
executing loops. In all cases, Bash ignores SIGQUIT.
When you send a process any signal using kill command, the process can choose to handle the signal as per its wish. (There are a few signals, which cannot be handled.)
When you use kill $$, you are actually passing it signal=15 (SIGTERM). It can be handled by a process.
You can google for linux signal example to know HOW it is implemented.
& To answer YOUR question:
If you pass an un-handlable signal, like SIGKILL(9) or SIGSTOP(19), it will respectively kill/stop a running bash process.
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?
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.
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)