source: ipc.c @ f545372

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

Ask for confirmation. Generally working fairly well now, but definitely
fragile.

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