source: ipc.c @ e92c4f4

Last change on this file since e92c4f4 was e92c4f4, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-07-11T00:29:19Z

Takeover stuff now works in daemon mode as well.

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