source: ipc.c @ debe871

Last change on this file since debe871 was debe871, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-07-09T21:06:38Z

Inform IPC master about nick changes.

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