source: ipc.c @ f665dab

Last change on this file since f665dab was 57c4fc0, checked in by Wilmer van der Gaast <wilmer@…>, at 2006-01-22T09:19:03Z

Error checking for accept(). Have to find out why it went wrong (and fork()ed a bit too much) last night.

  • Property mode set to 100644
File size: 11.6 KB
Line 
1  /********************************************************************\
2  * BitlBee -- An IRC to other IM-networks gateway                     *
3  *                                                                    *
4  * Copyright 2002-2006 Wilmer van der Gaast and others                *
5  \********************************************************************/
6
7/* IPC - communication between BitlBee processes                        */
8
9/*
10  This program is free software; you can redistribute it and/or modify
11  it under the terms of the GNU General Public License as published by
12  the Free Software Foundation; either version 2 of the License, or
13  (at your option) any later version.
14
15  This program is distributed in the hope that it will be useful,
16  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  GNU General Public License for more details.
19
20  You should have received a copy of the GNU General Public License with
21  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
22  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
23  Suite 330, Boston, MA  02111-1307  USA
24*/
25
26#define BITLBEE_CORE
27#include "bitlbee.h"
28#include "ipc.h"
29#include "commands.h"
30
31GSList *child_list = NULL;
32static char *statefile = NULL;
33
34static void ipc_master_cmd_client( irc_t *data, char **cmd )
35{
36        struct bitlbee_child *child = (void*) data;
37       
38        if( child && cmd[1] )
39        {
40                child->host = g_strdup( cmd[1] );
41                child->nick = g_strdup( cmd[2] );
42                child->realname = g_strdup( cmd[3] );
43        }
44       
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] );
48}
49
50static void ipc_master_cmd_die( irc_t *data, char **cmd )
51{
52        if( global.conf->runmode == RUNMODE_FORKDAEMON )
53                ipc_to_children_str( "DIE\r\n" );
54       
55        bitlbee_shutdown( NULL );
56}
57
58void ipc_master_cmd_rehash( irc_t *data, char **cmd )
59{
60        runmode_t oldmode;
61       
62        oldmode = global.conf->runmode;
63       
64        g_free( global.conf );
65        global.conf = conf_load( 0, NULL );
66       
67        if( global.conf->runmode != oldmode )
68        {
69                log_message( LOGLVL_WARNING, "Can't change RunMode setting at runtime, restoring original setting" );
70                global.conf->runmode = oldmode;
71        }
72       
73        if( global.conf->runmode == RUNMODE_FORKDAEMON )
74                ipc_to_children( cmd );
75}
76
77void 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
91static const command_t ipc_master_commands[] = {
92        { "client",     3, ipc_master_cmd_client,     0 },
93        { "hello",      0, ipc_master_cmd_client,     0 },
94        { "die",        0, ipc_master_cmd_die,        0 },
95        { "wallops",    1, NULL,                      IPC_CMD_TO_CHILDREN },
96        { "lilo",       1, NULL,                      IPC_CMD_TO_CHILDREN },
97        { "opermsg",    1, NULL,                      IPC_CMD_TO_CHILDREN },
98        { "rehash",     0, ipc_master_cmd_rehash,     0 },
99        { "kill",       2, NULL,                      IPC_CMD_TO_CHILDREN },
100        { "restart",    0, ipc_master_cmd_restart,    0 },
101        { NULL }
102};
103
104
105static void ipc_child_cmd_die( irc_t *irc, char **cmd )
106{
107        irc_abort( irc, 0, "Shutdown requested by operator" );
108}
109
110static void ipc_child_cmd_wallops( irc_t *irc, char **cmd )
111{
112        if( irc->status < USTATUS_LOGGED_IN )
113                return;
114       
115        if( strchr( irc->umode, 'w' ) )
116                irc_write( irc, ":%s WALLOPS :%s", irc->myhost, cmd[1] );
117}
118
119static void ipc_child_cmd_lilo( irc_t *irc, char **cmd )
120{
121        if( irc->status < USTATUS_LOGGED_IN )
122                return;
123       
124        if( strchr( irc->umode, 's' ) )
125                irc_write( irc, ":%s NOTICE %s :%s", irc->myhost, irc->nick, cmd[1] );
126}
127
128static void ipc_child_cmd_opermsg( irc_t *irc, char **cmd )
129{
130        if( irc->status < USTATUS_LOGGED_IN )
131                return;
132       
133        if( strchr( irc->umode, 'o' ) )
134                irc_write( irc, ":%s NOTICE %s :*** OperMsg *** %s", irc->myhost, irc->nick, cmd[1] );
135}
136
137static void ipc_child_cmd_rehash( irc_t *irc, char **cmd )
138{
139        runmode_t oldmode;
140       
141        oldmode = global.conf->runmode;
142       
143        g_free( global.conf );
144        global.conf = conf_load( 0, NULL );
145       
146        global.conf->runmode = oldmode;
147}
148
149static void ipc_child_cmd_kill( irc_t *irc, char **cmd )
150{
151        if( irc->status < USTATUS_LOGGED_IN )
152                return;
153       
154        if( nick_cmp( cmd[1], irc->nick ) != 0 )
155                return;         /* It's not for us. */
156       
157        irc_write( irc, ":%s!%s@%s KILL %s :%s", irc->mynick, irc->mynick, irc->myhost, irc->nick, cmd[2] );
158        irc_abort( irc, 0, "Killed by operator: %s", cmd[2] );
159}
160
161static 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
169static const command_t ipc_child_commands[] = {
170        { "die",        0, ipc_child_cmd_die,         0 },
171        { "wallops",    1, ipc_child_cmd_wallops,     0 },
172        { "lilo",       1, ipc_child_cmd_lilo,        0 },
173        { "opermsg",    1, ipc_child_cmd_opermsg,     0 },
174        { "rehash",     0, ipc_child_cmd_rehash,      0 },
175        { "kill",       2, ipc_child_cmd_kill,        0 },
176        { "hello",      0, ipc_child_cmd_hello,       0 },
177        { NULL }
178};
179
180
181static void ipc_command_exec( void *data, char **cmd, const command_t *commands )
182{
183        int i, j;
184       
185        if( !cmd[0] )
186                return;
187       
188        for( i = 0; commands[i].command; i ++ )
189                if( g_strcasecmp( commands[i].command, cmd[0] ) == 0 )
190                {
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                       
197                        if( commands[i].flags & IPC_CMD_TO_CHILDREN )
198                                ipc_to_children( cmd );
199                        else
200                                commands[i].execute( data, cmd );
201                       
202                        break;
203                }
204}
205
206static char *ipc_readline( int fd )
207{
208        char *buf, *eol;
209        int size;
210       
211        buf = g_new0( char, 513 );
212       
213        /* Because this is internal communication, it should be pretty safe
214           to just peek at the message, find its length (by searching for the
215           end-of-line) and then just read that message. With internal
216           sockets and limites message length, messages should always be
217           complete. Saves us quite a lot of code and buffering. */
218        size = recv( fd, buf, 512, MSG_PEEK );
219        if( size == 0 || ( size < 0 && !sockerr_again() ) )
220                return NULL;
221        else if( size < 0 ) /* && sockerr_again() */
222                return( g_strdup( "" ) );
223        else
224                buf[size] = 0;
225       
226        eol = strstr( buf, "\r\n" );
227        if( eol == NULL )
228                return NULL;
229        else
230                size = eol - buf + 2;
231       
232        g_free( buf );
233        buf = g_new0( char, size + 1 );
234       
235        if( recv( fd, buf, size, 0 ) != size )
236                return NULL;
237        else
238                buf[size-2] = 0;
239       
240        return buf;
241}
242
243void ipc_master_read( gpointer data, gint source, GaimInputCondition cond )
244{
245        char *buf, **cmd;
246       
247        if( ( buf = ipc_readline( source ) ) )
248        {
249                cmd = irc_parse_line( buf );
250                if( cmd )
251                        ipc_command_exec( data, cmd, ipc_master_commands );
252        }
253        else
254        {
255                GSList *l;
256                struct bitlbee_child *c;
257               
258                for( l = child_list; l; l = l->next )
259                {
260                        c = l->data;
261                        if( c->ipc_fd == source )
262                        {
263                                ipc_master_free_one( c );
264                                child_list = g_slist_remove( child_list, c );
265                                break;
266                        }
267                }
268        }
269}
270
271void ipc_child_read( gpointer data, gint source, GaimInputCondition cond )
272{
273        char *buf, **cmd;
274       
275        if( ( buf = ipc_readline( source ) ) )
276        {
277                cmd = irc_parse_line( buf );
278                if( cmd )
279                        ipc_command_exec( data, cmd, ipc_child_commands );
280        }
281        else
282        {
283                gaim_input_remove( global.listen_watch_source_id );
284                close( global.listen_socket );
285               
286                global.listen_socket = -1;
287        }
288}
289
290void ipc_to_master( char **cmd )
291{
292        if( global.conf->runmode == RUNMODE_FORKDAEMON )
293        {
294                char *s = irc_build_line( cmd );
295                ipc_to_master_str( "%s", s );
296                g_free( s );
297        }
298        else if( global.conf->runmode == RUNMODE_DAEMON )
299        {
300                ipc_command_exec( NULL, cmd, ipc_master_commands );
301        }
302}
303
304void ipc_to_master_str( char *format, ... )
305{
306        char *msg_buf;
307        va_list params;
308
309        va_start( params, format );
310        msg_buf = g_strdup_vprintf( format, params );
311        va_end( params );
312       
313        if( strlen( msg_buf ) > 512 )
314        {
315                /* Don't send it, it's too long... */
316        }
317        else if( global.conf->runmode == RUNMODE_FORKDAEMON )
318        {
319                write( global.listen_socket, msg_buf, strlen( msg_buf ) );
320        }
321        else if( global.conf->runmode == RUNMODE_DAEMON )
322        {
323                char **cmd, *s;
324               
325                if( ( s = strchr( msg_buf, '\r' ) ) )
326                        *s = 0;
327               
328                cmd = irc_parse_line( msg_buf );
329                ipc_command_exec( NULL, cmd, ipc_master_commands );
330                g_free( cmd );
331        }
332       
333        g_free( msg_buf );
334}
335
336void ipc_to_children( char **cmd )
337{
338        if( global.conf->runmode == RUNMODE_FORKDAEMON )
339        {
340                char *msg_buf = irc_build_line( cmd );
341                ipc_to_children_str( "%s", msg_buf );
342                g_free( msg_buf );
343        }
344        else if( global.conf->runmode == RUNMODE_DAEMON )
345        {
346                GSList *l;
347               
348                for( l = irc_connection_list; l; l = l->next )
349                        ipc_command_exec( l->data, cmd, ipc_child_commands );
350        }
351}
352
353void ipc_to_children_str( char *format, ... )
354{
355        char *msg_buf;
356        va_list params;
357
358        va_start( params, format );
359        msg_buf = g_strdup_vprintf( format, params );
360        va_end( params );
361       
362        if( strlen( msg_buf ) > 512 )
363        {
364                /* Don't send it, it's too long... */
365        }
366        else if( global.conf->runmode == RUNMODE_FORKDAEMON )
367        {
368                int msg_len = strlen( msg_buf );
369                GSList *l;
370               
371                for( l = child_list; l; l = l->next )
372                {
373                        struct bitlbee_child *c = l->data;
374                        write( c->ipc_fd, msg_buf, msg_len );
375                }
376        }
377        else if( global.conf->runmode == RUNMODE_DAEMON )
378        {
379                char **cmd, *s;
380               
381                if( ( s = strchr( msg_buf, '\r' ) ) )
382                        *s = 0;
383               
384                cmd = irc_parse_line( msg_buf );
385                ipc_to_children( cmd );
386                g_free( cmd );
387        }
388       
389        g_free( msg_buf );
390}
391
392void ipc_master_free_one( struct bitlbee_child *c )
393{
394        gaim_input_remove( c->ipc_inpa );
395        closesocket( c->ipc_fd );
396       
397        g_free( c->host );
398        g_free( c->nick );
399        g_free( c->realname );
400        g_free( c );
401}
402
403void ipc_master_free_all()
404{
405        GSList *l;
406       
407        for( l = child_list; l; l = l->next )
408                ipc_master_free_one( l->data );
409       
410        g_slist_free( child_list );
411        child_list = NULL;
412}
413
414char *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
454void ipc_master_set_statefile( char *fn )
455{
456        statefile = g_strdup( fn );
457}
458
459int 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}
Note: See TracBrowser for help on using the repository browser.