296

I have a small server program that accepts connections on a TCP or local UNIX socket, reads a simple command and (depending on the command) sends a reply.

The problem is that the client may have no interest in the answer and sometimes exits early. So writing to that socket will cause a SIGPIPE and make my server crash.

What's the best practice to prevent the crash here? Is there a way to check if the other side of the line is still reading? (select() doesn't seem to work here as it always says the socket is writable). Or should I just catch the SIGPIPE with a handler and ignore it?

Olivia Stork
  • 4,660
  • 5
  • 27
  • 40
jkramer
  • 15,440
  • 5
  • 47
  • 48

10 Answers10

301

You generally want to ignore the SIGPIPE and handle the error directly in your code. This is because signal handlers in C have many restrictions on what they can do.

The most portable way to do this is to set the SIGPIPE handler to SIG_IGN. This will prevent any socket or pipe write from causing a SIGPIPE signal.

To ignore the SIGPIPE signal, use the following code:

signal(SIGPIPE, SIG_IGN);

If you're using the send() call, another option is to use the MSG_NOSIGNAL option, which will turn the SIGPIPE behavior off on a per call basis. Note that not all operating systems support the MSG_NOSIGNAL flag.

Lastly, you may also want to consider the SO_SIGNOPIPE socket flag that can be set with setsockopt() on some operating systems. This will prevent SIGPIPE from being caused by writes just to the sockets it is set on.

dvorak
  • 31,281
  • 4
  • 27
  • 29
  • 78
    To make this explicit: signal(SIGPIPE, SIG_IGN); – jhclark Mar 04 '12 at 18:38
  • 5
    This may be necessary if you are getting an exit return code of 141 (128 + 13:SIGPIPE). SIG_IGN is the ignore signal handler. – velcrow Nov 08 '12 at 20:51
  • 6
    For sockets, it's really easier to use send() with MSG_NOSIGNAL, as @talash said. – Pawel Veselov May 14 '14 at 22:25
  • 5
    What exactly happens if my program writes to a broken pipe (a socket in my case)? SIG_IGN makes the program ignore the SIG_PIPE, but then does that result in send() doing something undesirable? – sudo May 22 '14 at 22:16
  • @velcrow where did you find these codes? (128 + 13)? – netdigger Nov 28 '14 at 08:41
  • 1
    NVM, found it: "The value 141 above is actually 128+13, where 13 is the numeric value of SIGPIPE and 128 is the value the shell adds to signal numbers to indicate that a process died due to a signal rather than exiting "normally". SIGPIPE might be different from 13 on other systems.", here: http://www.greenend.org.uk/rjk/tech/shellmistakes.html – netdigger Nov 28 '14 at 08:58
  • 7
    Worth mentioning, since I found it once, then forgot it later and got confused, then discovered it a second time. Debuggers (I know gdb does) override your signal handling, so ignored signals are not ignored! Test your code outside a debugger to ensure the SIGPIPE no longer occurs. http://stackoverflow.com/questions/6821469/how-to-prevent-sigpipe-or-prevent-the-server-from-ending – Jetski S-type Jan 19 '16 at 06:08
  • The man page for signal recommends using sigaction instead, should this answer be updated? – Ryder Bergerud Feb 09 '19 at 20:40
  • It is SO_NOSIGPIPE, not SO_SIGNOPIPE. – Johannes Overmann May 19 '22 at 08:19
165

Another method is to change the socket so it never generates SIGPIPE on write(). This is more convenient in libraries, where you might not want a global signal handler for SIGPIPE.

On most BSD-based (MacOS, FreeBSD...) systems, (assuming you are using C/C++), you can do this with:

int set = 1;
setsockopt(sd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&set, sizeof(int));

With this in effect, instead of the SIGPIPE signal being generated, EPIPE will be returned.

srdjan.veljkovic
  • 2,468
  • 16
  • 24
user55807
  • 1,659
  • 1
  • 10
  • 3
121

I'm super late to the party, but SO_NOSIGPIPE isn't portable, and might not work on your system (it seems to be a BSD thing).

A nice alternative if you're on, say, a Linux system without SO_NOSIGPIPE would be to set the MSG_NOSIGNAL flag on your send(2) call.

