Changeset 7233f68 for unix.c


Ignore:
Timestamp:
2014-11-26T05:25:05Z (6 years ago)
Author:
dequis <dx@…>
Branches:
master
Children:
bc7a0d4
Parents:
6f6725c
Message:

Improved signal handling to avoid deadlocks

  • SIGSEGV: broadcast a message manually, avoiding the usual irc_write() functions which are unsafe due to malloc().
  • SIGTERM, SIGINT: Write to a pipe which gets handled in the main loop by bitlbee_shutdown(), saving configs and stuff.
  • SIGCHLD: set to ignore explicitly, which handles zombies correctly. This also drops some log messages with 'info' level, which in practice means they never got logged.
  • SIGPIPE: set to ignore (nobody cares)
  • SIGILL, SIGBUS, SIGFPE, SIGQUIT, SIGXCPU: Not handling anymore.
File:
1 edited

Legend:

Unmodified
Added
Removed
  • unix.c

    r6f6725c r7233f68  
    4848global_t global;        /* Against global namespace pollution */
    4949
    50 static void sighandler( int signal );
     50static int signal_shutdown_pipe[2] = { -1, -1 };
     51static void sighandler_shutdown( int signal );
     52static void sighandler_crash( int signal );
    5153
    5254static int crypt_main( int argc, char *argv[] );
     
    156158        /* Catch some signals to tell the user what's happening before quitting */
    157159        memset( &sig, 0, sizeof( sig ) );
    158         sig.sa_handler = sighandler;
     160        sig.sa_handler = SIG_IGN;
    159161        sigaction( SIGCHLD, &sig, &old );
    160162        sigaction( SIGPIPE, &sig, &old );
    161163        sig.sa_flags = SA_RESETHAND;
    162         sigaction( SIGINT,  &sig, &old );
    163         sigaction( SIGILL,  &sig, &old );
    164         sigaction( SIGBUS,  &sig, &old );
    165         sigaction( SIGFPE,  &sig, &old );
     164        sig.sa_handler = sighandler_crash;
    166165        sigaction( SIGSEGV, &sig, &old );
    167         sigaction( SIGTERM, &sig, &old );
    168         sigaction( SIGQUIT, &sig, &old );
    169         sigaction( SIGXCPU, &sig, &old );
     166
     167        /* Use a pipe for SIGTERM/SIGINT so the actual signal handler doesn't do anything unsafe */
     168        if ( pipe( signal_shutdown_pipe ) == 0 ) {
     169                b_input_add( signal_shutdown_pipe[0], B_EV_IO_READ, bitlbee_shutdown, NULL );
     170                sig.sa_handler = sighandler_shutdown;
     171                sigaction( SIGINT, &sig, &old );
     172                sigaction( SIGTERM, &sig, &old );
     173        }
    170174       
    171175        if( !getuid() || !geteuid() )
     
    259263}
    260264
    261 static void sighandler( int signal )
    262 {
    263         /* FIXME: Calling log_message() here is not a very good idea! */
    264        
    265         if( signal == SIGTERM || signal == SIGQUIT || signal == SIGINT )
    266         {
    267                 static int first = 1;
    268                
    269                 if( first )
    270                 {
    271                         /* We don't know what we were doing when this signal came in. It's not safe to touch
    272                            the user data now (not to mention writing them to disk), so add a timer. */
    273                        
    274                         log_message( LOGLVL_ERROR, "SIGTERM received, cleaning up process." );
    275                         b_timeout_add( 1, (b_event_handler) bitlbee_shutdown, NULL );
    276                        
    277                         first = 0;
    278                 }
    279                 else
    280                 {
    281                         /* Well, actually, for now we'll never need this part because this signal handler
    282                            will never be called more than once in a session for a non-SIGPIPE signal...
    283                            But just in case we decide to change that: */
    284                        
    285                         log_message( LOGLVL_ERROR, "SIGTERM received twice, so long for a clean shutdown." );
    286                         raise( signal );
    287                 }
    288         }
    289         else if( signal == SIGCHLD )
    290         {
    291                 pid_t pid;
    292                 int st;
    293                
    294                 while( ( pid = waitpid( 0, &st, WNOHANG ) ) > 0 )
    295                 {
    296                         if( WIFSIGNALED( st ) )
    297                                 log_message( LOGLVL_INFO, "Client %d terminated normally. (status = %d)", (int) pid, WEXITSTATUS( st ) );
    298                         else if( WIFEXITED( st ) )
    299                                 log_message( LOGLVL_INFO, "Client %d killed by signal %d.", (int) pid, WTERMSIG( st ) );
    300                 }
    301         }
    302         else if( signal != SIGPIPE )
    303         {
    304                 log_message( LOGLVL_ERROR, "Fatal signal received: %d. That's probably a bug.", signal );
    305                 raise( signal );
    306         }
     265/* Signal handler for SIGTERM and SIGINT */
     266static void sighandler_shutdown( int signal )
     267{
     268        /* Write a single null byte to the pipe, just to send a message to the main loop.
     269         * This gets handled by bitlbee_shutdown (the b_input_add callback for this pipe) */
     270        write( signal_shutdown_pipe[1], "", 1 );
     271}
     272
     273/* Signal handler for SIGSEGV
     274 * A desperate attempt to tell the user that everything is wrong in the world.
     275 * Avoids using irc_abort() because it has several unsafe calls to malloc */
     276static void sighandler_crash( int signal )
     277{
     278        GSList *l;
     279        const char *message = "ERROR :BitlBee crashed! (SIGSEGV received)\r\n";
     280        int len = strlen(message);
     281
     282        for (l = irc_connection_list; l; l = l->next ) {
     283                irc_t *irc = l->data;
     284                write( irc->fd, message, len );
     285        }
     286
     287        raise( signal );
    307288}
    308289
Note: See TracChangeset for help on using the changeset viewer.