Inhibit SIGINT for the process in group - bash

I have a bash script, which start 2 processes:
openocd ...flags... 2>openocd.log &
arm-none-eabi-gdb
When in gdb, interrupting execution with Ctrl+C causes openocd to receive SIGINT as well and, thus it stops. I've tried to trap and reissue SIGINT directly to gdb with:
trap 'kill 2 $!' INT
But apart from requiring root, it does not work anyway:
./dbg.sh: 1: kill: No such process
Are there elegant ways to perform the task?
__
Well, running script with debug options on helped a lot. But still I encounter weird behavior. Here is the content of my script:
#!/bin/sh
set -vx
trap 'killall -s2 arm-none-eabi-gdb-py' 2
openocd -f ...flags... 2>openocd.log & arm-none-eabi-gdb-py
When I run killall -s2 arm-none-eabi-gdb-py from within different tty - it terminates execution of remote target and does not close openocd, but sending SIGINT through Ctrl+C returns:
+ killall -s2 arm-none-eabi-gdb-py
arm-none-eabi-gdb-py: no process found
Seems like trap does not inhibit signals at all... changing to trap 'ps -ef' INT reveals, openocd AND gdb are already down when the trap command executes.

Isn't there a missing '&' in your instruction (it would give that)?
openocd -f ...flags... 2>openocd.log &**&** arm-none-eabi-gdb-py

Related

Prevent sigint from closing OpenOCD when using OpenOCD with GDB

I am trying to write a script to launch OpenOCD in the background, and then launch and instance of GDB connected to my OpenOCD server. This mostly works, except that as soon as I type the interrupt character to halt the target I am debugging my OpenOCD server exits. It appears that OpenOCD is receiving SIGINT.
I have tried to separate OpenOCD from GDB in a number of different ways, at this point my script looks like this:
#! /bin/sh
trap '' SIGINT && nohup sh -c "trap '' SIGINT & openocd -f openocd-jlink.cfg < /dev/null" &
OPENOCD_PID=$!
arm-none-eabi-gdb -ex "set architecture armv6-m" -ex "target extended-remote localhost:2331" obj/main.elf
kill $OPENOCD_PID
I'm pretty sure that this should be very overkill yet OpenOCD still exits as soon as I type the interrupt character in GDB. If I run the same commands directly from my shell (not as part of script) everything works as expected. It works even if I just run openocd -f openocd-jlink.cfg & followed by GDB, no separate shell, nohup or trapping of SIGINT is required.
I'm hoping that someone might have an idea of what I can do in my script to prevent the SIGINT in GDB from reaching OpenOCD. Maybe there is some way to daemonize entirely from shell? I have read a lot of answers here about more generic issues with SIGINT in scripts, so I have a feeling this might be something specific to OpenOCD and GDB.
I have managed to solve this issue by using setsid. The working version of the script is:
#! /bin/sh
setsid openocd -f openocd-jlink.cfg -l /dev/null &
arm-none-eabi-gdbm" -ex "target extended-remote localhost:2331" obj/main.elf
killall openoc
Since I want the script to work on macOS as well as Linux and macOS does not ship with a setsid I ended up using Python to launch OpenOCD, which looks like this:
subprocess.Popen(["openocd", "-f", "openocd-jlink.cfg", "-l", "/dev/null"], preexec_fn = os.setsid)

shell script process termination issue

