source: ipc.c @ 178e2f8

Last change on this file since 178e2f8 was 178e2f8, checked in by Jelmer Vernooij <jelmer@…>, at 2008-06-28T17:32:41Z

Merge trunk.

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