Rate this page

Flattr this

Prevent a process from terminating when writing to a broken pipe

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:

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

Tags: c | posix | signal