/bin/sh -version
GNU sh, version 1.14.7(1)
exitfn () {
# Resore signal handling for SIGINT
echo "exiting with trap" >> /tmp/logfile
rm -f /var/run/lockfile.pid # Growl at user,
exit # then exit script.
}
trap 'exitfn; exit' SIGINT SIGQUIT SIGTERM SIGKILL SIGHUP
The above is my function in shell script.
I want to call it in some special conditions...like
when:
"kill -9" fires on pid of this script
"ctrl + z" press while it is running on -x mode
server reboots while script is executing ..
In short, with any kind of interrupt in script, should do some action
eg. rm -f /var/run/lockfile.pid
but my above function is not working properly; it works only for terminal close or "ctrl + c"
Kindly don't suggest to upgrade "bash / sh" version.
SIGKILL cannot be trapped by the trap command, or by any process. It is a guarenteed kill signal, that by it's definition cannot be trapped. Thus upgrading you sh/bash will not work anyway.
You can't trap kill -9 that's the whole point of it, to destroy processes violently that don't respond to other signals (there's a workaround for this, see below).
The server reboot should first deliver a signal to your script which should be caught with what you have.
As to the CTRL-Z, that also gives you a signal, SIGSTOP from memory, so you may want to add that. Though that wouldn't normally be a reason to shut down your process since it may be then put into the background and restarted (with bg).
As to what do do for those situations where your process dies without a catchable signal (like the -9 case), the program should check for that on startup.
By that, I mean lockfile.pid should store the actual PID of the process that created it (by using echo $$ >/var/run/myprog_lockfile.pid for example) and, if you try to start your program, it should check for the existence of that process.
If the process doesn't exist, or it exists but isn't the right one (based on name usually), your new process should delete the pidfile and carry on as if it was never there. If the old process both exists and is the right one, your new process should log a message and exit.

How does trap / kill work in bash on Linux?

