diff options
-rw-r--r-- | bitlbee.c | 3 | ||||
-rw-r--r-- | bitlbee.h | 1 | ||||
-rw-r--r-- | conf.c | 11 | ||||
-rw-r--r-- | ipc.c | 134 | ||||
-rw-r--r-- | ipc.h | 4 | ||||
-rw-r--r-- | irc_commands.c | 8 | ||||
-rw-r--r-- | unix.c | 42 |
7 files changed, 192 insertions, 11 deletions
@@ -110,6 +110,9 @@ int bitlbee_daemon_init() } #endif + if( global.conf->runmode == RUNMODE_FORKDAEMON ) + ipc_master_load_state(); + return( 0 ); } @@ -120,6 +120,7 @@ typedef struct global { GList *storage; /* The first backend in the list will be used for saving */ char *helpfile; GMainLoop *loop; + int restart; } global_t; int bitlbee_daemon_init( void ); @@ -31,6 +31,7 @@ #include "conf.h" #include "ini.h" #include "url.h" +#include "ipc.h" #include "protocols/proxy.h" @@ -76,7 +77,7 @@ conf_t *conf_load( int argc, char *argv[] ) fprintf( stderr, "Warning: Unable to read configuration file `%s'.\n", CONF_FILE ); } - while( argc > 0 && ( opt = getopt( argc, argv, "i:p:nvIDFc:d:h" ) ) >= 0 ) + while( argc > 0 && ( opt = getopt( argc, argv, "i:p:nvIDFc:d:hR:" ) ) >= 0 ) /* ^^^^ Just to make sure we skip this step from the REHASH handler. */ { if( opt == 'i' ) @@ -141,6 +142,14 @@ conf_t *conf_load( int argc, char *argv[] ) " -h Show this help page.\n" ); return( NULL ); } + else if( opt == 'R' ) + { + /* We can't load the statefile yet (and should make very sure we do this + only once), so set the filename here and load the state information + when initializing ForkDaemon. (This option only makes sense in that + mode anyway!) */ + ipc_master_set_statefile( optarg ); + } } if( conf->configdir[strlen(conf->configdir)-1] != '/' ) @@ -1,7 +1,7 @@ /********************************************************************\ * BitlBee -- An IRC to other IM-networks gateway * * * - * Copyright 2002-2004 Wilmer van der Gaast and others * + * Copyright 2002-2006 Wilmer van der Gaast and others * \********************************************************************/ /* IPC - communication between BitlBee processes */ @@ -29,21 +29,22 @@ #include "commands.h" GSList *child_list = NULL; - +static char *statefile = NULL; static void ipc_master_cmd_client( irc_t *data, char **cmd ) { struct bitlbee_child *child = (void*) data; - if( child ) + if( child && cmd[1] ) { child->host = g_strdup( cmd[1] ); child->nick = g_strdup( cmd[2] ); child->realname = g_strdup( cmd[3] ); } - ipc_to_children_str( "OPERMSG :Client connecting (PID=%d): %s@%s (%s)\r\n", - child ? child->pid : -1, cmd[2], cmd[1], cmd[3] ); + if( g_strcasecmp( cmd[0], "CLIENT" ) == 0 ) + ipc_to_children_str( "OPERMSG :Client connecting (PID=%d): %s@%s (%s)\r\n", + child ? child->pid : -1, cmd[2], cmd[1], cmd[3] ); } static void ipc_master_cmd_die( irc_t *data, char **cmd ) @@ -73,14 +74,30 @@ void ipc_master_cmd_rehash( irc_t *data, char **cmd ) ipc_to_children( cmd ); } +void ipc_master_cmd_restart( irc_t *data, char **cmd ) +{ + struct bitlbee_child *child = (void*) data; + + if( global.conf->runmode != RUNMODE_FORKDAEMON ) + { + /* Tell child that this is unsupported. */ + return; + } + + global.restart = -1; + bitlbee_shutdown( NULL ); +} + static const command_t ipc_master_commands[] = { { "client", 3, ipc_master_cmd_client, 0 }, + { "hello", 0, ipc_master_cmd_client, 0 }, { "die", 0, ipc_master_cmd_die, 0 }, { "wallops", 1, NULL, IPC_CMD_TO_CHILDREN }, { "lilo", 1, NULL, IPC_CMD_TO_CHILDREN }, { "opermsg", 1, NULL, IPC_CMD_TO_CHILDREN }, { "rehash", 0, ipc_master_cmd_rehash, 0 }, { "kill", 2, NULL, IPC_CMD_TO_CHILDREN }, + { "restart", 0, ipc_master_cmd_restart, 0 }, { NULL } }; @@ -141,6 +158,14 @@ static void ipc_child_cmd_kill( irc_t *irc, char **cmd ) irc_abort( irc, 0, "Killed by operator: %s", cmd[2] ); } +static void ipc_child_cmd_hello( irc_t *irc, char **cmd ) +{ + if( irc->status < USTATUS_LOGGED_IN ) + ipc_to_master_str( "HELLO\r\n" ); + else + ipc_to_master_str( "HELLO %s %s :%s\r\n", irc->host, irc->nick, irc->realname ); +} + static const command_t ipc_child_commands[] = { { "die", 0, ipc_child_cmd_die, 0 }, { "wallops", 1, ipc_child_cmd_wallops, 0 }, @@ -148,13 +173,14 @@ static const command_t ipc_child_commands[] = { { "opermsg", 1, ipc_child_cmd_opermsg, 0 }, { "rehash", 0, ipc_child_cmd_rehash, 0 }, { "kill", 2, ipc_child_cmd_kill, 0 }, + { "hello", 0, ipc_child_cmd_hello, 0 }, { NULL } }; static void ipc_command_exec( void *data, char **cmd, const command_t *commands ) { - int i; + int i, j; if( !cmd[0] ) return; @@ -162,12 +188,18 @@ static void ipc_command_exec( void *data, char **cmd, const command_t *commands for( i = 0; commands[i].command; i ++ ) if( g_strcasecmp( commands[i].command, cmd[0] ) == 0 ) { + /* There is no typo in this line: */ + for( j = 1; cmd[j]; j ++ ); j --; + + if( j < commands[i].required_parameters ) + break; + if( commands[i].flags & IPC_CMD_TO_CHILDREN ) ipc_to_children( cmd ); else commands[i].execute( data, cmd ); - return; + break; } } @@ -378,3 +410,91 @@ void ipc_master_free_all() g_slist_free( child_list ); child_list = NULL; } + +char *ipc_master_save_state() +{ + char *fn = g_strdup( "/tmp/bee-restart.XXXXXX" ); + int fd = mkstemp( fn ); + GSList *l; + FILE *fp; + int i; + + if( fd == -1 ) + { + log_message( LOGLVL_ERROR, "Could not create temporary file: %s", strerror( errno ) ); + g_free( fn ); + return NULL; + } + + /* This is more convenient now. */ + fp = fdopen( fd, "w" ); + + for( l = child_list, i = 0; l; l = l->next ) + i ++; + + /* Number of client processes. */ + fprintf( fp, "%d\n", i ); + + for( l = child_list; l; l = l->next ) + fprintf( fp, "%d %d\n", ((struct bitlbee_child*)l->data)->pid, + ((struct bitlbee_child*)l->data)->ipc_fd ); + + if( fclose( fp ) == 0 ) + { + return fn; + } + else + { + unlink( fn ); + g_free( fn ); + return NULL; + } +} + +void ipc_master_set_statefile( char *fn ) +{ + statefile = g_strdup( fn ); +} + +int ipc_master_load_state() +{ + struct bitlbee_child *child; + FILE *fp; + int i, n; + + if( statefile == NULL ) + return 0; + fp = fopen( statefile, "r" ); + unlink( statefile ); /* Why do it later? :-) */ + if( fp == NULL ) + return 0; + + if( fscanf( fp, "%d", &n ) != 1 ) + { + log_message( LOGLVL_WARNING, "Could not import state information for child processes." ); + fclose( fp ); + return 0; + } + + log_message( LOGLVL_INFO, "Importing information for %d child processes.", n ); + for( i = 0; i < n; i ++ ) + { + child = g_new0( struct bitlbee_child, 1 ); + + if( fscanf( fp, "%d %d", &child->pid, &child->ipc_fd ) != 2 ) + { + log_message( LOGLVL_WARNING, "Unexpected end of file: Only processed %d clients.", i ); + g_free( child ); + fclose( fp ); + return 0; + } + child->ipc_inpa = gaim_input_add( child->ipc_fd, GAIM_INPUT_READ, ipc_master_read, child ); + + child_list = g_slist_append( child_list, child ); + } + + ipc_to_children_str( "HELLO\r\n" ); + ipc_to_children_str( "OPERMSG :New BitlBee master process started (version " BITLBEE_VERSION ")\r\n" ); + + return 1; +} @@ -53,5 +53,9 @@ void ipc_to_children_str( char *format, ... ); /* We need this function in inetd mode, so let's just make it non-static. */ void ipc_master_cmd_rehash( irc_t *data, char **cmd ); +char *ipc_master_save_state(); +void ipc_master_set_statefile( char *fn ); +int ipc_master_load_state(); + extern GSList *child_list; diff --git a/irc_commands.c b/irc_commands.c index e4dc4f3e..bbeb3db9 100644 --- a/irc_commands.c +++ b/irc_commands.c @@ -572,13 +572,14 @@ static const command_t irc_commands[] = { { "wallops", 1, NULL, IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER }, { "lilo", 1, NULL, IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER }, { "rehash", 0, irc_cmd_rehash, IRC_CMD_OPER_ONLY }, + { "restart", 0, NULL, IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER }, { "kill", 2, NULL, IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER }, { NULL } }; void irc_exec( irc_t *irc, char *cmd[] ) { - int i; + int i, n_arg; if( !cmd[0] ) return; @@ -586,6 +587,9 @@ void irc_exec( irc_t *irc, char *cmd[] ) for( i = 0; irc_commands[i].command; i++ ) if( g_strcasecmp( irc_commands[i].command, cmd[0] ) == 0 ) { + /* There should be no typo in the next line: */ + for( n_arg = 0; cmd[n_arg]; n_arg ++ ); n_arg --; + if( irc_commands[i].flags & IRC_CMD_PRE_LOGIN && irc->status >= USTATUS_LOGGED_IN ) { irc_reply( irc, 462, ":Only allowed before logging in" ); @@ -598,7 +602,7 @@ void irc_exec( irc_t *irc, char *cmd[] ) { irc_reply( irc, 481, ":Permission denied - You're not an IRC operator" ); } - else if( !cmd[irc_commands[i].required_parameters] ) + else if( n_arg < irc_commands[i].required_parameters ) { irc_reply( irc, 461, "%s :Need more parameters", cmd[0] ); } @@ -28,6 +28,7 @@ #include "crypting.h" #include "protocols/nogaim.h" #include "help.h" +#include "ipc.h" #include <signal.h> #include <unistd.h> #include <sys/time.h> @@ -37,9 +38,10 @@ global_t global; /* Against global namespace pollution */ static void sighandler( int signal ); -int main( int argc, char *argv[] ) +int main( int argc, char *argv[], char **envp ) { int i = 0; + char *old_cwd; struct sigaction sig, old; memset( &global, 0, sizeof( global_t ) ); @@ -72,6 +74,15 @@ int main( int argc, char *argv[] ) } else if( global.conf->runmode == RUNMODE_FORKDAEMON ) { + /* In case the operator requests a restart, we need this. */ + old_cwd = g_malloc( 256 ); + if( getcwd( old_cwd, 255 ) == NULL ) + { + log_message( LOGLVL_WARNING, "Could not save current directory: %s", strerror( errno ) ); + g_free( old_cwd ); + old_cwd = NULL; + } + i = bitlbee_daemon_init(); log_message( LOGLVL_INFO, "Bitlbee %s starting in forking daemon mode.", BITLBEE_VERSION ); } @@ -107,6 +118,35 @@ int main( int argc, char *argv[] ) g_main_run( global.loop ); + if( global.restart ) + { + char *fn = ipc_master_save_state(); + char **args; + int n, i; + + chdir( old_cwd ); + + n = 0; + args = g_new0( char *, argc + 3 ); + args[n++] = argv[0]; + if( fn ) + { + args[n++] = "-R"; + args[n++] = fn; + } + for( i = 1; argv[i] && i < argc; i ++ ) + { + if( strcmp( argv[i], "-R" ) == 0 ) + i += 2; + + args[n++] = argv[i]; + } + + close( global.listen_socket ); + + execve( args[0], args, envp ); + } + return( 0 ); } |