aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWilmer van der Gaast <wilmer@gaast.net>2006-01-21 23:31:10 +0100
committerWilmer van der Gaast <wilmer@gaast.net>2006-01-21 23:31:10 +0100
commit54879ab3b2cbcaf1c9114bddd85ec5d4dc097915 (patch)
tree34cc9e33fc6343fd90141aa0b1faaee57debbe61
parentf73b9697f9be18e04ec7458634520f9dd2e2432f (diff)
parentf1d38f20f760376f43b90a105486cf3ff2fbf2c4 (diff)
Added RESTART command (only for ForkDaemon mode) for easier upgrades.
-rw-r--r--bitlbee.c3
-rw-r--r--bitlbee.h1
-rw-r--r--conf.c11
-rw-r--r--ipc.c134
-rw-r--r--ipc.h4
-rw-r--r--irc_commands.c8
-rw-r--r--unix.c42
7 files changed, 192 insertions, 11 deletions
diff --git a/bitlbee.c b/bitlbee.c
index 9a4688d8..64220914 100644
--- a/bitlbee.c
+++ b/bitlbee.c
@@ -110,6 +110,9 @@ int bitlbee_daemon_init()
}
#endif
+ if( global.conf->runmode == RUNMODE_FORKDAEMON )
+ ipc_master_load_state();
+
return( 0 );
}
diff --git a/bitlbee.h b/bitlbee.h
index e459f07d..b43159d0 100644
--- a/bitlbee.h
+++ b/bitlbee.h
@@ -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 );
diff --git a/conf.c b/conf.c
index ae4b77a2..d90816d5 100644
--- a/conf.c
+++ b/conf.c
@@ -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] != '/' )
diff --git a/ipc.c b/ipc.c
index 8d44e4eb..0fb56099 100644
--- a/ipc.c
+++ b/ipc.c
@@ -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;
+}
diff --git a/ipc.h b/ipc.h
index b69a6ae5..e8ad2a0d 100644
--- a/ipc.h
+++ b/ipc.h
@@ -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] );
}
diff --git a/unix.c b/unix.c
index fafa828e..e59c341a 100644
--- a/unix.c
+++ b/unix.c
@@ -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 );
}