Skip to content

Commit 4ecfa7a

Browse files
committed
Added RESTART command (only for ForkDaemon mode) for easier upgrades.
2 parents ce91a21 + 134fade commit 4ecfa7a

File tree

7 files changed

+192
-11
lines changed

7 files changed

+192
-11
lines changed

bitlbee.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,9 @@ int bitlbee_daemon_init()
110110
}
111111
#endif
112112

113+
if( global.conf->runmode == RUNMODE_FORKDAEMON )
114+
ipc_master_load_state();
115+
113116
return( 0 );
114117
}
115118

bitlbee.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ typedef struct global {
120120
GList *storage; /* The first backend in the list will be used for saving */
121121
char *helpfile;
122122
GMainLoop *loop;
123+
int restart;
123124
} global_t;
124125

125126
int bitlbee_daemon_init( void );

conf.c

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include "conf.h"
3232
#include "ini.h"
3333
#include "url.h"
34+
#include "ipc.h"
3435

3536
#include "protocols/proxy.h"
3637

@@ -76,7 +77,7 @@ conf_t *conf_load( int argc, char *argv[] )
7677
fprintf( stderr, "Warning: Unable to read configuration file `%s'.\n", CONF_FILE );
7778
}
7879

79-
while( argc > 0 && ( opt = getopt( argc, argv, "i:p:nvIDFc:d:h" ) ) >= 0 )
80+
while( argc > 0 && ( opt = getopt( argc, argv, "i:p:nvIDFc:d:hR:" ) ) >= 0 )
8081
/* ^^^^ Just to make sure we skip this step from the REHASH handler. */
8182
{
8283
if( opt == 'i' )
@@ -141,6 +142,14 @@ conf_t *conf_load( int argc, char *argv[] )
141142
" -h Show this help page.\n" );
142143
return( NULL );
143144
}
145+
else if( opt == 'R' )
146+
{
147+
/* We can't load the statefile yet (and should make very sure we do this
148+
only once), so set the filename here and load the state information
149+
when initializing ForkDaemon. (This option only makes sense in that
150+
mode anyway!) */
151+
ipc_master_set_statefile( optarg );
152+
}
144153
}
145154

146155
if( conf->configdir[strlen(conf->configdir)-1] != '/' )

ipc.c

Lines changed: 127 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/********************************************************************\
22
* BitlBee -- An IRC to other IM-networks gateway *
33
* *
4-
* Copyright 2002-2004 Wilmer van der Gaast and others *
4+
* Copyright 2002-2006 Wilmer van der Gaast and others *
55
\********************************************************************/
66

77
/* IPC - communication between BitlBee processes */
@@ -29,21 +29,22 @@
2929
#include "commands.h"
3030

3131
GSList *child_list = NULL;
32-
32+
static char *statefile = NULL;
3333

3434
static void ipc_master_cmd_client( irc_t *data, char **cmd )
3535
{
3636
struct bitlbee_child *child = (void*) data;
3737

38-
if( child )
38+
if( child && cmd[1] )
3939
{
4040
child->host = g_strdup( cmd[1] );
4141
child->nick = g_strdup( cmd[2] );
4242
child->realname = g_strdup( cmd[3] );
4343
}
4444

45-
ipc_to_children_str( "OPERMSG :Client connecting (PID=%d): %s@%s (%s)\r\n",
46-
child ? child->pid : -1, cmd[2], cmd[1], cmd[3] );
45+
if( g_strcasecmp( cmd[0], "CLIENT" ) == 0 )
46+
ipc_to_children_str( "OPERMSG :Client connecting (PID=%d): %s@%s (%s)\r\n",
47+
child ? child->pid : -1, cmd[2], cmd[1], cmd[3] );
4748
}
4849

4950
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 )
7374
ipc_to_children( cmd );
7475
}
7576

77+
void ipc_master_cmd_restart( irc_t *data, char **cmd )
78+
{
79+
struct bitlbee_child *child = (void*) data;
80+
81+
if( global.conf->runmode != RUNMODE_FORKDAEMON )
82+
{
83+
/* Tell child that this is unsupported. */
84+
return;
85+
}
86+
87+
global.restart = -1;
88+
bitlbee_shutdown( NULL );
89+
}
90+
7691
static const command_t ipc_master_commands[] = {
7792
{ "client", 3, ipc_master_cmd_client, 0 },
93+
{ "hello", 0, ipc_master_cmd_client, 0 },
7894
{ "die", 0, ipc_master_cmd_die, 0 },
7995
{ "wallops", 1, NULL, IPC_CMD_TO_CHILDREN },
8096
{ "lilo", 1, NULL, IPC_CMD_TO_CHILDREN },
8197
{ "opermsg", 1, NULL, IPC_CMD_TO_CHILDREN },
8298
{ "rehash", 0, ipc_master_cmd_rehash, 0 },
8399
{ "kill", 2, NULL, IPC_CMD_TO_CHILDREN },
100+
{ "restart", 0, ipc_master_cmd_restart, 0 },
84101
{ NULL }
85102
};
86103

