Killing a terminal-attached process that doesn't respond to SIGINT, SIGQUIT - linux

Sometimes both Ctr-C (SIGINT) and Ctrl-\ (SIGQUIT) are too weak. Is there a way to do an more aggressive kill (e.g. kill -9) on the currently-attached process using a quick keyboard shortcut?

If you are a zsh user, you can send SIGTERM with this in .zshrc
function terminate-current-job() { kill -s TERM %+ ; }
zle -N terminate-current-job terminate-current-job
bindkey "^T" terminate-current-job
That binds CTRLT to the previously defined widget/function.

If you are having problems with a specific command not responding to CTRL-C (because it ignores SIGINT, or because it asked the terminal driver to no longer recognise it as an interrupt character) , you can try wrapping it in rlwrap:
rlwrap -a -I <command>
rlwrap will catch the SIGINT sent by the terminal driver when you press CTRL-C and send a SIGTERM to <command> instead.
Of course, <command> may catch, or even ignore SIGTERM as well, but many commands that ignore SIGINT will respond to SIGTERM - while still being able to clean up before they terminate, in contrast to what happens when you use SIGKILL (kill -9)

Like proposed in the other answers, you can try to kill the process by catching some other signal. This can be also done with the linux bash built in trap command
that is used to execute a command when the shell receives any signal
To KILL your executable if SIGINT (CTRL-C) is captured, you need to start it like this:
yourexecutable & pid=$! ; trap 'echo KILL ; kill -9 $pid' INT ; echo WAIT $pid ; wait $pid ; echo DONE
Note that the echos are just for debugging purposes, they can simply be removed if you don't need them.

Related

How to catch SIGINT within a Bash subshell

