source: ipc.c @ 7ea8697

Last change on this file since 7ea8697 was f5d1b31, checked in by Wilmer van der Gaast <wilmer@…>, at 2008-06-29T23:41:39Z

Merging changes from Jelmer: It's now possible to cross-compile a Windows
version of BitlBee from Linux. No working SSL support yet though!

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