@@ -141,33 +158,48 @@ static void ipc_child_cmd_kill( irc_t *irc, char **cmd )
141158
irc_abort( irc, 0, "Killed by operator: %s", cmd[2] );
142159
}
143160

161+
static void ipc_child_cmd_hello( irc_t *irc, char **cmd )
162+
{
163+
if( irc->status < USTATUS_LOGGED_IN )
164+
ipc_to_master_str( "HELLO\r\n" );
165+
else
166+
ipc_to_master_str( "HELLO %s %s :%s\r\n", irc->host, irc->nick, irc->realname );
167+
}
168+
144169
static const command_t ipc_child_commands[] = {
145170
{ "die", 0, ipc_child_cmd_die, 0 },
146171
{ "wallops", 1, ipc_child_cmd_wallops, 0 },
147172
{ "lilo", 1, ipc_child_cmd_lilo, 0 },
148173
{ "opermsg", 1, ipc_child_cmd_opermsg, 0 },
149174
{ "rehash", 0, ipc_child_cmd_rehash, 0 },
150175
{ "kill", 2, ipc_child_cmd_kill, 0 },
176+
{ "hello", 0, ipc_child_cmd_hello, 0 },
151177
{ NULL }
152178
};
153179

154180

155181
static void ipc_command_exec( void *data, char **cmd, const command_t *commands )
156182
{
157-
int i;
183+
int i, j;
158184

159185
if( !cmd[0] )
160186
return;
161187

162188
for( i = 0; commands[i].command; i ++ )
163189
if( g_strcasecmp( commands[i].command, cmd[0] ) == 0 )
164190
{
191+
/* There is no typo in this line: */
192+
for( j = 1; cmd[j]; j ++ ); j --;
193+
194+
if( j < commands[i].required_parameters )
195+
break;
196+
165197
if( commands[i].flags & IPC_CMD_TO_CHILDREN )
166198
ipc_to_children( cmd );
167199
else
168200
commands[i].execute( data, cmd );
169201

170-
return;
202+
break;
171203
}
172204
}
173205

@@ -378,3 +410,91 @@ void ipc_master_free_all()
378410
g_slist_free( child_list );
379411
child_list = NULL;
380412
}
413+
414+
char *ipc_master_save_state()
415+
{
416+
char *fn = g_strdup( "/tmp/bee-restart.XXXXXX" );
417+
int fd = mkstemp( fn );
418+
GSList *l;
419+
FILE *fp;
420+
int i;
421+
422+
if( fd == -1 )
423+
{
424+
log_message( LOGLVL_ERROR, "Could not create temporary file: %s", strerror( errno ) );
425+
g_free( fn );
426+
return NULL;
427+
}
428+
429+
/* This is more convenient now. */
430+
fp = fdopen( fd, "w" );
431+
432+
for( l = child_list, i = 0; l; l = l->next )
433+
i ++;
434+
435+
/* Number of client processes. */
436+
fprintf( fp, "%d\n", i );
437+
438+
for( l = child_list; l; l = l->next )
439+
fprintf( fp, "%d %d\n", ((struct bitlbee_child*)l->data)->pid,
440+
((struct bitlbee_child*)l->data)->ipc_fd );
441+
442+
if( fclose( fp ) == 0 )
443+
{
444+
return fn;
445+
}
446+
else
447+
{
448+
unlink( fn );
449+
g_free( fn );
450+
return NULL;
451+
}
452+
}
453+
454+
void ipc_master_set_statefile( char *fn )
455+
{
456+
statefile = g_strdup( fn );
457+
}
458+
459+
int ipc_master_load_state()
460+
{
461+
struct bitlbee_child *child;
462+
FILE *fp;
463+
int i, n;
464+
465+
if( statefile == NULL )
466+
return 0;
467+
fp = fopen( statefile, "r" );
468+
unlink( statefile ); /* Why do it later? :-) */
469+
if( fp == NULL )
470+
return 0;
471+
472+
if( fscanf( fp, "%d", &n ) != 1 )
473+
{
474+
log_message( LOGLVL_WARNING, "Could not import state information for child processes." );
475+
fclose( fp );
476+
return 0;
477+
}
478+
479+
log_message( LOGLVL_INFO, "Importing information for %d child processes.", n );
480+
for( i = 0; i < n; i ++ )
481+
{
482+
child = g_new0( struct bitlbee_child, 1 );
483+
484+
if( fscanf( fp, "%d %d", &child->pid, &child->ipc_fd ) != 2 )
485+
{
486+
log_message( LOGLVL_WARNING, "Unexpected end of file: Only processed %d clients.", i );
487+
g_free( child );
488+
fclose( fp );
489+
return 0;
490+
}
491+
child->ipc_inpa = gaim_input_add( child->ipc_fd, GAIM_INPUT_READ, ipc_master_read, child );
492+
493+
child_list = g_slist_append( child_list, child );
494+
}
495+
496+
ipc_to_children_str( "HELLO\r\n" );
497+
ipc_to_children_str( "OPERMSG :New BitlBee master process started (version " BITLBEE_VERSION ")\r\n" );
498+
499+
return 1;
500+
}

