source: ipc.c @ 1145964

Last change on this file since 1145964 was 565a1ea, checked in by Wilmer van der Gaast <wilmer@…>, at 2008-06-29T09:35:41Z

Added the DEAF command, which makes the daemon stop listening for new
connections. This makes it easier to upgrade a BitlBee without having
to disconnect all current users immediately. Closes #428.

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