source: ipc.c @ 8bd866f

Last change on this file since 8bd866f was 18e1f3b, checked in by Wilmer van der Gaast <wilmer@…>, at 2012-05-02T07:58:22Z

Set PACKAGE to BitlBee-LIBPURPLE for the libpurple variant, because in many
ways it's not BitlBee and I'm tired of getting libpurple-related bug reports.

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