source: ipc.c @ 9ead105

Last change on this file since 9ead105 was e252d8c, checked in by dequis <dx@…>, at 2014-09-27T14:54:35Z

RIP native win32 support (use cygwin instead)

It has been broken for a very long time and nobody cared about it.

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