source: ipc.c @ 420ddc00

Last change on this file since 420ddc00 was 0e788f5, checked in by Wilmer van der Gaast <wilmer@…>, at 2013-02-21T19:15:59Z

I'm still bored on a long flight. Wrote a script to automatically update
my copyright mentions since some were getting pretty stale. Left files not
touched since before 2012 alone so that this change doesn't touch almost
EVERY source file.

  • Property mode set to 100644
File size: 24.5 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"
[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.