source: ipc.c @ 5e713f6

Last change on this file since 5e713f6 was 5e713f6, checked in by Wilmer van der Gaast <wilmer@…>, at 2006-03-01T22:17:57Z

Added a little comment for this scary cast. (-:

  • Property mode set to 100644
File size: 11.8 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        /* Normally data points at an irc_t block, but for the IPC master
37           this is different. We think this scary cast is better than
38           creating a new command_t structure, just to make the compiler
39           happy. */
40        struct bitlbee_child *child = (void*) data;
41       
42        if( child && cmd[1] )
43        {
44                child->host = g_strdup( cmd[1] );
45                child->nick = g_strdup( cmd[2] );
46                child->realname = g_strdup( cmd[3] );
47        }
48       
49        if( g_strcasecmp( cmd[0], "CLIENT" ) == 0 )
50                ipc_to_children_str( "OPERMSG :Client connecting (PID=%d): %s@%s (%s)\r\n",
51                                     child ? child->pid : -1, cmd[2], cmd[1], cmd[3] );
52}
53
54static void ipc_master_cmd_die( irc_t *data, char **cmd )
55{
56        if( global.conf->runmode == RUNMODE_FORKDAEMON )
57                ipc_to_children_str( "DIE\r\n" );
58       
59        bitlbee_shutdown( NULL );
60}
61
62void ipc_master_cmd_rehash( irc_t *data, char **cmd )
63{
64        runmode_t oldmode;
65       
66        oldmode = global.conf->runmode;
67       
68        g_free( global.conf );
69        global.conf = conf_load( 0, NULL );
70       
71        if( global.conf->runmode != oldmode )
72        {
73                log_message( LOGLVL_WARNING, "Can't change RunMode setting at runtime, restoring original setting" );
74                global.conf->runmode = oldmode;
75        }
76       
77        if( global.conf->runmode == RUNMODE_FORKDAEMON )
78                ipc_to_children( cmd );
79}
80
81void ipc_master_cmd_restart( irc_t *data, char **cmd )
82{
83        struct bitlbee_child *child = (void*) data;
84       
85        if( global.conf->runmode != RUNMODE_FORKDAEMON )
86        {
87                /* Tell child that this is unsupported. */
88                return;
89        }
90       
91        global.restart = -1;
92        bitlbee_shutdown( NULL );
93}
94
95static const command_t ipc_master_commands[] = {
96        { "client",     3, ipc_master_cmd_client,     0 },
97        { "hello",      0, ipc_master_cmd_client,     0 },
98        { "die",        0, ipc_master_cmd_die,        0 },
99        { "wallops",    1, NULL,                      IPC_CMD_TO_CHILDREN },
100        { "lilo",       1, NULL,                      IPC_CMD_TO_CHILDREN },
101        { "opermsg",    1, NULL,                      IPC_CMD_TO_CHILDREN },
102        { "rehash",     0, ipc_master_cmd_rehash,     0 },
103        { "kill",       2, NULL,                      IPC_CMD_TO_CHILDREN },
104        { "restart",    0, ipc_master_cmd_restart,    0 },
105        { NULL }
106};
107
108
109static void ipc_child_cmd_die( irc_t *irc, char **cmd )
110{
111        irc_abort( irc, 0, "Shutdown requested by operator" );
112}
113
114static void ipc_child_cmd_wallops( irc_t *irc, char **cmd )
115{
116        if( irc->status < USTATUS_LOGGED_IN )
117                return;
118       
119        if( strchr( irc->umode, 'w' ) )
120                irc_write( irc, ":%s WALLOPS :%s", irc->myhost, cmd[1] );
121}
122
123static void ipc_child_cmd_lilo( irc_t *irc, char **cmd )
124{
125        if( irc->status < USTATUS_LOGGED_IN )
126                return;
127       
128        if( strchr( irc->umode, 's' ) )
129                irc_write( irc, ":%s NOTICE %s :%s", irc->myhost, irc->nick, cmd[1] );
130}
131
132static void ipc_child_cmd_opermsg( irc_t *irc, char **cmd )
133{
134        if( irc->status < USTATUS_LOGGED_IN )
135                return;
136       
137        if( strchr( irc->umode, 'o' ) )
138                irc_write( irc, ":%s NOTICE %s :*** OperMsg *** %s", irc->myhost, irc->nick, cmd[1] );
139}
140
141static void ipc_child_cmd_rehash( irc_t *irc, char **cmd )
142{
143        runmode_t oldmode;
144       
145        oldmode = global.conf->runmode;
146       
147        g_free( global.conf );
148        global.conf = conf_load( 0, NULL );
149       
150        global.conf->runmode = oldmode;
151}
152
153static void ipc_child_cmd_kill( irc_t *irc, char **cmd )
154{
155        if( irc->status < USTATUS_LOGGED_IN )
156                return;
157       
158        if( nick_cmp( cmd[1], irc->nick ) != 0 )
159                return;         /* It's not for us. */
160       
161        irc_write( irc, ":%s!%s@%s KILL %s :%s", irc->mynick, irc->mynick, irc->myhost, irc->nick, cmd[2] );
162        irc_abort( irc, 0, "Killed by operator: %s", cmd[2] );
163}
164
165static void ipc_child_cmd_hello( irc_t *irc, char **cmd )
166{
167        if( irc->status < USTATUS_LOGGED_IN )
168                ipc_to_master_str( "HELLO\r\n" );
169        else
170                ipc_to_master_str( "HELLO %s %s :%s\r\n", irc->host, irc->nick, irc->realname );
171}
172
173static const command_t ipc_child_commands[] = {
174        { "die",        0, ipc_child_cmd_die,         0 },
175        { "wallops",    1, ipc_child_cmd_wallops,     0 },
176        { "lilo",       1, ipc_child_cmd_lilo,        0 },
177        { "opermsg",    1, ipc_child_cmd_opermsg,     0 },
178        { "rehash",     0, ipc_child_cmd_rehash,      0 },
179        { "kill",       2, ipc_child_cmd_kill,        0 },
180        { "hello",      0, ipc_child_cmd_hello,       0 },
181        { NULL }
182};
183
184
185static void ipc_command_exec( void *data, char **cmd, const command_t *commands )
186{
187        int i, j;
188       
189        if( !cmd[0] )
190                return;
191       
192        for( i = 0; commands[i].command; i ++ )
193                if( g_strcasecmp( commands[i].command, cmd[0] ) == 0 )
194                {
195                        /* There is no typo in this line: */
196                        for( j = 1; cmd[j]; j ++ ); j --;
197                       
198                        if( j < commands[i].required_parameters )
199                                break;
200                       
201                        if( commands[i].flags & IPC_CMD_TO_CHILDREN )
202                                ipc_to_children( cmd );
203                        else
204                                commands[i].execute( data, cmd );
205                       
206                        break;
207                }
208}
209
210static char *ipc_readline( int fd )
211{
212        char *buf, *eol;
213        int size;
214       
215        buf = g_new0( char, 513 );
216       
217        /* Because this is internal communication, it should be pretty safe
218           to just peek at the message, find its length (by searching for the
219           end-of-line) and then just read that message. With internal
220           sockets and limites message length, messages should always be
221           complete. Saves us quite a lot of code and buffering. */
222        size = recv( fd, buf, 512, MSG_PEEK );
223        if( size == 0 || ( size < 0 && !sockerr_again() ) )
224                return NULL;
225        else if( size < 0 ) /* && sockerr_again() */
226                return( g_strdup( "" ) );
227        else
228                buf[size] = 0;
229       
230        eol = strstr( buf, "\r\n" );
231        if( eol == NULL )
232                return NULL;
233        else
234                size = eol - buf + 2;
235       
236        g_free( buf );
237        buf = g_new0( char, size + 1 );
238       
239        if( recv( fd, buf, size, 0 ) != size )
240                return NULL;
241        else
242                buf[size-2] = 0;
243       
244        return buf;
245}
246
247void ipc_master_read( gpointer data, gint source, GaimInputCondition cond )
248{
249        char *buf, **cmd;
250       
251        if( ( buf = ipc_readline( source ) ) )
252        {
253                cmd = irc_parse_line( buf );
254                if( cmd )
255                        ipc_command_exec( data, cmd, ipc_master_commands );
256        }
257        else
258        {
259                GSList *l;
260                struct bitlbee_child *c;
261               
262                for( l = child_list; l; l = l->next )
263                {
264                        c = l->data;
265                        if( c->ipc_fd == source )
266                        {
267                                ipc_master_free_one( c );
268                                child_list = g_slist_remove( child_list, c );
269                                break;
270                        }
271                }
272        }
273}
274
275void ipc_child_read( gpointer data, gint source, GaimInputCondition cond )
276{
277        char *buf, **cmd;
278       
279        if( ( buf = ipc_readline( source ) ) )
280        {
281                cmd = irc_parse_line( buf );
282                if( cmd )
283                        ipc_command_exec( data, cmd, ipc_child_commands );
284        }
285        else
286        {
287                gaim_input_remove( global.listen_watch_source_id );
288                close( global.listen_socket );
289               
290                global.listen_socket = -1;
291        }
292}
293
294void ipc_to_master( char **cmd )
295{
296        if( global.conf->runmode == RUNMODE_FORKDAEMON )
297        {
298                char *s = irc_build_line( cmd );
299                ipc_to_master_str( "%s", s );
300                g_free( s );
301        }
302        else if( global.conf->runmode == RUNMODE_DAEMON )
303        {
304                ipc_command_exec( NULL, cmd, ipc_master_commands );
305        }
306}
307
308void ipc_to_master_str( char *format, ... )
309{
310        char *msg_buf;
311        va_list params;
312
313        va_start( params, format );
314        msg_buf = g_strdup_vprintf( format, params );
315        va_end( params );
316       
317        if( strlen( msg_buf ) > 512 )
318        {
319                /* Don't send it, it's too long... */
320        }
321        else if( global.conf->runmode == RUNMODE_FORKDAEMON )
322        {
323                write( global.listen_socket, msg_buf, strlen( msg_buf ) );
324        }
325        else if( global.conf->runmode == RUNMODE_DAEMON )
326        {
327                char **cmd, *s;
328               
329                if( ( s = strchr( msg_buf, '\r' ) ) )
330                        *s = 0;
331               
332                cmd = irc_parse_line( msg_buf );
333                ipc_command_exec( NULL, cmd, ipc_master_commands );
334                g_free( cmd );
335        }
336       
337        g_free( msg_buf );
338}
339
340void ipc_to_children( char **cmd )
341{
342        if( global.conf->runmode == RUNMODE_FORKDAEMON )
343        {
344                char *msg_buf = irc_build_line( cmd );
345                ipc_to_children_str( "%s", msg_buf );
346                g_free( msg_buf );
347        }
348        else if( global.conf->runmode == RUNMODE_DAEMON )
349        {
350                GSList *l;
351               
352                for( l = irc_connection_list; l; l = l->next )
353                        ipc_command_exec( l->data, cmd, ipc_child_commands );
354        }
355}
356
357void ipc_to_children_str( char *format, ... )
358{
359        char *msg_buf;
360        va_list params;
361
362        va_start( params, format );
363        msg_buf = g_strdup_vprintf( format, params );
364        va_end( params );
365       
366        if( strlen( msg_buf ) > 512 )
367        {
368                /* Don't send it, it's too long... */
369        }
370        else if( global.conf->runmode == RUNMODE_FORKDAEMON )
371        {
372                int msg_len = strlen( msg_buf );
373                GSList *l;
374               
375                for( l = child_list; l; l = l->next )
376                {
377                        struct bitlbee_child *c = l->data;
378                        write( c->ipc_fd, msg_buf, msg_len );
379                }
380        }
381        else if( global.conf->runmode == RUNMODE_DAEMON )
382        {
383                char **cmd, *s;
384               
385                if( ( s = strchr( msg_buf, '\r' ) ) )
386                        *s = 0;
387               
388                cmd = irc_parse_line( msg_buf );
389                ipc_to_children( cmd );
390                g_free( cmd );
391        }
392       
393        g_free( msg_buf );
394}
395
396void ipc_master_free_one( struct bitlbee_child *c )
397{
398        gaim_input_remove( c->ipc_inpa );
399        closesocket( c->ipc_fd );
400       
401        g_free( c->host );
402        g_free( c->nick );
403        g_free( c->realname );
404        g_free( c );
405}
406
407void ipc_master_free_all()
408{
409        GSList *l;
410       
411        for( l = child_list; l; l = l->next )
412                ipc_master_free_one( l->data );
413       
414        g_slist_free( child_list );
415        child_list = NULL;
416}
417
418char *ipc_master_save_state()
419{
420        char *fn = g_strdup( "/tmp/bee-restart.XXXXXX" );
421        int fd = mkstemp( fn );
422        GSList *l;
423        FILE *fp;
424        int i;
425       
426        if( fd == -1 )
427        {
428                log_message( LOGLVL_ERROR, "Could not create temporary file: %s", strerror( errno ) );
429                g_free( fn );
430                return NULL;
431        }
432       
433        /* This is more convenient now. */
434        fp = fdopen( fd, "w" );
435       
436        for( l = child_list, i = 0; l; l = l->next )
437                i ++;
438       
439        /* Number of client processes. */
440        fprintf( fp, "%d\n", i );
441       
442        for( l = child_list; l; l = l->next )
443                fprintf( fp, "%d %d\n", ((struct bitlbee_child*)l->data)->pid,
444                                        ((struct bitlbee_child*)l->data)->ipc_fd );
445       
446        if( fclose( fp ) == 0 )
447        {
448                return fn;
449        }
450        else
451        {
452                unlink( fn );
453                g_free( fn );
454                return NULL;
455        }
456}
457
458void ipc_master_set_statefile( char *fn )
459{
460        statefile = g_strdup( fn );
461}
462
463int ipc_master_load_state()
464{
465        struct bitlbee_child *child;
466        FILE *fp;
467        int i, n;
468       
469        if( statefile == NULL )
470                return 0;
471        fp = fopen( statefile, "r" );
472        unlink( statefile );    /* Why do it later? :-) */
473        if( fp == NULL )
474                return 0;
475       
476        if( fscanf( fp, "%d", &n ) != 1 )
477        {
478                log_message( LOGLVL_WARNING, "Could not import state information for child processes." );
479                fclose( fp );
480                return 0;
481        }
482       
483        log_message( LOGLVL_INFO, "Importing information for %d child processes.", n );
484        for( i = 0; i < n; i ++ )
485        {
486                child = g_new0( struct bitlbee_child, 1 );
487               
488                if( fscanf( fp, "%d %d", &child->pid, &child->ipc_fd ) != 2 )
489                {
490                        log_message( LOGLVL_WARNING, "Unexpected end of file: Only processed %d clients.", i );
491                        g_free( child );
492                        fclose( fp );
493                        return 0;
494                }
495                child->ipc_inpa = gaim_input_add( child->ipc_fd, GAIM_INPUT_READ, ipc_master_read, child );
496               
497                child_list = g_slist_append( child_list, child );
498        }
499       
500        ipc_to_children_str( "HELLO\r\n" );
501        ipc_to_children_str( "OPERMSG :New BitlBee master process started (version " BITLBEE_VERSION ")\r\n" );
502       
503        return 1;
504}
Note: See TracBrowser for help on using the repository browser.