Prevent a process from terminating when writing to a broken pipe
Content |
Tested on |
Debian (Etch, Lenny, Squeeze) |
Ubuntu (Hardy, Intrepid, Jaunty, Karmic, Lucid, Maverick, Natty, Oneiric, Precise, Quantal) |
Objective
To prevent a process from being terminated by SIGPIPE
when writing to a broken pipe or disconnected socket
Background
Writing to a disconnected pipe, FIFO or socket causes the signal SIGPIPE
to be raised. The default behaviour is to terminate the process responsible. The reason for this happening is to prevent processes from lingering when their output is no longer needed. For example, consider the pipeline:
ls -lR | less
If the user exits from less
before ls
has finished executing then it will only be able to output a small amount of further data (typically a few tens of kilobytes) before blocking due to the pipe buffer becoming full. Even if this did not happen, it would be pointless for ls
to continue generating output with no possibility of it being used.
In the absence of SIGPIPE
would be able to detect this condition (and terminate if required) by testing for the error EPIPE
. What SIGPIPE
does is to make termination the default behaviour, thereby providing coverage for programs that were not written for POSIX or which mistakenly assume that writing to standard output will always succeed.
Termination can be rather less helpful in other circumstances, for example:
- interactive programs,
- daemons, and
- programs which need to clean up before exiting.
The onus is then on the process to either arrange for a different response to SIGPIPE
or to prevent it from being raised in the first place.
Scenarios
Invoking a CGI script from an HTTP server
Suppose you are writing an HTTP server with the ability to execute CGI scripts. In some circumstances, for example when handling a POST
request, it is necessary to pass data to the script via its standard input. A convenient way of achieving this is to feed the standard input of the CGI process from a pipe.
If the CGI process dies before the HTTP server has finished writing to the pipe then the next write operation will cause SIGPIPE
to be raised. This may or may not terminate the HTTP server as a whole, depending on whether a separate child processes is spawned for each connection, but it would prevent an error response from being sent back to the end user.
Sending an HTTP request from a web browser
Similarly, if a web server were to close an inbound HTTP connection unexpectedly before the browser had finished sending the HTTP request then the browser would receive a SIGPIPE
. This prevents the browser from reporting the error to the user, and may cause the browser as a whole to terminate.
Method
Since it is the default SIGPIPE
action which causes the process to terminate, changing its disposition to SIG_IGN
will prevent that from happening:
struct sigaction sa; sa.sa_handler = SIG_IGN; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; if (sigaction(SIGPIPE, &sa, 0) == -1) { perror(0); exit(1); }
This can be implemented using the signal
function if you prefer:
if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) { perror(0); exit(1); }
(signal
is deprecated by POSIX but provided by standard C, whereas sigaction
is specific to POSIX. The behaviour of signal
is portable when the action is SIG_DFL
or SIG_IGN
, but not when a handler function is specified. SIGPIPE
is provided by POSIX but not by standard C.)
Although the program will probably want to take some action in response to the broken pipe, it is usually more convenient to do this in response to EPIPE
rather than SIGPIPE
due to restrictions on what can be executed within a signal handler.
Alternatives
Using MSG_NOSIGNAL
When writing to a socket using the send
, sendto
or sendmsg
function there is a flag which can be set, MSG_NOSIGNAL
, which prevents SIGPIPE
from being raised. It does not suppress EPIPE
, so broken connections can still be detected by the program.
One advantage of this method is that it does not change the signal disposition for the process as a whole, making it attractive for use in libraries. Its main drawback is its limitation to the functions listed above, so is only available when writing to sockets (as opposed to file descriptors generally) and not when using write
or writev
.
Using SO_NOSIGPIPE
Some operating systems provide a socket option SO_NOSIGPIPE
which suppresses SIGPIPE
for all subsequent operations on a given socket:
int on = 1; if (setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &on, sizeof(on)) == -1) { perror("setsockopt"); exit(1); }
This option is unfortunately not provided by POSIX generally, or by Linux in particular, so you should certainly not rely on its availability when writing code that is intended to be at all portable. It is available in some environments where MSG_NOSIGNAL
is not, making it useful as a fallback option in those cases.
Blocking SIGPIPE using pthread_sigmask then accepting it using sigtimedwait
SIGPIPE
can be temporarily blocked by adding it to the signal mask using pthread_sigmask
. While blocked it can then be accepted using a function from the sigwait
family, which prevents it from being delivered. Using this technique a library can temporarily suppress SIGPIPE
without altering the signal disposition for the process as a whole.
See Ignore SIGPIPE without affecting other threads in a process for further details of this method.
Further reading
- <signal.h>, Base Specifications, Issue 7, The Open Group, 2008