If I run a command, such as grep, at the command line and hit ^C, the command is properly killed (with SIGINT I think). And if I run the grep in background and then run a kill SIGINT on its PID, it similarly gets terminated. But if I'm inside a script and run grep in background from the script, get its PID and then use 'kill -s SIGINT $PID', grep does not get killed. Why? If I use SIGTERM, instead of SIGINT, the kill does work.
#!/bin/bash
grep -rqa shazam /usr &
PID=$!
kill -s SIGINT $PID
Even if I put the grep in a subprocess, preceded by a SIGINT handler (in the subprocess), and hit the subprocess with SIGINT, the handler is not invoked.
#!/bin/bash
( trap 'echo "caught signal"' SIGINT; grep -rqa shazam /usr ) &
PID=$!
kill -s SIGINT $PID
The trap handler is invoked if I use SIGTERM, instead of SIGINT, but does not interrupt grep. If I add '/bin/kill -s SIGTERM 0' to the trap handler, there is an indication that the grep process gets terminated, but grep has already completed its work by then. I realize that Bash may have different default behaviors for the different signals, but I don't understand why my call to kill SIGINT is different than a ^C, why the trap call works for SIGTERM, but not for SIGINT, nor why SIGTERM isn't handled by the subprocess immediately.
Well, with further digging, I figured out 2 of my 3 questions. When I backgrounded grep within the script, the shell told it to ignore SIGINT. And Bash says it will wait to handle the signal until the subcommand is complete in some situations (which I don't fully follow at the moment), but the signal is handled immediately if hit the grep process directly with pkill.
"Actually bash will disable SIGINT (and SIGQUIT) on background processes and they can't be enabled" Background process and signals How SIGINT works
"Further background jobs are not supposed to be tied to the shell that started them. If you exit a shell, they will continue running. As such they shouldn't be interrupted by SIGINT, not by default. When job control is enabled, that is fulfilled automatically, since background jobs are running in separate process groups. When job control is disabled (generally in non-interactive shells), bash makes the asynchronous commands ignore SIGINT." Independent Program
Reason why SIGTERM works

make redis server ignore Ctrl+C when launched from shell script

I want to use shell script to launch Redis server and then monitor a log file:
#!/bin/bash
/path/to/redis/src/redis-server &
tail -f /path/to/log/logfile.log
If I run this script and press Ctrl+C from the terminal, the tail -f terminated, which is what I want, however the Redis also detected SIGINT and exited.
I tried to write the script like this:
#!/bin/bash
trap '' INT TSTP
~/redis/src/redis-server &
tail -f ./script1
This time things go even worse, the tail -f refused to terminate while Redis still detected SIGINT and exited.
It seems that there is some problems specific to Redis regarding ignoring signals.
My goal is to make tail -f responds to Ctrl+C while making Redis ignore this signal.
Please anyone tell me whether this can be achieved and if so, give me some advice?
redis-server catches SIGINT (Ctrl+C), even if SIGINT was being ignored. This is an unusual choice; most software will check and won't catch SIGINT if it's already being ignored.
When it receives SIGINT, it saves the database and shuts down.
If you start it as a service, it won't be associated with any terminal at all, and won't see any Ctrl+C you type.
If you start it as a background job in an interactive shell:
$ /path/to/redis/src/redis-server &
your shell will put it into a process group that is different from the terminal's process group, and typing Ctrl+C won't affect it. (If you bring it to the foreground with fg, Ctrl+C will send SIGINT to the program).
But, when you run a script like this:
#!/bin/bash
/path/to/redis/src/redis-server &
tail -f /path/to/log/logfile.log
the shell that runs the script will be non-interactive, and any program that it starts in the background (with &) will be in the same process group as the shell. So if you run that shell script in the foreground, typing Ctrl+C will send SIGINT to the shell, to redis-server, and to tail.
To prevent Ctrl+C from sending SIGINT to redis-server in a case like this, you need to either put redis-server in its own process group or disassociate it from your terminal. You can do this with setsid, which does both:
#!/bin/bash
setsid /path/to/redis/src/redis-server &
tail -f /path/to/log/logfile.log

How to launch a shell command on an already opened specific terminal?

I have a script ./runStart.sh that creates a new xterm and in this xterm launch the script run.log command.
Then, to finish the process I launch a script ./runStop.sh that kills the xterm and do other stuff behind.
The problem I have is that as I kill the process while running, there is no exit call, so the script command does not save the data in the actual file.
Do you know how I could launch a command in the second script ./runStop.sh that makes this exit call instead of the kill <pid> command ?
You can use the bash builtin trap to catch a signal (except KILL and STOP) and do the required cleanup.
For example, here i am catching the pseudo signal EXIT that would cover any signal that would make the shell to exit:
xterm -e '/bin/bash' -c 'trap "echo ok >/tmp/foobar" EXIT; sleep 5'
sleep 5 is my main task and echo ok >/tmp/foobar is cleanup task upon exiting.
Alternately if are sure about the signal to be sent/received e.g. HUP, INT, you can trap those directly.

Shell Script get CTRL+Z with Trap

I am trying to get the SIGSTOP CTRL+Z signal in my script's trap.
When my script is executing, if I temporarily suspend from execution, send a SIGSTOP signalCTRL+Z, it needs to remove the files I create in it and to kill the execution.
I don't understand why the following script doesn't work. But, more important, what is the correct way to do it?
#!/bin/bash
DIR="temp_folder"
trap "rm -r $DIR; kill -SIGINT $$" SIGSTP
if [ -d $DIR ]
then
rm -r $DIR
else
mkdir $DIR
fi
sleep 5
EDIT:
SIGSTOP cannot be trapped, however SIGTSTP can be trapped, and from what I understood after searching on the internet and the answer below it's the correct to trap when sending signal with CTRL+Z. However, when I press CTRL+Z while running the script it will get stuck, meaning that the script will be endlessly execute no matter what signals I send afterwards.
The problem here is you are trying to suspend a process that is already sleeping.
It is also good practice to use DIR=$(mktemp -d) in shell scripts to create temp directories.
CTRL-C is signal (2) / CTRL-Z (20):
catch_exits() {
printf "\n$(basename $0): exiting\n" 1>&2
rm -rf $DIR
exit 1
}
trap catch_exits 1 2 3 15 20
DIR="$(mktemp -d)"
read -p "not sleeping" test
if you send a function to the background (such as for a cursor spinner) - then you need to disable CTRL-Z while the long process is running with:
trap "" SIGTSTP
There are two signals you can't trap*, SIGKILL and SIGSTOP. Use another signal.
*: without modifying the kernel
IEEE standard:
Setting a trap for SIGKILL or SIGSTOP produces undefined results.

Kill a process and wait for the process to exit

When I start my tcp server from my bash script, I need to kill the previous instance (which may still be listening to the same port) right before the current instance starts listening.
I could use something like pkill <previous_pid>. If I understand it correctly, this just sends SIGTERM to the target pid. When pkill returns, the target process may still be alive. Is there a way to let pkill wait until it exits?
No. What you can do is write a loop with kill -0 $PID. If this call fails ($? -ne 0), the process has terminated (after your normal kill):
while kill -0 $PID; do
sleep 1
done
(kudos to qbolec for the code)
Related:
What does `kill -0 $pid` in a shell script do?
Use wait (bash builtin) to wait for the process to finish:
pkill <previous_pid>
wait <previous_pid>

Resources