source: ipc.c @ 6dff9d4

Last change on this file since 6dff9d4 was 6dff9d4, checked in by Jelmer Vernooij <jelmer@…>, at 2006-03-01T21:08:03Z

Also listen for admin connections on a unix domain socket at /var/run/bitlbee

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