source: ipc.c @ f1c2b21

Last change on this file since f1c2b21 was f1c2b21, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-07-10T15:08:02Z

Cleanup. Move some code to a more appropriate location, and show the old
connection a quit message instead of just breaking the connection.

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