Example replacing write(...) by send(...,MSG_NOSIGNAL) (see nobar's comment)

char buf[888];
//write( sockfd, buf, sizeof(buf) );
send(    sockfd, buf, sizeof(buf), MSG_NOSIGNAL );
Community
  • 1
  • 1
sklnd
  • 4,471
  • 3
  • 21
  • 8
  • 8
    In other words, use send(...,MSG_NOSIGNAL) as a replacement for write() and you won't get SIGPIPE. This should work well for sockets (on supported platforms), but send() seems to be limited to use with sockets (not pipes), so this isn't a general solution to the SIGPIPE problem. – Brent Bradburn Jan 25 '11 at 17:19
  • @Ray2k Who on earth is still developing applications for Linux < 2.2? That's actually a semi-serious question; most distros ship with at least 2.6. – Parthian Shot Jun 12 '14 at 17:54
  • 3
    @Parthian Shot: You should rethink your answer. Maintenance of old embedded systems is a valid reason to care for older Linux versions. – kirsche40 Aug 20 '14 at 12:27
  • 1
    @kirsche40 I'm not the OP- I was just curious. – Parthian Shot Sep 03 '14 at 23:01
  • @sklnd this worked for me perfectly. I'm using a Qt `QTcpSocket` object to wrap sockets usage, I had to replace `write` method call by a OS `send` (using `socketDescriptor` method). Anyone knows a cleaner option to set this option in a `QTcpSocket` class? – Emilio González Montaña Feb 05 '18 at 09:05
31

In this post I described possible solution for Solaris case when neither SO_NOSIGPIPE nor MSG_NOSIGNAL is available.

Instead, we have to temporarily suppress SIGPIPE in the current thread that executes library code. Here's how to do this: to suppress SIGPIPE we first check if it is pending. If it does, this means that it is blocked in this thread, and we have to do nothing. If the library generates additional SIGPIPE, it will be merged with the pending one, and that's a no-op. If SIGPIPE is not pending then we block it in this thread, and also check whether it was already blocked. Then we are free to execute our writes. When we are to restore SIGPIPE to its original state, we do the following: if SIGPIPE was pending originally, we do nothing. Otherwise we check if it is pending now. If it does (which means that out actions have generated one or more SIGPIPEs), then we wait for it in this thread, thus clearing its pending status (to do this we use sigtimedwait() with zero timeout; this is to avoid blocking in a scenario where malicious user sent SIGPIPE manually to a whole process: in this case we will see it pending, but other thread may handle it before we had a change to wait for it). After clearing pending status we unblock SIGPIPE in this thread, but only if it wasn't blocked originally.

Example code at https://github.com/kroki/XProbes/blob/1447f3d93b6dbf273919af15e59f35cca58fcc23/src/libxprobes.c#L156

hroptatyr
  • 4,702
  • 1
  • 35
  • 38
kroki
  • 435
  • 4
  • 3
22

Handle SIGPIPE Locally

It's usually best to handle the error locally rather than in a global signal event handler since locally you will have more context as to what's going on and what recourse to take.

I have a communication layer in one of my apps that allows my app to communicate with an external accessory. When a write error occurs I throw and exception in the communication layer and let it bubble up to a try catch block to handle it there.

Code:

The code to ignore a SIGPIPE signal so that you can handle it locally is:

// We expect write failures to occur but we want to handle them where 
// the error occurs rather than in a SIGPIPE handler.
signal(SIGPIPE, SIG_IGN);

This code will prevent the SIGPIPE signal from being raised, but you will get a read / write error when trying to use the socket, so you will need to check for that.

Sam
  • 26,946
  • 12
  • 75
  • 101
  • should this call be made after connecting a socket or before that ? where is it best to place. Thanks – Ahmed Feb 14 '13 at 02:14
  • @Ahmed I personally put it in [applicationDidFinishLaunching:](http://developer.apple.com/library/ios/documentation/uikit/reference/UIApplicationDelegate_Protocol/Reference/Reference.html#//apple_ref/doc/uid/TP40006786-CH3-SW29). The main thing is that it should be before interacting with a socket connection. – Sam Feb 14 '13 at 04:53
  • but you will get a read / write error when trying to use the socket, so you will need to check for that. - May I ask how this is possible? signal(SIGPIPE, SIG_IGN) works for me but under debug mode it returns an error is it possible to also ignore that error? – n0minal Oct 03 '13 at 01:56
  • @Dreyfus15 I believe I just wrapped calls using the socket in a try / catch block to handle it locally. It's been awhile since I've seen this. – Sam Oct 03 '13 at 14:36
15

You cannot prevent the process on the far end of a pipe from exiting, and if it exits before you've finished writing, you will get a SIGPIPE signal. If you SIG_IGN the signal, then your write will return with an error - and you need to note and react to that error. Just catching and ignoring the signal in a handler is not a good idea -- you must note that the pipe is now defunct and modify the program's behaviour so it does not write to the pipe again (because the signal will be generated again, and ignored again, and you'll try again, and the whole process could go on for a long time and waste a lot of CPU power).

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
5

Or should I just catch the SIGPIPE with a handler and ignore it?

I believe that is right on. You want to know when the other end has closed their descriptor and that's what SIGPIPE tells you.

Sam

Sam Reynolds
  • 181
  • 3
  • 9
4

What's the best practice to prevent the crash here?

Either disable sigpipes as per everybody, or catch and ignore the error.

Is there a way to check if the other side of the line is still reading?

Yes, use select().

select() doesn't seem to work here as it always says the socket is writable.

You need to select on the read bits. You can probably ignore the write bits.

When the far end closes its file handle, select will tell you that there is data ready to read. When you go and read that, you will get back 0 bytes, which is how the OS tells you that the file handle has been closed.

The only time you can't ignore the write bits is if you are sending large volumes, and there is a risk of the other end getting backlogged, which can cause your buffers to fill. If that happens, then trying to write to the file handle can cause your program/thread to block or fail. Testing select before writing will protect you from that, but it doesn't guarantee that the other end is healthy or that your data is going to arrive.

Note that you can get a sigpipe from close(), as well as when you write.

Close flushes any buffered data. If the other end has already been closed, then close will fail, and you will receive a sigpipe.

If you are using buffered TCPIP, then a successful write just means your data has been queued to send, it doesn't mean it has been sent. Until you successfully call close, you don't know that your data has been sent.

Sigpipe tells you something has gone wrong, it doesn't tell you what, or what you should do about it.

Ben Aveling
  • 842
  • 7
  • 13
  • *Sigpipe tells you something has gone wrong, it doesn't tell you what, or what you should do about it.* Exactly. Virtually the only purpose of SIGPIPE is to tear down piped command line utilities when the next stage no longer needs the input. If your program is doing networking, you should usually just block or ignore SIGPIPE program-wide. – DepressedDaniel Mar 10 '17 at 22:18
  • Precisely. SIGPIPE is intended for situations like "find XXX | head". Once head has matched its 10 lines, there's no point continuing with the find. So head exits, and the next time find tries to talk to it, it gets a sigpipe and knows that it too can exit. – Ben Aveling Mar 13 '17 at 05:48
  • 1
    Really, the only purpose of SIGPIPE is to allow programs to be sloppy and not check for errors on writes! – William Pursell May 21 '18 at 23:48
4

Under a modern POSIX system (i.e. Linux), you can use the sigprocmask() function.

#include <signal.h>

void block_signal(int signal_to_block /* i.e. SIGPIPE */ )
{
    sigset_t set;
    sigset_t old_state;

    // get the current state
    //
    sigprocmask(SIG_BLOCK, NULL, &old_state);

    // add signal_to_block to that existing state
    //
    set = old_state;
    sigaddset(&set, signal_to_block);

    // block that signal also
    //
    sigprocmask(SIG_BLOCK, &set, NULL);

    // ... deal with old_state if required ...
}

If you want to restore the previous state later, make sure to save the old_state somewhere safe. If you call that function multiple times, you need to either use a stack or only save the first or last old_state... or maybe have a function which removes a specific blocked signal.

For more info read the man page.

Alexis Wilke
  • 19,179
  • 10
  • 84
  • 156
  • It's not necessary to read-modify-write the set of blocked signals like this. `sigprocmask` adds to the set of blocked signals, so you can do all this with a single call. – Luchs Nov 06 '18 at 10:42
  • I don't _read-modify-write_, I read to save the current state which I keep in `old_state` so that way I can restore it later, if I choose to. If you know you won't need to ever restore the state, there is indeed no need to read and store it in this way. – Alexis Wilke Nov 06 '18 at 15:50
  • 1
    But you do: the first call to `sigprocmask()` reads the old state, the call to `sigaddset` modifies it, the second call to `sigprocmask()` writes it. You could remove to first call, initialize with `sigemptyset(&set)` and change the second call to `sigprocmask(SIG_BLOCK, &set, &old_state)`. – Luchs Nov 21 '18 at 09:16
  • Ah! Ha ha! You're right. I do. Well... in my software, I do not know which signals I've already blocked or not blocked. So I do it this way. However, I agree that if you have only one "set signals this way" then a simple clear + set is enough. – Alexis Wilke Nov 22 '18 at 05:53
2

Linux manual said:

EPIPE The local end has been shut down on a connection oriented socket. In this case the process will also receive a SIGPIPE unless MSG_NOSIGNAL is set.

But for Ubuntu 12.04 it isn't right. I wrote a test for that case and I always receive EPIPE withot SIGPIPE. SIGPIPE is genereated if I try to write to the same broken socket second time. So you don't need to ignore SIGPIPE if this signal happens it means logic error in your program.

talash
  • 29
  • 2
  • 3
    I most definitely receive SIGPIPE without first seeing EPIPE on a socket in linux. This sounds like a kernel bug. – davmac Jun 14 '16 at 22:08