source: ipc.c @ a4dc9f7

Last change on this file since a4dc9f7 was 9a1555d, checked in by Jelmer Vernooij <jelmer@…>, at 2006-03-01T22:35:50Z

[merge] wilmer

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