source: ipc.c @ da7b484

Last change on this file since da7b484 was da7b484, checked in by Wilmer van der Gaast <wilmer@…>, at 2008-06-21T17:09:20Z

Fixed memory leaking ipc_readline().

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