My sample file
traptest.sh:
#!/bin/bash
trap 'echo trapped' TERM
while :
do
sleep 1000
done
$ traptest.sh &
[1] 4280
$ kill %1 <-- kill by job number works
Terminated
trapped
$ traptest.sh &
[1] 4280
$ kill 4280 <-- kill by process id doesn't work?
(sound of crickets, process isn't killed)
If I remove the trap statement completely, kill process-id works again?
Running some RHEL 2.6.18-194.11.4.el5 at work. I am really confused by this behaviour, is it right?
kill [pid]
send the TERM signal exclusively to the specified PID.
kill %1
send the TERM signal to the job #1's entire process group, in this case to the script pid + his children (sleep).
I've verified that with strace on sleep process and on script process
Anyway, someone got a similar problem here (but with SIGINT instead of SIGTERM): http://www.vidarholen.net/contents/blog/?p=34.
Quoting the most important sentence:
kill -INT %1 sends the signal to the job’s process group, not the backgrounded pid!
This is expected behavior. Default signal sent by kill is SIGTERM, which you are catching by your trap. Consider this:
#!/bin/bash
# traptest.sh
trap "echo Booh!" SIGINT SIGTERM
echo "pid is $$"
while : # This is the same as "while true".
do
a=1
done
(sleep really creates a new process and the behavior is clearer with my example I guess).
So if you run traptest.sh in one terminal and kill TRAPTEST_PROCESS_ID from another terminal, output in the terminal running traptest will be Booh! as expected (and the process will NOT be killed). If you try sending kill -s HUP TRAPTEST_PROCESS_ID, it will kill the traptest process.
This should clear up the %1 confusion.
Note: the code example is taken from tldp
Davide Berra explained the difference between kill %<jobspec> and kill <PID>, but not how that difference results in what you observed. After all, Unix signal handlers should be called pretty much instantaneously, so why does sending a SIGTERM to the script alone not trigger its trap handler?
The bash man page explains why, in the last paragraph of the SIGNALS section:
If bash is waiting for a command to complete and receives a signal for
which a trap has been set, the trap will not be executed until the
command completes.
So, the signal was delivered immediately, but the handler execution was deferred until sleep exited.
Hence, with kill %<jobspec>:
Both the script and sleep received SIGTERM
bash registered the signal, noticed that a trap was set for it, and queued the handler for future execution
sleep exited immediately
bash noted sleep's exit, and ran the trap handler
whereas with kill <script_PID>:
Only the script received SIGTERM
bash registered the signal, noticed that a trap was set for it, and queued the handler for future execution
sleep exited after 1000 seconds
bash noted sleep's exit, and ran the trap handler
Obviously, you didn't want long enough to see that last bit. :)
If you're interested in the gory details, download the bash source code and look in trap.c, specifically the trap_handler() and run_pending_traps() functions.

How do I trap SIGQUIT properly in a bash script?

I can write shell scripts that trap SIGINT just fine, but I can't seem to trap SIGQUIT.
#!/bin/bash
function die {
echo "Dying on signal $1"
exit 0
}
trap 'die "SIGINT"' SIGINT
trap 'die "SIGQUIT"' SIGQUIT
while true; do
echo "sleeping..."
sleep 5
done
Executing this script and pressing CTRL-C has the desired effect, but pressing CTRL-\ (which, as I understand, should trigger SIGQUIT) does nothing except print ^\ in the terminal. Why?
I have two running theories. The first is that the semantics of SIGINT and SIGQUIT are different such that SIGQUIT only gets sent to the child process sleep, while SIGINT gets sent to both the child process and the parent bash process. If this is the case, where is it documented?
My second theory is that bash not only ignores (i.e., has a no-op handler for) SIGQUIT by default (as the man page suggests), but does not allow it to be trapped at all. This theory overlaps with the first theory, since it could be the case that SIGQUIT is going to both parent and child, but the parent (bash) just can't trap it. If this is the case, is there any way to trap SIGQUIT in a bash script?... perhaps some shopt I can set?
Edit: this is on Ubuntu 10.10 in gnome-terminal 2.32.0 running bash 4.1.5, and yes ^\ is configured to issue SIGQUIT (as reported by stty -a and confirmed by issuing ^\ SIGQUITs to other programs like ping).
UPDATE:
I just discovered that the problem must be due somehow to gnome-terminal. If I run this script from a virtual console (i.e., ctrl-alt-f1 to get out of X), it traps SIGQUIT perfectly fine when I press ^\. Same bash and everything, so the only difference must be the terminal emulator. So now my question becomes: how can I configure gnome-terminal to behave like the virtual console in this respect? I diff'd the outputs of stty -a in the virtual console and in gnome-terminal, and while there are differences, nothing seems immediately relevant (e.g., they both have quit = ^\;).
UPDATE 2:
Another experiment. Simply execute $ sleep 60 in gnome-terminal; press ^\ and the signal goes uncaught. Now execute $ sleep 60 in the virtual console; press ^\ and the signal is caught -- the process prints Quit and exits. But now run $ ping google.com in gnome-terminal and press ^\ -- the signal is caught and handled as expected. So there is something weird about gnome-terminal such that SIGQUIT can be caught by some programs, but not by others, even if those other others do catch it when invoked from the virtual console. Perhaps I should just upgrade my gnome-terminal.
I can only assume that this was some sort of bug in gnome-terminal 2.32.0; I have since upgraded to Ubuntu 11.04, with gnome-terminal 2.32.1 (and bash 4.2.8) and SIGQUIT is now trapped as expected.
I observe the same behavior on Fedora 19 with XFCE: according to ps s, bash in the xfce4-terminal has SIGQUIT ignored, running yes >/dev/null & and then ps s shows that even a subprocess has SIGQUIT ignored. When I (from the same terminal) run ssh localhost, the shell in the ssh session also has SIGQUIT ignored, but yes >/dev/null & has not.
Given the above comment which mentions gnome-terminal, I would guess the bug is in the common part of the two terminals: the vte library. Mine is vte-0.28.2-9.fc19.x86_64.

Run command when bash script is stopped

How can i, in a bash script, execute a command when the user stops the script (with ctrl - c)?
Currently, i have this:
afplay file.mp3
while true:
do osascript -e "set volume 10"
end
But i would like it to execute killall afplay when the user is finished with it, regardless if it is command-c or another keypress.
trap 'killall afplay' EXIT
Use trap.
trap "kill $pid" INT TERM EXIT
Also avoid killall or pkill, since it could kill unrelated processes (for instance, from another instance of your script, or even a different script). Instead, put the player's PID in a variable and kill only that PID.
You need to put a trap statement in your bash script:
trap 'killall afplay' EXIT
Note however that this won't work if the bash process is sent a KILL signal (9) as it's not possible for processes to intercept that signal.

Resources