source: ipc.c @ 5a61bf59

Last change on this file since 5a61bf59 was 3709301, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-07-14T23:52:11Z

Merging *BSD compatibility fix from Doug Luce (include sys/uio.h even
though the sendmsg() manpage says this isn't necessary).

  • Property mode set to 100644
File size: 24.0 KB
RevLine 
[0431ea1]1  /********************************************************************\
2  * BitlBee -- An IRC to other IM-networks gateway                     *
3  *                                                                    *
[3709301]4  * Copyright 2002-2010 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
[3709301]31#include <sys/uio.h>
[6dff9d4]32#include <sys/un.h>
33#endif
[0431ea1]34
35GSList *child_list = NULL;
[0b09da0]36static int ipc_child_recv_fd = -1;
[74c119d]37
[c5bff81]38static void ipc_master_takeover_fail( struct bitlbee_child *child, gboolean both );
39static gboolean ipc_send_fd( int fd, int send_fd );
40
[f73b969]41static void ipc_master_cmd_client( irc_t *data, char **cmd )
[2face62]42{
[5e713f6]43        /* Normally data points at an irc_t block, but for the IPC master
44           this is different. We think this scary cast is better than
45           creating a new command_t structure, just to make the compiler
46           happy. */
[2face62]47        struct bitlbee_child *child = (void*) data;
48       
[54879ab]49        if( child && cmd[1] )
[bd9b00f]50        {
51                child->host = g_strdup( cmd[1] );
52                child->nick = g_strdup( cmd[2] );
53                child->realname = g_strdup( cmd[3] );
54        }
[2face62]55       
[debe871]56        /* CLIENT == On initial connects, HELLO is after /RESTARTs. */
[54879ab]57        if( g_strcasecmp( cmd[0], "CLIENT" ) == 0 )
58                ipc_to_children_str( "OPERMSG :Client connecting (PID=%d): %s@%s (%s)\r\n",
[52744f8]59                                     (int) ( child ? child->pid : -1 ), cmd[2], cmd[1], cmd[3] );
[2face62]60}
61
[debe871]62static void ipc_master_cmd_nick( irc_t *data, char **cmd )
63{
64        struct bitlbee_child *child = (void*) data;
65       
66        if( child && cmd[1] )
67        {
68                g_free( child->nick );
69                child->nick = g_strdup( cmd[1] );
70        }
71}
72
[f73b969]73static void ipc_master_cmd_die( irc_t *data, char **cmd )
[0431ea1]74{
[74c119d]75        if( global.conf->runmode == RUNMODE_FORKDAEMON )
76                ipc_to_children_str( "DIE\r\n" );
77       
[ba9edaa]78        bitlbee_shutdown( NULL, -1, 0 );
[0431ea1]79}
80
[565a1ea]81static void ipc_master_cmd_deaf( irc_t *data, char **cmd )
82{
83        if( global.conf->runmode == RUNMODE_DAEMON )
84        {
85                b_event_remove( global.listen_watch_source_id );
86                close( global.listen_socket );
87               
88                global.listen_socket = global.listen_watch_source_id = -1;
89       
90                ipc_to_children_str( "OPERMSG :Closed listening socket, waiting "
91                                     "for all users to disconnect." );
92        }
93        else
94        {
95                ipc_to_children_str( "OPERMSG :The DEAF command only works in "
96                                     "normal daemon mode. Try DIE instead." );
97        }
98}
99
[f73b969]100void ipc_master_cmd_rehash( irc_t *data, char **cmd )
[0431ea1]101{
[f4a5940]102        runmode_t oldmode;
103       
104        oldmode = global.conf->runmode;
105       
106        g_free( global.conf );
107        global.conf = conf_load( 0, NULL );
108       
109        if( global.conf->runmode != oldmode )
110        {
111                log_message( LOGLVL_WARNING, "Can't change RunMode setting at runtime, restoring original setting" );
112                global.conf->runmode = oldmode;
113        }
114       
115        if( global.conf->runmode == RUNMODE_FORKDAEMON )
116                ipc_to_children( cmd );
[0431ea1]117}
118
[54879ab]119void ipc_master_cmd_restart( irc_t *data, char **cmd )
120{
121        if( global.conf->runmode != RUNMODE_FORKDAEMON )
122        {
123                /* Tell child that this is unsupported. */
124                return;
125        }
126       
127        global.restart = -1;
[ba9edaa]128        bitlbee_shutdown( NULL, -1, 0 );
[54879ab]129}
130
[6c2404e]131void ipc_master_cmd_identify( irc_t *data, char **cmd )
132{
133        struct bitlbee_child *child = (void*) data, *old = NULL;
[0b09da0]134        char *resp;
[6c2404e]135        GSList *l;
136       
[133cdff]137        if( !child || !child->nick || strcmp( child->nick, cmd[1] ) != 0 )
[6c2404e]138                return;
139       
140        g_free( child->password );
141        child->password = g_strdup( cmd[2] );
142       
143        for( l = child_list; l; l = l->next )
144        {
145                old = l->data;
[133cdff]146                if( child != old &&
147                    old->nick && nick_cmp( old->nick, child->nick ) == 0 &&
[0b09da0]148                    old->password && strcmp( old->password, child->password ) == 0 )
[6c2404e]149                        break;
150        }
151       
[f545372]152        if( l && !child->to_child && !old->to_child )
[0b09da0]153        {
154                resp = "TAKEOVER INIT\r\n";
[f545372]155                child->to_child = old;
156                old->to_child = child;
[0b09da0]157        }
158        else
159        {
160                /* Won't need the fd since we can't send it anywhere. */
[c5bff81]161                closesocket( child->to_fd );
[0b09da0]162                child->to_fd = -1;
163                resp = "TAKEOVER NO\r\n";
164        }
165       
166        if( write( child->ipc_fd, resp, strlen( resp ) ) != strlen( resp ) )
167                ipc_master_free_one( child );
168}
169
170
171void ipc_master_cmd_takeover( irc_t *data, char **cmd )
172{
173        struct bitlbee_child *child = (void*) data;
[f545372]174        char *fwd = NULL;
[0b09da0]175       
[e92c4f4]176        /* Normal daemon mode doesn't keep these and has simplified code for
177           takeovers. */
178        if( child == NULL )
179                return;
180       
[c5bff81]181        if( child->to_child == NULL ||
182            g_slist_find( child_list, child->to_child ) == NULL )
183                return ipc_master_takeover_fail( child, FALSE );
184       
[0b09da0]185        if( strcmp( cmd[1], "AUTH" ) == 0 )
186        {
[c5bff81]187                /* New connection -> Master */
[0b09da0]188                if( child->to_child &&
189                    child->nick && child->to_child->nick && cmd[2] &&
190                    child->password && child->to_child->password && cmd[3] &&
191                    strcmp( child->nick, child->to_child->nick ) == 0 &&
192                    strcmp( child->nick, cmd[2] ) == 0 &&
193                    strcmp( child->password, child->to_child->password ) == 0 &&
194                    strcmp( child->password, cmd[3] ) == 0 )
195                {
196                        ipc_send_fd( child->to_child->ipc_fd, child->to_fd );
197                       
[f545372]198                        fwd = irc_build_line( cmd );
199                        if( write( child->to_child->ipc_fd, fwd, strlen( fwd ) ) != strlen( fwd ) )
[0b09da0]200                                ipc_master_free_one( child );
[f545372]201                        g_free( fwd );
[0b09da0]202                }
[c5bff81]203                else
204                        return ipc_master_takeover_fail( child, TRUE );
[0b09da0]205        }
[f545372]206        else if( strcmp( cmd[1], "DONE" ) == 0 || strcmp( cmd[1], "FAIL" ) == 0 )
207        {
[c5bff81]208                /* Old connection -> Master */
[f545372]209                int fd;
210               
211                /* The copy was successful (or not), we don't need it anymore. */
[c5bff81]212                closesocket( child->to_fd );
[f545372]213                child->to_fd = -1;
214               
215                /* Pass it through to the other party, and flush all state. */
216                fwd = irc_build_line( cmd );
217                fd = child->to_child->ipc_fd;
218                child->to_child->to_child = NULL;
219                child->to_child = NULL;
220                if( write( fd, fwd, strlen( fwd ) ) != strlen( fwd ) )
221                        ipc_master_free_one( child );
222                g_free( fwd );
223        }
[6c2404e]224}
225
[0431ea1]226static const command_t ipc_master_commands[] = {
[2face62]227        { "client",     3, ipc_master_cmd_client,     0 },
[54879ab]228        { "hello",      0, ipc_master_cmd_client,     0 },
[debe871]229        { "nick",       1, ipc_master_cmd_nick,       0 },
[0431ea1]230        { "die",        0, ipc_master_cmd_die,        0 },
[565a1ea]231        { "deaf",       0, ipc_master_cmd_deaf,       0 },
[f4a5940]232        { "wallops",    1, NULL,                      IPC_CMD_TO_CHILDREN },
[dfc8a46]233        { "wall",       1, NULL,                      IPC_CMD_TO_CHILDREN },
[2face62]234        { "opermsg",    1, NULL,                      IPC_CMD_TO_CHILDREN },
[f4a5940]235        { "rehash",     0, ipc_master_cmd_rehash,     0 },
[48721c3]236        { "kill",       2, NULL,                      IPC_CMD_TO_CHILDREN },
[54879ab]237        { "restart",    0, ipc_master_cmd_restart,    0 },
[6c2404e]238        { "identify",   2, ipc_master_cmd_identify,   0 },
[0b09da0]239        { "takeover",   1, ipc_master_cmd_takeover,   0 },
[0431ea1]240        { NULL }
241};
242
[74c119d]243
[f73b969]244static void ipc_child_cmd_die( irc_t *irc, char **cmd )
[74c119d]245{
[87de505]246        irc_abort( irc, 0, "Shutdown requested by operator" );
[74c119d]247}
248
[f73b969]249static void ipc_child_cmd_wallops( irc_t *irc, char **cmd )
[0431ea1]250{
[3af70b0]251        if( !( irc->status & USTATUS_LOGGED_IN ) )
[f73b969]252                return;
[daa9e02]253       
[0431ea1]254        if( strchr( irc->umode, 'w' ) )
[3ddb7477]255                irc_write( irc, ":%s WALLOPS :%s", irc->root->host, cmd[1] );
[e0ca412]256}
257
[dfc8a46]258static void ipc_child_cmd_wall( irc_t *irc, char **cmd )
[e0ca412]259{
[3af70b0]260        if( !( irc->status & USTATUS_LOGGED_IN ) )
[f73b969]261                return;
[daa9e02]262       
[74c119d]263        if( strchr( irc->umode, 's' ) )
[3ddb7477]264                irc_write( irc, ":%s NOTICE %s :%s", irc->root->host, irc->user->nick, cmd[1] );
[0431ea1]265}
266
[f73b969]267static void ipc_child_cmd_opermsg( irc_t *irc, char **cmd )
[2face62]268{
[3af70b0]269        if( !( irc->status & USTATUS_LOGGED_IN ) )
[f73b969]270                return;
[2face62]271       
272        if( strchr( irc->umode, 'o' ) )
[3ddb7477]273                irc_write( irc, ":%s NOTICE %s :*** OperMsg *** %s", irc->root->host, irc->user->nick, cmd[1] );
[2face62]274}
275
[f73b969]276static void ipc_child_cmd_rehash( irc_t *irc, char **cmd )
[f4a5940]277{
278        runmode_t oldmode;
279       
280        oldmode = global.conf->runmode;
281       
282        g_free( global.conf );
283        global.conf = conf_load( 0, NULL );
284       
285        global.conf->runmode = oldmode;
286}
287
[f73b969]288static void ipc_child_cmd_kill( irc_t *irc, char **cmd )
[48721c3]289{
[3af70b0]290        if( !( irc->status & USTATUS_LOGGED_IN ) )
[f73b969]291                return;
[48721c3]292       
[3ddb7477]293        if( nick_cmp( cmd[1], irc->user->nick ) != 0 )
[f73b969]294                return;         /* It's not for us. */
[48721c3]295       
[3ddb7477]296        irc_write( irc, ":%s!%s@%s KILL %s :%s", irc->root->nick, irc->root->nick, irc->root->host, irc->user->nick, cmd[2] );
[f73b969]297        irc_abort( irc, 0, "Killed by operator: %s", cmd[2] );
[48721c3]298}
299
[54879ab]300static void ipc_child_cmd_hello( irc_t *irc, char **cmd )
301{
[3af70b0]302        if( !( irc->status & USTATUS_LOGGED_IN ) )
[54879ab]303                ipc_to_master_str( "HELLO\r\n" );
304        else
[3ddb7477]305                ipc_to_master_str( "HELLO %s %s :%s\r\n", irc->user->host, irc->user->nick, irc->user->fullname );
[54879ab]306}
307
[f545372]308static void ipc_child_cmd_takeover_yes( void *data );
309static void ipc_child_cmd_takeover_no( void *data );
310
[0b09da0]311static void ipc_child_cmd_takeover( irc_t *irc, char **cmd )
312{
313        if( strcmp( cmd[1], "NO" ) == 0 )
314        {
[f545372]315                /* Master->New connection */
[0b09da0]316                /* No takeover, finish the login. */
317        }
318        else if( strcmp( cmd[1], "INIT" ) == 0 )
319        {
[f545372]320                /* Master->New connection */
[af9f2ca]321                if( !set_getbool( &irc->b->set, "allow_takeover" ) )
322                {
323                        ipc_child_cmd_takeover_no( irc );
324                        return;
325                }
326               
[f545372]327                /* Offer to take over the old session, unless for some reason
328                   we're already logging into IM connections. */
329                if( irc->login_source_id != -1 )
330                        query_add( irc, NULL,
331                                   "You're already connected to this server. "
332                                   "Would you like to take over this session?",
333                                   ipc_child_cmd_takeover_yes,
[1e52e1f]334                                   ipc_child_cmd_takeover_no, NULL, irc );
[0b09da0]335               
[f545372]336                /* This one's going to connect to accounts, avoid that. */
337                b_event_remove( irc->login_source_id );
338                irc->login_source_id = -1;
[0b09da0]339        }
340        else if( strcmp( cmd[1], "AUTH" ) == 0 )
341        {
[f545372]342                /* Master->Old connection */
[0b09da0]343                if( irc->password && cmd[2] && cmd[3] &&
344                    ipc_child_recv_fd != -1 &&
345                    strcmp( irc->user->nick, cmd[2] ) == 0 &&
[af9f2ca]346                    strcmp( irc->password, cmd[3] ) == 0 &&
347                    set_getbool( &irc->b->set, "allow_takeover" ) )
[0b09da0]348                {
[f1c2b21]349                        irc_switch_fd( irc, ipc_child_recv_fd );
350                        irc_sync( irc );
[f545372]351                        irc_usermsg( irc, "You've successfully taken over your old session" );
[f1c2b21]352                        ipc_child_recv_fd = -1;
[f545372]353                       
354                        ipc_to_master_str( "TAKEOVER DONE\r\n" );
[0b09da0]355                }
[f545372]356                else
357                {
358                        ipc_to_master_str( "TAKEOVER FAIL\r\n" );
359                }
360        }
361        else if( strcmp( cmd[1], "DONE" ) == 0 ) 
362        {
363                /* Master->New connection (now taken over by old process) */
364                irc_free( irc );
365        }
366        else if( strcmp( cmd[1], "FAIL" ) == 0 ) 
367        {
368                /* Master->New connection */
369                irc_usermsg( irc, "Could not take over old session" );
[0b09da0]370        }
371}
372
[f545372]373static void ipc_child_cmd_takeover_yes( void *data )
374{
[e92c4f4]375        irc_t *irc = data, *old = NULL;
376        char *to_auth[] = { "TAKEOVER", "AUTH", irc->user->nick, irc->password, NULL };
[f545372]377       
378        /* Master->New connection */
379        ipc_to_master_str( "TAKEOVER AUTH %s :%s\r\n",
380                           irc->user->nick, irc->password );
381       
[e92c4f4]382        if( global.conf->runmode == RUNMODE_DAEMON )
383        {
384                GSList *l;
385               
386                for( l = irc_connection_list; l; l = l->next )
387                {
388                        old = l->data;
389                       
390                        if( irc != old &&
391                            irc->user->nick && old->user->nick &&
392                            irc->password && old->password &&
393                            strcmp( irc->user->nick, old->user->nick ) == 0 &&
394                            strcmp( irc->password, old->password ) == 0 )
395                                break;
396                }
397                if( l == NULL )
398                {
399                        to_auth[1] = "FAIL";
400                        ipc_child_cmd_takeover( irc, to_auth );
401                        return;
402                }
403        }
404       
[f545372]405        /* Drop credentials, we'll shut down soon and shouldn't overwrite
406           any settings. */
407        irc_usermsg( irc, "Trying to take over existing session" );
408       
[e92c4f4]409        irc_desync( irc );
410       
411        if( old )
412        {
413                ipc_child_recv_fd = dup( irc->fd );
414                ipc_child_cmd_takeover( old, to_auth );
415        }
416       
[f545372]417        /* TODO: irc_setpass() should do all of this. */
418        irc_setpass( irc, NULL );
419        irc->status &= ~USTATUS_IDENTIFIED;
420        irc_umode_set( irc, "-R", 1 );
421       
[e92c4f4]422        if( old )
423        {
424                irc->status |= USTATUS_SHUTDOWN;
425                irc_abort( irc, FALSE, NULL );
426        }
[f545372]427}
428
429static void ipc_child_cmd_takeover_no( void *data )
430{
431        ipc_to_master_str( "TAKEOVER NO\r\n" );
432        cmd_identify_finish( data, 0, 0 );
433}
434
[0431ea1]435static const command_t ipc_child_commands[] = {
[74c119d]436        { "die",        0, ipc_child_cmd_die,         0 },
437        { "wallops",    1, ipc_child_cmd_wallops,     0 },
[dfc8a46]438        { "wall",       1, ipc_child_cmd_wall,        0 },
[2face62]439        { "opermsg",    1, ipc_child_cmd_opermsg,     0 },
[48721c3]440        { "rehash",     0, ipc_child_cmd_rehash,      0 },
441        { "kill",       2, ipc_child_cmd_kill,        0 },
[54879ab]442        { "hello",      0, ipc_child_cmd_hello,       0 },
[0b09da0]443        { "takeover",   1, ipc_child_cmd_takeover,    0 },
[0431ea1]444        { NULL }
445};
446
[6c2404e]447gboolean ipc_child_identify( irc_t *irc )
448{
449        if( global.conf->runmode == RUNMODE_FORKDAEMON )
450        {
451                if( !ipc_send_fd( global.listen_socket, irc->fd ) )
452                        ipc_child_disable();
453       
454                ipc_to_master_str( "IDENTIFY %s :%s\r\n", irc->user->nick, irc->password );
455               
456                return TRUE;
457        }
[e92c4f4]458        else if( global.conf->runmode == RUNMODE_DAEMON )
459        {
460                GSList *l;
461                irc_t *old;
462                char *to_init[] = { "TAKEOVER", "INIT", NULL };
463               
464                for( l = irc_connection_list; l; l = l->next )
465                {
466                        old = l->data;
467                       
468                        if( irc != old &&
469                            irc->user->nick && old->user->nick &&
470                            irc->password && old->password &&
471                            strcmp( irc->user->nick, old->user->nick ) == 0 &&
472                            strcmp( irc->password, old->password ) == 0 )
473                                break;
474                }
[af9f2ca]475                if( l == NULL ||
476                    !set_getbool( &irc->b->set, "allow_takeover" ) ||
477                    !set_getbool( &old->b->set, "allow_takeover" ) )
[e92c4f4]478                        return FALSE;
479               
480                ipc_child_cmd_takeover( irc, to_init );
481               
482                return TRUE;
483        }
[6c2404e]484        else
485                return FALSE;
486}
[74c119d]487
[c5bff81]488static void ipc_master_takeover_fail( struct bitlbee_child *child, gboolean both )
489{
490        if( child == NULL || g_slist_find( child_list, child ) == NULL )
491                return;
492       
493        if( both && child->to_child != NULL )
494                ipc_master_takeover_fail( child->to_child, FALSE );
495       
496        if( child->to_fd > -1 )
497        {
498                /* Send this error only to the new connection, which can be
499                   recognised by to_fd being set. */
500                if( write( child->ipc_fd, "TAKEOVER FAIL\r\n", 15 ) != 15 )
501                {
502                        ipc_master_free_one( child );
503                        return;
504                }
505                close( child->to_fd );
506                child->to_fd = -1;
507        }
508        child->to_child = NULL;
509}
510
[0431ea1]511static void ipc_command_exec( void *data, char **cmd, const command_t *commands )
512{
[f1d38f2]513        int i, j;
[0431ea1]514       
515        if( !cmd[0] )
516                return;
517       
518        for( i = 0; commands[i].command; i ++ )
519                if( g_strcasecmp( commands[i].command, cmd[0] ) == 0 )
520                {
[f1d38f2]521                        /* There is no typo in this line: */
522                        for( j = 1; cmd[j]; j ++ ); j --;
523                       
524                        if( j < commands[i].required_parameters )
525                                break;
526                       
[f4a5940]527                        if( commands[i].flags & IPC_CMD_TO_CHILDREN )
528                                ipc_to_children( cmd );
529                        else
530                                commands[i].execute( data, cmd );
531                       
[f1d38f2]532                        break;
[0431ea1]533                }
534}
535
[da7b484]536/* Return just one line. Returns NULL if something broke, an empty string
537   on temporary "errors" (EAGAIN and friends). */
[6c2404e]538static char *ipc_readline( int fd, int *recv_fd )
[0431ea1]539{
[6c2404e]540        struct msghdr msg;
541        struct iovec iov;
542        char ccmsg[CMSG_SPACE(sizeof(recv_fd))];
543        struct cmsghdr *cmsg;
[da7b484]544        char buf[513], *eol;
[0431ea1]545        int size;
546       
547        /* Because this is internal communication, it should be pretty safe
548           to just peek at the message, find its length (by searching for the
549           end-of-line) and then just read that message. With internal
550           sockets and limites message length, messages should always be
551           complete. Saves us quite a lot of code and buffering. */
[da7b484]552        size = recv( fd, buf, sizeof( buf ) - 1, MSG_PEEK );
[0431ea1]553        if( size == 0 || ( size < 0 && !sockerr_again() ) )
554                return NULL;
[1ea13be]555        else if( size < 0 ) /* && sockerr_again() */
556                return( g_strdup( "" ) );
[0431ea1]557        else
558                buf[size] = 0;
559       
[da7b484]560        if( ( eol = strstr( buf, "\r\n" ) ) == NULL )
[0431ea1]561                return NULL;
562        else
563                size = eol - buf + 2;
564       
[6c2404e]565        iov.iov_base = buf;
566        iov.iov_len = size;
567       
568        memset( &msg, 0, sizeof( msg ) );
569        msg.msg_iov = &iov;
570        msg.msg_iovlen = 1;
571        msg.msg_control = ccmsg;
572        msg.msg_controllen = sizeof( ccmsg );
573       
574        if( recvmsg( fd, &msg, 0 ) != size )
[0431ea1]575                return NULL;
[6c2404e]576       
577        if( recv_fd )
578                for( cmsg = CMSG_FIRSTHDR( &msg ); cmsg; cmsg = CMSG_NXTHDR( &msg, cmsg ) )
579                        if( cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS )
580                        {
581                                /* Getting more than one shouldn't happen but if it does,
582                                   make sure we don't leave them around. */
583                                if( *recv_fd != -1 )
584                                        close( *recv_fd );
585                               
586                                *recv_fd = *(int*) CMSG_DATA( cmsg );
[e92c4f4]587                                /*
[0b09da0]588                                fprintf( stderr, "pid %d received fd %d\n", (int) getpid(), *recv_fd );
[e92c4f4]589                                */
[6c2404e]590                        }
591       
[e92c4f4]592        /*
[0b09da0]593        fprintf( stderr, "pid %d received: %s", (int) getpid(), buf );
[e92c4f4]594        */
[6c2404e]595        return g_strndup( buf, size - 2 );
[0431ea1]596}
597
[ba9edaa]598gboolean ipc_master_read( gpointer data, gint source, b_input_condition cond )
[0431ea1]599{
[6c2404e]600        struct bitlbee_child *child = data;
[0431ea1]601        char *buf, **cmd;
602       
[6c2404e]603        if( ( buf = ipc_readline( source, &child->to_fd ) ) )
[0431ea1]604        {
605                cmd = irc_parse_line( buf );
606                if( cmd )
[edc767b]607                {
[6c2404e]608                        ipc_command_exec( child, cmd, ipc_master_commands );
[edc767b]609                        g_free( cmd );
610                }
611                g_free( buf );
[0431ea1]612        }
613        else
614        {
[171ef85]615                ipc_master_free_fd( source );
[0431ea1]616        }
[ba9edaa]617       
618        return TRUE;
[0431ea1]619}
620
[ba9edaa]621gboolean ipc_child_read( gpointer data, gint source, b_input_condition cond )
[0431ea1]622{
623        char *buf, **cmd;
624       
[0b09da0]625        if( ( buf = ipc_readline( source, &ipc_child_recv_fd ) ) )
[0431ea1]626        {
627                cmd = irc_parse_line( buf );
628                if( cmd )
[edc767b]629                {
[0431ea1]630                        ipc_command_exec( data, cmd, ipc_child_commands );
[edc767b]631                        g_free( cmd );
632                }
633                g_free( buf );
[0431ea1]634        }
635        else
636        {
[171ef85]637                ipc_child_disable();
[0431ea1]638        }
[ba9edaa]639       
640        return TRUE;
[0431ea1]641}
642
643void ipc_to_master( char **cmd )
644{
645        if( global.conf->runmode == RUNMODE_FORKDAEMON )
646        {
[74c119d]647                char *s = irc_build_line( cmd );
[2face62]648                ipc_to_master_str( "%s", s );
[f4a5940]649                g_free( s );
650        }
651        else if( global.conf->runmode == RUNMODE_DAEMON )
652        {
653                ipc_command_exec( NULL, cmd, ipc_master_commands );
654        }
655}
656
[2face62]657void ipc_to_master_str( char *format, ... )
[f4a5940]658{
[2face62]659        char *msg_buf;
660        va_list params;
661
662        va_start( params, format );
663        msg_buf = g_strdup_vprintf( format, params );
664        va_end( params );
665       
666        if( strlen( msg_buf ) > 512 )
667        {
668                /* Don't send it, it's too long... */
669        }
670        else if( global.conf->runmode == RUNMODE_FORKDAEMON )
[f4a5940]671        {
[171ef85]672                if( global.listen_socket >= 0 )
673                        if( write( global.listen_socket, msg_buf, strlen( msg_buf ) ) <= 0 )
674                                ipc_child_disable();
[f4a5940]675        }
676        else if( global.conf->runmode == RUNMODE_DAEMON )
677        {
[bd9b00f]678                char **cmd, *s;
679               
680                if( ( s = strchr( msg_buf, '\r' ) ) )
681                        *s = 0;
[f4a5940]682               
[2face62]683                cmd = irc_parse_line( msg_buf );
[f4a5940]684                ipc_command_exec( NULL, cmd, ipc_master_commands );
685                g_free( cmd );
[74c119d]686        }
[2face62]687       
688        g_free( msg_buf );
[74c119d]689}
690
691void ipc_to_children( char **cmd )
692{
693        if( global.conf->runmode == RUNMODE_FORKDAEMON )
694        {
695                char *msg_buf = irc_build_line( cmd );
[2face62]696                ipc_to_children_str( "%s", msg_buf );
[74c119d]697                g_free( msg_buf );
698        }
[f4a5940]699        else if( global.conf->runmode == RUNMODE_DAEMON )
700        {
701                GSList *l;
702               
703                for( l = irc_connection_list; l; l = l->next )
704                        ipc_command_exec( l->data, cmd, ipc_child_commands );
705        }
[74c119d]706}
707
[2face62]708void ipc_to_children_str( char *format, ... )
[74c119d]709{
[2face62]710        char *msg_buf;
711        va_list params;
712
713        va_start( params, format );
714        msg_buf = g_strdup_vprintf( format, params );
715        va_end( params );
716       
717        if( strlen( msg_buf ) > 512 )
718        {
719                /* Don't send it, it's too long... */
720        }
721        else if( global.conf->runmode == RUNMODE_FORKDAEMON )
[74c119d]722        {
723                int msg_len = strlen( msg_buf );
[171ef85]724                GSList *l, *next;
[0431ea1]725               
[171ef85]726                for( l = child_list; l; l = next )
[0431ea1]727                {
[74c119d]728                        struct bitlbee_child *c = l->data;
[171ef85]729                       
730                        next = l->next;
731                        if( write( c->ipc_fd, msg_buf, msg_len ) <= 0 )
732                                ipc_master_free_one( c );
[0431ea1]733                }
734        }
[f4a5940]735        else if( global.conf->runmode == RUNMODE_DAEMON )
736        {
[bd9b00f]737                char **cmd, *s;
738               
739                if( ( s = strchr( msg_buf, '\r' ) ) )
740                        *s = 0;
[f4a5940]741               
[2face62]742                cmd = irc_parse_line( msg_buf );
[f4a5940]743                ipc_to_children( cmd );
744                g_free( cmd );
745        }
[2face62]746       
747        g_free( msg_buf );
748}
749
[6c2404e]750static gboolean ipc_send_fd( int fd, int send_fd )
751{
752        struct msghdr msg;
753        struct iovec iov;
754        char ccmsg[CMSG_SPACE(sizeof(fd))];
755        struct cmsghdr *cmsg;
756       
757        memset( &msg, 0, sizeof( msg ) );
[c5bff81]758        iov.iov_base = "0x90\r\n";         /* Ja, noppes */
[6c2404e]759        iov.iov_len = 6;
760        msg.msg_iov = &iov;
761        msg.msg_iovlen = 1;
762       
763        msg.msg_control = ccmsg;
764        msg.msg_controllen = sizeof( ccmsg );
765        cmsg = CMSG_FIRSTHDR( &msg );
766        cmsg->cmsg_level = SOL_SOCKET;
767        cmsg->cmsg_type = SCM_RIGHTS;
768        cmsg->cmsg_len = CMSG_LEN( sizeof( send_fd ) );
769        *(int*)CMSG_DATA( cmsg ) = send_fd;
770        msg.msg_controllen = cmsg->cmsg_len;
771       
772        return sendmsg( fd, &msg, 0 ) == 6;
773}
774
[2face62]775void ipc_master_free_one( struct bitlbee_child *c )
776{
[c5bff81]777        GSList *l;
778       
[ba9edaa]779        b_event_remove( c->ipc_inpa );
[2face62]780        closesocket( c->ipc_fd );
781       
[6c2404e]782        if( c->to_fd != -1 )
783                close( c->to_fd );
784       
[2face62]785        g_free( c->host );
786        g_free( c->nick );
787        g_free( c->realname );
[6c2404e]788        g_free( c->password );
[2face62]789        g_free( c );
[c5bff81]790       
791        child_list = g_slist_remove( child_list, c );
792       
793        /* Also, if any child has a reference to this one, remove it. */
794        for( l = child_list; l; l = l->next )
795        {
796                struct bitlbee_child *oc = l->data;
797               
798                if( oc->to_child == c )
799                        ipc_master_takeover_fail( oc, FALSE );
800        }
[2face62]801}
802
[171ef85]803void ipc_master_free_fd( int fd )
804{
805        GSList *l;
806        struct bitlbee_child *c;
807       
808        for( l = child_list; l; l = l->next )
809        {
810                c = l->data;
811                if( c->ipc_fd == fd )
812                {
813                        ipc_master_free_one( c );
814                        break;
815                }
816        }
817}
818
[2face62]819void ipc_master_free_all()
820{
[c5bff81]821        while( child_list )
822                ipc_master_free_one( child_list->data );
[0431ea1]823}
[54879ab]824
[171ef85]825void ipc_child_disable()
826{
827        b_event_remove( global.listen_watch_source_id );
828        close( global.listen_socket );
829       
830        global.listen_socket = -1;
831}
832
[80c1e4d]833#ifndef _WIN32
[54879ab]834char *ipc_master_save_state()
835{
836        char *fn = g_strdup( "/tmp/bee-restart.XXXXXX" );
837        int fd = mkstemp( fn );
838        GSList *l;
839        FILE *fp;
840        int i;
841       
842        if( fd == -1 )
843        {
844                log_message( LOGLVL_ERROR, "Could not create temporary file: %s", strerror( errno ) );
845                g_free( fn );
846                return NULL;
847        }
848       
849        /* This is more convenient now. */
850        fp = fdopen( fd, "w" );
851       
852        for( l = child_list, i = 0; l; l = l->next )
853                i ++;
854       
855        /* Number of client processes. */
856        fprintf( fp, "%d\n", i );
857       
858        for( l = child_list; l; l = l->next )
[52744f8]859                fprintf( fp, "%d %d\n", (int) ((struct bitlbee_child*)l->data)->pid,
[54879ab]860                                        ((struct bitlbee_child*)l->data)->ipc_fd );
861       
862        if( fclose( fp ) == 0 )
863        {
864                return fn;
865        }
866        else
867        {
868                unlink( fn );
869                g_free( fn );
870                return NULL;
871        }
872}
873
[6dff9d4]874
[ba9edaa]875static gboolean new_ipc_client( gpointer data, gint serversock, b_input_condition cond )
[6dff9d4]876{
877        struct bitlbee_child *child = g_new0( struct bitlbee_child, 1 );
[a0d04d6]878       
[6c2404e]879        child->to_fd = -1;
[a0d04d6]880        child->ipc_fd = accept( serversock, NULL, 0 );
881        if( child->ipc_fd == -1 )
882        {
[8a56e52]883                log_message( LOGLVL_WARNING, "Unable to accept connection on UNIX domain socket: %s", strerror(errno) );
884                return TRUE;
885        }
[6dff9d4]886               
[e046390]887        child->ipc_inpa = b_input_add( child->ipc_fd, B_EV_IO_READ, ipc_master_read, child );
[a0d04d6]888       
[6c2404e]889        child_list = g_slist_prepend( child_list, child );
[ba9edaa]890       
[6dff9d4]891        return TRUE;
892}
893
894int ipc_master_listen_socket()
895{
896        struct sockaddr_un un_addr;
897        int serversock;
898
899        /* Clean up old socket files that were hanging around.. */
[8a56e52]900        if (unlink(IPCSOCKET) == -1 && errno != ENOENT) {
901                log_message( LOGLVL_ERROR, "Could not remove old IPC socket at %s: %s", IPCSOCKET, strerror(errno) );
902                return 0;
903        }
[6dff9d4]904
905        un_addr.sun_family = AF_UNIX;
906        strcpy(un_addr.sun_path, IPCSOCKET);
907
908        serversock = socket(AF_UNIX, SOCK_STREAM, PF_UNIX);
909
[8a56e52]910        if (serversock == -1) {
911                log_message( LOGLVL_WARNING, "Unable to create UNIX socket: %s", strerror(errno) );
912                return 0;
913        }
[6dff9d4]914
[5d6c178]915        if (bind(serversock, (struct sockaddr *)&un_addr, sizeof(un_addr)) == -1) {
[8a56e52]916                log_message( LOGLVL_WARNING, "Unable to bind UNIX socket to %s: %s", IPCSOCKET, strerror(errno) );
917                return 0;
918        }
919
920        if (listen(serversock, 5) == -1) {
921                log_message( LOGLVL_WARNING, "Unable to listen on UNIX socket: %s", strerror(errno) );
922                return 0;
923        }
[6dff9d4]924       
[e046390]925        b_input_add( serversock, B_EV_IO_READ, new_ipc_client, NULL );
[8a56e52]926       
[6dff9d4]927        return 1;
928}
929#else
[1cda4f3]930int ipc_master_listen_socket()
931{
[6dff9d4]932        /* FIXME: Open named pipe \\.\BITLBEE */
[1cda4f3]933        return 0;
934}
[6dff9d4]935#endif
936
[cd63d58]937int ipc_master_load_state( char *statefile )
[54879ab]938{
939        struct bitlbee_child *child;
940        FILE *fp;
941        int i, n;
942       
943        if( statefile == NULL )
944                return 0;
[cd63d58]945       
[54879ab]946        fp = fopen( statefile, "r" );
947        unlink( statefile );    /* Why do it later? :-) */
948        if( fp == NULL )
949                return 0;
950       
951        if( fscanf( fp, "%d", &n ) != 1 )
952        {
953                log_message( LOGLVL_WARNING, "Could not import state information for child processes." );
954                fclose( fp );
955                return 0;
956        }
957       
958        log_message( LOGLVL_INFO, "Importing information for %d child processes.", n );
959        for( i = 0; i < n; i ++ )
960        {
961                child = g_new0( struct bitlbee_child, 1 );
962               
[52744f8]963                if( fscanf( fp, "%d %d", (int *) &child->pid, &child->ipc_fd ) != 2 )
[54879ab]964                {
965                        log_message( LOGLVL_WARNING, "Unexpected end of file: Only processed %d clients.", i );
966                        g_free( child );
967                        fclose( fp );
968                        return 0;
969                }
[e046390]970                child->ipc_inpa = b_input_add( child->ipc_fd, B_EV_IO_READ, ipc_master_read, child );
[6c2404e]971                child->to_fd = -1;
[54879ab]972               
[6c2404e]973                child_list = g_slist_prepend( child_list, child );
[54879ab]974        }
975       
976        ipc_to_children_str( "HELLO\r\n" );
977        ipc_to_children_str( "OPERMSG :New BitlBee master process started (version " BITLBEE_VERSION ")\r\n" );
978       
[dd89a55]979        fclose( fp );
[54879ab]980        return 1;
981}
Note: See TracBrowser for help on using the repository browser.