source: ipc.c @ c5bff81

Last change on this file since c5bff81 was c5bff81, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-07-09T23:24:23Z

More state consistency checks/error handling.

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