diff options
| author | dequis <dx@dxzone.com.ar> | 2014-11-26 02:25:05 -0300 | 
|---|---|---|
| committer | dequis <dx@dxzone.com.ar> | 2014-11-26 02:25:05 -0300 | 
| commit | 7233f68b3d99c447d4f0cd855b179b9079383bb2 (patch) | |
| tree | f06359d045523795c6e6f75dd68d28c5412dec9a /unix.c | |
| parent | 6f6725c5bbec31d1b9f6937f229f140828e50e45 (diff) | |
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.
Diffstat (limited to 'unix.c')
| -rw-r--r-- | unix.c | 87 | 
1 files changed, 34 insertions, 53 deletions
| @@ -47,7 +47,9 @@  global_t global;	/* Against global namespace pollution */ -static void sighandler( int signal ); +static int signal_shutdown_pipe[2] = { -1, -1 }; +static void sighandler_shutdown( int signal ); +static void sighandler_crash( int signal );  static int crypt_main( int argc, char *argv[] ); @@ -155,18 +157,20 @@ int main( int argc, char *argv[] )  	/* Catch some signals to tell the user what's happening before quitting */  	memset( &sig, 0, sizeof( sig ) ); -	sig.sa_handler = sighandler; +	sig.sa_handler = SIG_IGN;  	sigaction( SIGCHLD, &sig, &old );  	sigaction( SIGPIPE, &sig, &old );  	sig.sa_flags = SA_RESETHAND; -	sigaction( SIGINT,  &sig, &old ); -	sigaction( SIGILL,  &sig, &old ); -	sigaction( SIGBUS,  &sig, &old ); -	sigaction( SIGFPE,  &sig, &old ); +	sig.sa_handler = sighandler_crash;  	sigaction( SIGSEGV, &sig, &old ); -	sigaction( SIGTERM, &sig, &old ); -	sigaction( SIGQUIT, &sig, &old ); -	sigaction( SIGXCPU, &sig, &old ); + +	/* Use a pipe for SIGTERM/SIGINT so the actual signal handler doesn't do anything unsafe */ +	if ( pipe( signal_shutdown_pipe ) == 0 ) { +		b_input_add( signal_shutdown_pipe[0], B_EV_IO_READ, bitlbee_shutdown, NULL ); +		sig.sa_handler = sighandler_shutdown; +		sigaction( SIGINT, &sig, &old ); +		sigaction( SIGTERM, &sig, &old ); +	}  	if( !getuid() || !geteuid() )  		log_message( LOGLVL_WARNING, "BitlBee is running with root privileges. Why?" ); @@ -258,52 +262,29 @@ static int crypt_main( int argc, char *argv[] )  	return 0;  } -static void sighandler( int signal ) +/* Signal handler for SIGTERM and SIGINT */ +static void sighandler_shutdown( int signal )  { -	/* FIXME: Calling log_message() here is not a very good idea! */ -	 -	if( signal == SIGTERM || signal == SIGQUIT || signal == SIGINT ) -	{ -		static int first = 1; -		 -		if( first ) -		{ -			/* We don't know what we were doing when this signal came in. It's not safe to touch -			   the user data now (not to mention writing them to disk), so add a timer. */ -			 -			log_message( LOGLVL_ERROR, "SIGTERM received, cleaning up process." ); -			b_timeout_add( 1, (b_event_handler) bitlbee_shutdown, NULL ); -			 -			first = 0; -		} -		else -		{ -			/* Well, actually, for now we'll never need this part because this signal handler -			   will never be called more than once in a session for a non-SIGPIPE signal... -			   But just in case we decide to change that: */ -			 -			log_message( LOGLVL_ERROR, "SIGTERM received twice, so long for a clean shutdown." ); -			raise( signal ); -		} -	} -	else if( signal == SIGCHLD ) -	{ -		pid_t pid; -		int st; -		 -		while( ( pid = waitpid( 0, &st, WNOHANG ) ) > 0 ) -		{ -			if( WIFSIGNALED( st ) ) -				log_message( LOGLVL_INFO, "Client %d terminated normally. (status = %d)", (int) pid, WEXITSTATUS( st ) ); -			else if( WIFEXITED( st ) ) -				log_message( LOGLVL_INFO, "Client %d killed by signal %d.", (int) pid, WTERMSIG( st ) ); -		} -	} -	else if( signal != SIGPIPE ) -	{ -		log_message( LOGLVL_ERROR, "Fatal signal received: %d. That's probably a bug.", signal ); -		raise( signal ); +	/* Write a single null byte to the pipe, just to send a message to the main loop. +	 * This gets handled by bitlbee_shutdown (the b_input_add callback for this pipe) */ +	write( signal_shutdown_pipe[1], "", 1 ); +} + +/* Signal handler for SIGSEGV + * A desperate attempt to tell the user that everything is wrong in the world. + * Avoids using irc_abort() because it has several unsafe calls to malloc */ +static void sighandler_crash( int signal ) +{ +	GSList *l; +	const char *message = "ERROR :BitlBee crashed! (SIGSEGV received)\r\n"; +	int len = strlen(message); + +	for (l = irc_connection_list; l; l = l->next ) { +		irc_t *irc = l->data; +		write( irc->fd, message, len );  	} + +	raise( signal );  }  double gettime() | 
