How to prevent SIGPIPEs (or handle them properly)

前端 未结 10 1587
名媛妹妹
名媛妹妹 2020-11-22 07:27

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

相关标签:
10条回答
  • 2020-11-22 07:53

    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.

    0 讨论(0)
  • 2020-11-22 07:58

    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

    0 讨论(0)
  • 2020-11-22 08:00

    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

    0 讨论(0)
  • 2020-11-22 08:01

    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.

    0 讨论(0)
  • 2020-11-22 08:01

    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 );
    
    0 讨论(0)
  • 2020-11-22 08:08

    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.

    0 讨论(0)
提交回复
热议问题