I've read in several places (including SO) that -e is considered "poor form" and is unreliable for exiting a script on any error. A better way to handle errors seems to be using trap, as such:
trap "echo there was an error; exit 1;" ERR
I can't seem to locate in the man pages what signal ERR is actually? I'm assuming it's SIGQUIT but I can't find for sure.
man 7 signal
only has the normal signals you would expect SIGTERM SIGQUIT SIGINT, etc.
man trap
has references to ERR signal, but doesn't seem to define it.
ex: "A trap on ERR, if set, is executed before the shell exits."
man bash
is similar to man trap in that is makes references to ERR but doesn't define it from what I've seen.
What is the actual signal behind the shortcut ERR? (in normal signals as seen in man 7 signal).
I'd prefer to trap the actual signal name instead of the shorthand version, although I realize they would produce the same results (catching any error from a command in a script then throwing to the handler).
There is no signal corresponding to the trap signal specification ERR.
ERR is one of the signal specifications implemented internally by bash. [Note 1] If trap ERR is enabled, then bash will invoke the corresponding handler in exactly the same cases as it would have exited had set -e been enabled. (Consequently, it is no more "reliable" then set -e but it is a lot more flexible.)
Other special trap names which do not correspond to any signal are EXIT, DEBUG and RETURN.
help trap will explain the meaning of these signal specifications.
Notes:
Actually, all of the signal specifications are implemented by bash, but most of them are implemented by bash trapping the signal and then executing the signal handler. The special ones just involve executing the signal handler.
Related
I've got a piece of classic problem, but can't figure out how to deal with it. There is a bash process which executes children, and I want to send some signal to it (SIGHUP), handle it there and propagate this signal to one of the children (another_long_running_process for example). Here is snippet:
#!/bin/bash
long_running_process &
another_long_running_process &
pid=$!
trap 'kill -1 $pid' HUP
wait $pid
Ok, now I setup trap, create handler to send signal to particular pid, but then find out that my script just exits after first SIGHUP receiving and handling. The problem that bash returns immediately from wait built-in:
When Bash receives a signal for which a trap has been set while waiting for a command to complete, the trap will not be executed until the command completes. When Bash is waiting for an asynchronous command via the wait built-in, the reception of a signal for which a trap has been set will cause the wait built-in to return immediately with an exit status greater than 128, immediately after which the trap is executed.
And yes, my script just exits after first time I send SIGHUP, by design. But I need to keep it running.
And I can't figure out how to wait children processes, and propagate one of them (ok, even all of them) SIGHUP signals multiple times while they're running. Is this achievable in such problem definition? I think with parent pid I can iterate over children and find required process and then send signal particularly there, but it looks a bit overengineering, or not?
Ok, finally I fix my problem with following approach: I setup ignore signal handler in bash-script and make it leader of process group. Then redefine SIGHUP handler in another_long_running_process and then send signal to process group. So bash-script and long_running_process ignore that signal, and another_long_running_process catch signal and handle it.
My requirement is that whenever a program terminates in any way other than its normal completion [i.e. exit() system call at the end], I need to handle it (say, hook a print "Hello" statement) before it actually terminates.
For example, when I hit Ctrl+C while running a program, it should print Hello and continue the way a SIGINT must have been handled.
If I use my custom signal handler function (having a print statement logic) in my source code, it would alter the default behavior i.e. how SIGINT would have ideally terminated.
1) Can anyone help me achieve both of this? What other signals I need to handle explicitly (maybe SIGTERM ?) which can cause termination of a running process?
2) How can I generate/test them ?(say, SIGINT can be generated by hitting Ctrl + C in linux)
there are several signals supported in unix/linux.
Except SIGKILL, SIGSTOP, all others can be interpreted and handled.
process of registering the handler to a particular signumber should be same.
We can use kill command to send signals to other process.
For example: it sends TERM signal to processid 1234
kill -s TERM 1234
The sigaction(2) man page has some useful info. For one thing, every signal but SIGKILL and SIGSTOP can be caught.
In your signal handler, you have two options:
puts(3) and then manually do something (exit() or raise(SIGSTOP) or something).
puts(3) and then try to get the default signal behaviour by setting the handler back to SIG_DFL, and sending the signal to yourself with raise(3). I'm not sure whether you can just sigaction() to restore your signal handler right after raise() from inside that signal handler, and whether that would be portable even if it happens to work on Linux.
List all signals with kill -l
Send a signal with kill -INT 1234, or in the shell you started a background process from: kill -INT %1. Or to avoid copy/pasting a PID every time: pkill -INT process_name (pkill and pgrep are related.)
!/usr/bin/env perl
use POSIX;
my $sig_set = POSIX::SigSet->new(POSIX::SIGINT);
my $sig_act = POSIX::SigAction->new(sub { print "called\n"; exit 0 },$sig_set);
POSIX::sigaction(SIGINT,$sig_act);
sleep(15);
Why do I need to use POSIX::SigSet if I already tell POSIX::sigaction that I want SIGINT?
Basically I'm trying to respond with my coderef to each of the signal I add to SigSet, looking at POSIX::sigaction signature, it must accept a singal as the first parametner, which doesnt seems reasonable to be if I already tell POSIX::SigAction about my POSIX::SigSet.
I'm sure I am missing something here.
thanks,
The answer to your question
The POSIX::SigSet specifies additional signals to mask off (to ignore) during the execution of your signal handler sub. It corresponds to the sa_mask member of the underlying struct passed to the C version of sigaction.
Now, SIGINT (well, the first argument to sigaction) will be masked off by default, unless you explicitly request otherwise via the SA_NODEFER.
A better approach?
However, if all you want to do it to register a signal handler whose execution won't be interrupted by the signal for which it was registered (e.g., don't allow SIGINT during your SIGINT handler), you can skip the POSIX module entirely:
$SIG{INT} = sub { print "called\n"; exit 0; }; # Won't be interrupted by SIGINT
Where it can, Perl's signal dispatching emulates the traditional UNIX semantics of blocking a signal during its handler execution. (And on Linux, it certainly can. sigprocmask() is called before executing the handler, and then a scope-guard function is registered to re-allow that signal at the end of the user-supplied sub.)
I have a shell script background process that runs "nohupped". This process shall receive signals in a trap, but when playing around with some code, I noticed that some signals are ignored if the interval between them is too small. The execution of the trap function takes too much time and therefore the subsequent signal goes
unserved. Unfortunately, the trap command doesn't have some kind of signal queue, that's why I am asking: What is the best way to solve this problem?
A simple example:
function receive_signal()
{
local TIMESTAMP=`date '+%Y%m%d%H%M%S'`
echo "some text" > $TIMESTAMP
}
trap receive_signal USR1
while :
do
sleep 5
done
The easiest change, without redesigning your approach, is to use realtime signals, which queue.
This is not portable. Realtime signals themselves are an optional extension, and shell and utility support for them are not required by the extension in any case. However, it so happens that the relevant GNU utilities on Linux — bash(1) and kill(1) — do support realtime signals in a commonsense way. So, you can say:
trap sahandler RTMIN+1
and, elsewhere:
$ kill RTMIN+1 $pid_of_my_process
Did you consider multiple one line trap statements? One for each signal you want to block or process?
trap dosomething 15
trap segfault SEGV
Also you want to have the least possible code in a signal handler for the reason you just encountered.
Edit - for bash you can code your own error handling / signal handling in C, or anything else using modern signal semantics if you want with dynamically loadable modules:
http://cfajohnson.com/shell/articles/dynamically-loadable/
When you pipe two process and kill the one at the "output" of the pipe, the first process used to receive the "Broken Pipe" signal, which usually terminated it aswell. E.g. running
$> do_something_intensive | less
and then exiting less used to return you immediately to a responsive shell, on a SuSE8 or former releases.
when i'm trying that today, do_something_intensive is obviously still running until i kill it manually. It seems that something has changed (glib ? shell ?) that makes program ignore "broken pipes" ...
Anyone of you has hints on this ? how to restore the former behaviour ? why it has been changed (or why it always existed multiple semantics) ?
edit : further tests (using strace) reveal that "SIGPIPE" is generated, but that the program is not interrupted. A simple
#include <stdio.h>
int main()
{
while(1) printf("dumb test\n");
exit(0);
}
will go on with an endless
--- SIGPIPE (Broken pipe) # 0 (0) ---
write(1, "dumb test\ndumb test\ndumb test\ndu"..., 1024) = -1 EPIPE (Broken pipe)
when less is killed. I could for sure program a signal handler in my program and ensure it terminates, but i'm more looking for some environment variable or a shell option that would force programs to terminate on SIGPIPE
edit again: it seems to be a tcsh-specific issue (bash handles it properly) and terminal-dependent (Eterm 0.9.4)
Well, if there is an attempt to write to a pipe after the reader has gone away, a SIGPIPE signal gets generated. The application has the ability to catch this signal, but if it doesn't, the process is killed.
The SIGPIPE won't be generated until the calling process attempts to write, so if there's no more output, it won't be generated.
Has "do something intensive" changed at all?
As Daniel has mentioned SIGPIPE is not a magic "your pipe went away" signal but rather a "nice try, you can no longer read/write that pipe" signal.
If you have control of "do something intensive" you could change it to write out some "progress indicator" output as it spins. This would raise the SIGPIPE in a timely fashion.
Thanks for your advices, the solution is getting closer...
According to the manpage of tcsh, "non-login shells inherit the terminate behavior from their parents. Other signals have the values which the shell inherited from its parent."
Which suggest my terminal is actually the root of the problem ... if it ignored SIGPIPE, the shell itself will ignore SIGPIPE as well ...
edit: i have the definitive confirmation that the problem only arise with Eterm+tcsh and found a suspiciously missing signal(SIGPIPE,SIG_DFL) in Eterm source code. I think that close the case.