ipc.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,5 +53,9 @@ void ipc_to_children_str( char *format, ... );
5353
/* We need this function in inetd mode, so let's just make it non-static. */
5454
void ipc_master_cmd_rehash( irc_t *data, char **cmd );
5555

56+
char *ipc_master_save_state();
57+
void ipc_master_set_statefile( char *fn );
58+
int ipc_master_load_state();
59+
5660

5761
extern GSList *child_list;

irc_commands.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -572,20 +572,24 @@ static const command_t irc_commands[] = {
572572
{ "wallops", 1, NULL, IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER },
573573
{ "lilo", 1, NULL, IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER },
574574
{ "rehash", 0, irc_cmd_rehash, IRC_CMD_OPER_ONLY },
575+
{ "restart", 0, NULL, IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER },
575576
{ "kill", 2, NULL, IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER },
576577
{ NULL }
577578
};
578579

579580
void irc_exec( irc_t *irc, char *cmd[] )
580581
{
581-
int i;
582+
int i, n_arg;
582583

583584
if( !cmd[0] )
584585
return;
585586

586587
for( i = 0; irc_commands[i].command; i++ )
587588
if( g_strcasecmp( irc_commands[i].command, cmd[0] ) == 0 )
588589
{
590+
/* There should be no typo in the next line: */
591+
for( n_arg = 0; cmd[n_arg]; n_arg ++ ); n_arg --;
592+
589593
if( irc_commands[i].flags & IRC_CMD_PRE_LOGIN && irc->status >= USTATUS_LOGGED_IN )
590594
{
591595
irc_reply( irc, 462, ":Only allowed before logging in" );
@@ -598,7 +602,7 @@ void irc_exec( irc_t *irc, char *cmd[] )
598602
{
599603
irc_reply( irc, 481, ":Permission denied - You're not an IRC operator" );
600604
}
601-
else if( !cmd[irc_commands[i].required_parameters] )
605+
else if( n_arg < irc_commands[i].required_parameters )
602606
{
603607
irc_reply( irc, 461, "%s :Need more parameters", cmd[0] );
604608
}

unix.c

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "crypting.h"
2929
#include "protocols/nogaim.h"
3030
#include "help.h"
31+
#include "ipc.h"
3132
#include <signal.h>
3233
#include <unistd.h>
3334
#include <sys/time.h>
@@ -37,9 +38,10 @@ global_t global; /* Against global namespace pollution */
3738

3839
static void sighandler( int signal );
3940

40-
int main( int argc, char *argv[] )
41+
int main( int argc, char *argv[], char **envp )
4142
{
4243
int i = 0;
44+
char *old_cwd;
4345
struct sigaction sig, old;
4446

4547
memset( &global, 0, sizeof( global_t ) );
@@ -72,6 +74,15 @@ int main( int argc, char *argv[] )
7274
}
7375
else if( global.conf->runmode == RUNMODE_FORKDAEMON )
7476
{
77+
/* In case the operator requests a restart, we need this. */
78+
old_cwd = g_malloc( 256 );
79+
if( getcwd( old_cwd, 255 ) == NULL )
80+
{
81+
log_message( LOGLVL_WARNING, "Could not save current directory: %s", strerror( errno ) );
82+
g_free( old_cwd );
83+
old_cwd = NULL;
84+
}
85+
7586
i = bitlbee_daemon_init();
7687
log_message( LOGLVL_INFO, "Bitlbee %s starting in forking daemon mode.", BITLBEE_VERSION );
7788
}
@@ -107,6 +118,35 @@ int main( int argc, char *argv[] )
107118

108119
g_main_run( global.loop );
109120

121+
if( global.restart )
122+
{
123+
char *fn = ipc_master_save_state();
124+
char **args;
125+
int n, i;
126+
127+
chdir( old_cwd );
128+
129+
n = 0;
130+
args = g_new0( char *, argc + 3 );
131+
args[n++] = argv[0];
132+
if( fn )
133+
{
134+
args[n++] = "-R";
135+
args[n++] = fn;
136+
}
137+
for( i = 1; argv[i] && i < argc; i ++ )
138+
{
139+
if( strcmp( argv[i], "-R" ) == 0 )
140+
i += 2;
141+
142+
args[n++] = argv[i];
143+
}
144+
145+
close( global.listen_socket );
146+
147+
execve( args[0], args, envp );
148+
}
149+
110150
return( 0 );
111151
}
112152

0 commit comments

Comments
 (0)