source: ipc.c @ 09dfb68

Last change on this file since 09dfb68 was 133cdff, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-07-11T12:11:27Z

More careful pointer checking in the master.

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