source: ipc.c @ 9595d2b

Last change on this file since 9595d2b was 9595d2b, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-07-09T08:25:25Z

Also sync umodes.

  • Property mode set to 100644
File size: 22.0 KB
RevLine 
[0431ea1]1  /********************************************************************\
2  * BitlBee -- An IRC to other IM-networks gateway                     *
3  *                                                                    *
[54879ab]4  * Copyright 2002-2006 Wilmer van der Gaast and others                *
[0431ea1]5  \********************************************************************/
6
7/* IPC - communication between BitlBee processes                        */
8
9/*
10  This program is free software; you can redistribute it and/or modify
11  it under the terms of the GNU General Public License as published by
12  the Free Software Foundation; either version 2 of the License, or
13  (at your option) any later version.
14
15  This program is distributed in the hope that it will be useful,
16  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  GNU General Public License for more details.
19
20  You should have received a copy of the GNU General Public License with
21  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
22  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
23  Suite 330, Boston, MA  02111-1307  USA
24*/
25
26#define BITLBEE_CORE
27#include "bitlbee.h"
28#include "ipc.h"
29#include "commands.h"
[6dff9d4]30#ifndef _WIN32
31#include <sys/un.h>
32#endif
[0431ea1]33
34GSList *child_list = NULL;
[0b09da0]35static int ipc_child_recv_fd = -1;
[74c119d]36
[f73b969]37static void ipc_master_cmd_client( irc_t *data, char **cmd )
[2face62]38{
[5e713f6]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. */
[2face62]43        struct bitlbee_child *child = (void*) data;
44       
[54879ab]45        if( child && cmd[1] )
[bd9b00f]46        {
47                child->host = g_strdup( cmd[1] );
48                child->nick = g_strdup( cmd[2] );
49                child->realname = g_strdup( cmd[3] );
50        }
[2face62]51       
[54879ab]52        if( g_strcasecmp( cmd[0], "CLIENT" ) == 0 )
53                ipc_to_children_str( "OPERMSG :Client connecting (PID=%d): %s@%s (%s)\r\n",
[52744f8]54                                     (int) ( child ? child->pid : -1 ), cmd[2], cmd[1], cmd[3] );
[2face62]55}
56
[f73b969]57static void ipc_master_cmd_die( irc_t *data, char **cmd )
[0431ea1]58{
[74c119d]59        if( global.conf->runmode == RUNMODE_FORKDAEMON )
60                ipc_to_children_str( "DIE\r\n" );
61       
[ba9edaa]62        bitlbee_shutdown( NULL, -1, 0 );
[0431ea1]63}
64
[565a1ea]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
[f73b969]84void ipc_master_cmd_rehash( irc_t *data, char **cmd )
[0431ea1]85{
[f4a5940]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 );
[0431ea1]101}
102
[54879ab]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;
[ba9edaa]112        bitlbee_shutdown( NULL, -1, 0 );
[54879ab]113}
114
[6c2404e]115void ipc_master_cmd_identify( irc_t *data, char **cmd )
116{
117        struct bitlbee_child *child = (void*) data, *old = NULL;
[0b09da0]118        char *resp;
[6c2404e]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 &&
[0b09da0]131                    old->password && strcmp( old->password, child->password ) == 0 )
[6c2404e]132                        break;
133        }
134       
[f545372]135        if( l && !child->to_child && !old->to_child )
[0b09da0]136        {
137                resp = "TAKEOVER INIT\r\n";
[f545372]138                child->to_child = old;
139                old->to_child = child;
[0b09da0]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;
[f545372]161        char *fwd = NULL;
[0b09da0]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                       
[f545372]176                        fwd = irc_build_line( cmd );
177                        if( write( child->to_child->ipc_fd, fwd, strlen( fwd ) ) != strlen( fwd ) )
[0b09da0]178                        {
179                                ipc_master_free_one( child );
180                                child_list = g_slist_remove( child_list, child );
181                        }
[f545372]182                        g_free( fwd );
[0b09da0]183                }
184        }
[f545372]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        }
[6c2404e]205}
206
[0431ea1]207static const command_t ipc_master_commands[] = {
[2face62]208        { "client",     3, ipc_master_cmd_client,     0 },
[54879ab]209        { "hello",      0, ipc_master_cmd_client,     0 },
[0431ea1]210        { "die",        0, ipc_master_cmd_die,        0 },
[565a1ea]211        { "deaf",       0, ipc_master_cmd_deaf,       0 },
[f4a5940]212        { "wallops",    1, NULL,                      IPC_CMD_TO_CHILDREN },
[dfc8a46]213        { "wall",       1, NULL,                      IPC_CMD_TO_CHILDREN },
[2face62]214        { "opermsg",    1, NULL,                      IPC_CMD_TO_CHILDREN },
[f4a5940]215        { "rehash",     0, ipc_master_cmd_rehash,     0 },
[48721c3]216        { "kill",       2, NULL,                      IPC_CMD_TO_CHILDREN },
[54879ab]217        { "restart",    0, ipc_master_cmd_restart,    0 },
[6c2404e]218        { "identify",   2, ipc_master_cmd_identify,   0 },
[0b09da0]219        { "takeover",   1, ipc_master_cmd_takeover,   0 },
[0431ea1]220        { NULL }
221};
222
[74c119d]223
[f73b969]224static void ipc_child_cmd_die( irc_t *irc, char **cmd )
[74c119d]225{
[87de505]226        irc_abort( irc, 0, "Shutdown requested by operator" );
[74c119d]227}
228
[f73b969]229static void ipc_child_cmd_wallops( irc_t *irc, char **cmd )
[0431ea1]230{
[3af70b0]231        if( !( irc->status & USTATUS_LOGGED_IN ) )
[f73b969]232                return;
[daa9e02]233       
[0431ea1]234        if( strchr( irc->umode, 'w' ) )
[3ddb7477]235                irc_write( irc, ":%s WALLOPS :%s", irc->root->host, cmd[1] );
[e0ca412]236}
237
[dfc8a46]238static void ipc_child_cmd_wall( irc_t *irc, char **cmd )
[e0ca412]239{
[3af70b0]240        if( !( irc->status & USTATUS_LOGGED_IN ) )
[f73b969]241                return;
[daa9e02]242       
[74c119d]243        if( strchr( irc->umode, 's' ) )
[3ddb7477]244                irc_write( irc, ":%s NOTICE %s :%s", irc->root->host, irc->user->nick, cmd[1] );
[0431ea1]245}
246
[f73b969]247static void ipc_child_cmd_opermsg( irc_t *irc, char **cmd )
[2face62]248{
[3af70b0]249        if( !( irc->status & USTATUS_LOGGED_IN ) )
[f73b969]250                return;
[2face62]251       
252        if( strchr( irc->umode, 'o' ) )
[3ddb7477]253                irc_write( irc, ":%s NOTICE %s :*** OperMsg *** %s", irc->root->host, irc->user->nick, cmd[1] );
[2face62]254}
255
[f73b969]256static void ipc_child_cmd_rehash( irc_t *irc, char **cmd )
[f4a5940]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
[f73b969]268static void ipc_child_cmd_kill( irc_t *irc, char **cmd )
[48721c3]269{
[3af70b0]270        if( !( irc->status & USTATUS_LOGGED_IN ) )
[f73b969]271                return;
[48721c3]272       
[3ddb7477]273        if( nick_cmp( cmd[1], irc->user->nick ) != 0 )
[f73b969]274                return;         /* It's not for us. */
[48721c3]275       
[3ddb7477]276        irc_write( irc, ":%s!%s@%s KILL %s :%s", irc->root->nick, irc->root->nick, irc->root->host, irc->user->nick, cmd[2] );
[f73b969]277        irc_abort( irc, 0, "Killed by operator: %s", cmd[2] );
[48721c3]278}
279
[54879ab]280static void ipc_child_cmd_hello( irc_t *irc, char **cmd )
281{
[3af70b0]282        if( !( irc->status & USTATUS_LOGGED_IN ) )
[54879ab]283                ipc_to_master_str( "HELLO\r\n" );
284        else
[3ddb7477]285                ipc_to_master_str( "HELLO %s %s :%s\r\n", irc->user->host, irc->user->nick, irc->user->fullname );
[54879ab]286}
287
[f545372]288static void ipc_child_cmd_takeover_yes( void *data );
289static void ipc_child_cmd_takeover_no( void *data );
290
[0b09da0]291static void ipc_child_cmd_takeover( irc_t *irc, char **cmd )
292{
293        if( strcmp( cmd[1], "NO" ) == 0 )
294        {
[f545372]295                /* Master->New connection */
[0b09da0]296                /* No takeover, finish the login. */
297        }
298        else if( strcmp( cmd[1], "INIT" ) == 0 )
299        {
[f545372]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 );
[0b09da0]309               
[f545372]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;
[0b09da0]313        }
314        else if( strcmp( cmd[1], "AUTH" ) == 0 )
315        {
[f545372]316                /* Master->Old connection */
[0b09da0]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                {
[f545372]322                        GSList *l;
323                       
[9595d2b]324                        /* TODO: Move this all into irc_switch_fd() or so and
325                           irc_sync() */
[0b09da0]326                        b_event_remove( irc->r_watch_source_id );
327                        closesocket( irc->fd );
328                        irc->fd = ipc_child_recv_fd;
329                        irc->r_watch_source_id = b_input_add( irc->fd, B_EV_IO_READ, bitlbee_io_current_client_read, irc );
330                        ipc_child_recv_fd = -1;
[f545372]331                       
[9595d2b]332                        irc_write( irc, ":%s!%s@%s MODE %s :%s", irc->user->nick,
333                                   irc->user->user, irc->user->host, irc->user->nick,
334                                   irc->umode );
335                       
[f545372]336                        for( l = irc->channels; l; l = l->next )
337                        {
338                                irc_channel_t *ic = l->data;
339                                if( ic->flags & IRC_CHANNEL_JOINED )
340                                        irc_send_join( ic, irc->user );
341                        }
342                        irc_usermsg( irc, "You've successfully taken over your old session" );
343                       
344                        ipc_to_master_str( "TAKEOVER DONE\r\n" );
[0b09da0]345                }
[f545372]346                else
347                {
348                        ipc_to_master_str( "TAKEOVER FAIL\r\n" );
349                }
350        }
351        else if( strcmp( cmd[1], "DONE" ) == 0 ) 
352        {
353                /* Master->New connection (now taken over by old process) */
354                irc_free( irc );
355        }
356        else if( strcmp( cmd[1], "FAIL" ) == 0 ) 
357        {
358                /* Master->New connection */
359                irc_usermsg( irc, "Could not take over old session" );
[0b09da0]360        }
361}
362
[f545372]363static void ipc_child_cmd_takeover_yes( void *data )
364{
365        irc_t *irc = data;
366        GSList *l;
367       
368        /* Master->New connection */
369        ipc_to_master_str( "TAKEOVER AUTH %s :%s\r\n",
370                           irc->user->nick, irc->password );
371       
372        /* Drop credentials, we'll shut down soon and shouldn't overwrite
373           any settings. */
374        irc_usermsg( irc, "Trying to take over existing session" );
375       
376        /* TODO: irc_setpass() should do all of this. */
377        irc_setpass( irc, NULL );
378        irc->status &= ~USTATUS_IDENTIFIED;
379        irc_umode_set( irc, "-R", 1 );
380       
381        for( l = irc->channels; l; l = l->next )
382                irc_channel_del_user( l->data, irc->user, IRC_CDU_KICK,
383                                      "Switching to old session" );
[9595d2b]384       
385        irc_write( irc, ":%s!%s@%s MODE %s :-%s", irc->user->nick,
386                   irc->user->user, irc->user->host, irc->user->nick,
387                   irc->umode );
[f545372]388}
389
390static void ipc_child_cmd_takeover_no( void *data )
391{
392        ipc_to_master_str( "TAKEOVER NO\r\n" );
393        cmd_identify_finish( data, 0, 0 );
394}
395
[0431ea1]396static const command_t ipc_child_commands[] = {
[74c119d]397        { "die",        0, ipc_child_cmd_die,         0 },
398        { "wallops",    1, ipc_child_cmd_wallops,     0 },
[dfc8a46]399        { "wall",       1, ipc_child_cmd_wall,        0 },
[2face62]400        { "opermsg",    1, ipc_child_cmd_opermsg,     0 },
[48721c3]401        { "rehash",     0, ipc_child_cmd_rehash,      0 },
402        { "kill",       2, ipc_child_cmd_kill,        0 },
[54879ab]403        { "hello",      0, ipc_child_cmd_hello,       0 },
[0b09da0]404        { "takeover",   1, ipc_child_cmd_takeover,    0 },
[0431ea1]405        { NULL }
406};
407
[6c2404e]408gboolean ipc_child_identify( irc_t *irc )
409{
410        if( global.conf->runmode == RUNMODE_FORKDAEMON )
411        {
412                if( !ipc_send_fd( global.listen_socket, irc->fd ) )
413                        ipc_child_disable();
414       
415                ipc_to_master_str( "IDENTIFY %s :%s\r\n", irc->user->nick, irc->password );
416               
417                return TRUE;
418        }
419        else
420                return FALSE;
421}
[74c119d]422
[0431ea1]423static void ipc_command_exec( void *data, char **cmd, const command_t *commands )
424{
[f1d38f2]425        int i, j;
[0431ea1]426       
427        if( !cmd[0] )
428                return;
429       
430        for( i = 0; commands[i].command; i ++ )
431                if( g_strcasecmp( commands[i].command, cmd[0] ) == 0 )
432                {
[f1d38f2]433                        /* There is no typo in this line: */
434                        for( j = 1; cmd[j]; j ++ ); j --;
435                       
436                        if( j < commands[i].required_parameters )
437                                break;
438                       
[f4a5940]439                        if( commands[i].flags & IPC_CMD_TO_CHILDREN )
440                                ipc_to_children( cmd );
441                        else
442                                commands[i].execute( data, cmd );
443                       
[f1d38f2]444                        break;
[0431ea1]445                }
446}
447
[da7b484]448/* Return just one line. Returns NULL if something broke, an empty string
449   on temporary "errors" (EAGAIN and friends). */
[6c2404e]450static char *ipc_readline( int fd, int *recv_fd )
[0431ea1]451{
[6c2404e]452        struct msghdr msg;
453        struct iovec iov;
454        char ccmsg[CMSG_SPACE(sizeof(recv_fd))];
455        struct cmsghdr *cmsg;
[da7b484]456        char buf[513], *eol;
[0431ea1]457        int size;
458       
459        /* Because this is internal communication, it should be pretty safe
460           to just peek at the message, find its length (by searching for the
461           end-of-line) and then just read that message. With internal
462           sockets and limites message length, messages should always be
463           complete. Saves us quite a lot of code and buffering. */
[da7b484]464        size = recv( fd, buf, sizeof( buf ) - 1, MSG_PEEK );
[0431ea1]465        if( size == 0 || ( size < 0 && !sockerr_again() ) )
466                return NULL;
[1ea13be]467        else if( size < 0 ) /* && sockerr_again() */
468                return( g_strdup( "" ) );
[0431ea1]469        else
470                buf[size] = 0;
471       
[da7b484]472        if( ( eol = strstr( buf, "\r\n" ) ) == NULL )
[0431ea1]473                return NULL;
474        else
475                size = eol - buf + 2;
476       
[6c2404e]477        iov.iov_base = buf;
478        iov.iov_len = size;
479       
480        memset( &msg, 0, sizeof( msg ) );
481        msg.msg_iov = &iov;
482        msg.msg_iovlen = 1;
483        msg.msg_control = ccmsg;
484        msg.msg_controllen = sizeof( ccmsg );
485       
486        if( recvmsg( fd, &msg, 0 ) != size )
[0431ea1]487                return NULL;
[6c2404e]488       
489        if( recv_fd )
490                for( cmsg = CMSG_FIRSTHDR( &msg ); cmsg; cmsg = CMSG_NXTHDR( &msg, cmsg ) )
491                        if( cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS )
492                        {
493                                /* Getting more than one shouldn't happen but if it does,
494                                   make sure we don't leave them around. */
495                                if( *recv_fd != -1 )
496                                        close( *recv_fd );
497                               
498                                *recv_fd = *(int*) CMSG_DATA( cmsg );
[0b09da0]499                                fprintf( stderr, "pid %d received fd %d\n", (int) getpid(), *recv_fd );
[6c2404e]500                        }
501       
[0b09da0]502        fprintf( stderr, "pid %d received: %s", (int) getpid(), buf );
[6c2404e]503        return g_strndup( buf, size - 2 );
[0431ea1]504}
505
[ba9edaa]506gboolean ipc_master_read( gpointer data, gint source, b_input_condition cond )
[0431ea1]507{
[6c2404e]508        struct bitlbee_child *child = data;
[0431ea1]509        char *buf, **cmd;
510       
[6c2404e]511        if( ( buf = ipc_readline( source, &child->to_fd ) ) )
[0431ea1]512        {
513                cmd = irc_parse_line( buf );
514                if( cmd )
[edc767b]515                {
[6c2404e]516                        ipc_command_exec( child, cmd, ipc_master_commands );
[edc767b]517                        g_free( cmd );
518                }
519                g_free( buf );
[0431ea1]520        }
521        else
522        {
[171ef85]523                ipc_master_free_fd( source );
[0431ea1]524        }
[ba9edaa]525       
526        return TRUE;
[0431ea1]527}
528
[ba9edaa]529gboolean ipc_child_read( gpointer data, gint source, b_input_condition cond )
[0431ea1]530{
531        char *buf, **cmd;
532       
[0b09da0]533        if( ( buf = ipc_readline( source, &ipc_child_recv_fd ) ) )
[0431ea1]534        {
535                cmd = irc_parse_line( buf );
536                if( cmd )
[edc767b]537                {
[0431ea1]538                        ipc_command_exec( data, cmd, ipc_child_commands );
[edc767b]539                        g_free( cmd );
540                }
541                g_free( buf );
[0431ea1]542        }
543        else
544        {
[171ef85]545                ipc_child_disable();
[0431ea1]546        }
[ba9edaa]547       
548        return TRUE;
[0431ea1]549}
550
551void ipc_to_master( char **cmd )
552{
553        if( global.conf->runmode == RUNMODE_FORKDAEMON )
554        {
[74c119d]555                char *s = irc_build_line( cmd );
[2face62]556                ipc_to_master_str( "%s", s );
[f4a5940]557                g_free( s );
558        }
559        else if( global.conf->runmode == RUNMODE_DAEMON )
560        {
561                ipc_command_exec( NULL, cmd, ipc_master_commands );
562        }
563}
564
[2face62]565void ipc_to_master_str( char *format, ... )
[f4a5940]566{
[2face62]567        char *msg_buf;
568        va_list params;
569
570        va_start( params, format );
571        msg_buf = g_strdup_vprintf( format, params );
572        va_end( params );
573       
574        if( strlen( msg_buf ) > 512 )
575        {
576                /* Don't send it, it's too long... */
577        }
578        else if( global.conf->runmode == RUNMODE_FORKDAEMON )
[f4a5940]579        {
[171ef85]580                if( global.listen_socket >= 0 )
581                        if( write( global.listen_socket, msg_buf, strlen( msg_buf ) ) <= 0 )
582                                ipc_child_disable();
[f4a5940]583        }
584        else if( global.conf->runmode == RUNMODE_DAEMON )
585        {
[bd9b00f]586                char **cmd, *s;
587               
588                if( ( s = strchr( msg_buf, '\r' ) ) )
589                        *s = 0;
[f4a5940]590               
[2face62]591                cmd = irc_parse_line( msg_buf );
[f4a5940]592                ipc_command_exec( NULL, cmd, ipc_master_commands );
593                g_free( cmd );
[74c119d]594        }
[2face62]595       
596        g_free( msg_buf );
[74c119d]597}
598
599void ipc_to_children( char **cmd )
600{
601        if( global.conf->runmode == RUNMODE_FORKDAEMON )
602        {
603                char *msg_buf = irc_build_line( cmd );
[2face62]604                ipc_to_children_str( "%s", msg_buf );
[74c119d]605                g_free( msg_buf );
606        }
[f4a5940]607        else if( global.conf->runmode == RUNMODE_DAEMON )
608        {
609                GSList *l;
610               
611                for( l = irc_connection_list; l; l = l->next )
612                        ipc_command_exec( l->data, cmd, ipc_child_commands );
613        }
[74c119d]614}
615
[2face62]616void ipc_to_children_str( char *format, ... )
[74c119d]617{
[2face62]618        char *msg_buf;
619        va_list params;
620
621        va_start( params, format );
622        msg_buf = g_strdup_vprintf( format, params );
623        va_end( params );
624       
625        if( strlen( msg_buf ) > 512 )
626        {
627                /* Don't send it, it's too long... */
628        }
629        else if( global.conf->runmode == RUNMODE_FORKDAEMON )
[74c119d]630        {
631                int msg_len = strlen( msg_buf );
[171ef85]632                GSList *l, *next;
[0431ea1]633               
[171ef85]634                for( l = child_list; l; l = next )
[0431ea1]635                {
[74c119d]636                        struct bitlbee_child *c = l->data;
[171ef85]637                       
638                        next = l->next;
639                        if( write( c->ipc_fd, msg_buf, msg_len ) <= 0 )
640                        {
641                                ipc_master_free_one( c );
642                                child_list = g_slist_remove( child_list, c );
643                        }
[0431ea1]644                }
645        }
[f4a5940]646        else if( global.conf->runmode == RUNMODE_DAEMON )
647        {
[bd9b00f]648                char **cmd, *s;
649               
650                if( ( s = strchr( msg_buf, '\r' ) ) )
651                        *s = 0;
[f4a5940]652               
[2face62]653                cmd = irc_parse_line( msg_buf );
[f4a5940]654                ipc_to_children( cmd );
655                g_free( cmd );
656        }
[2face62]657       
658        g_free( msg_buf );
659}
660
[6c2404e]661static gboolean ipc_send_fd( int fd, int send_fd )
662{
663        struct msghdr msg;
664        struct iovec iov;
665        char ccmsg[CMSG_SPACE(sizeof(fd))];
666        struct cmsghdr *cmsg;
667       
668        memset( &msg, 0, sizeof( msg ) );
669        iov.iov_base = "0x90\r\n";
670        iov.iov_len = 6;
671        msg.msg_iov = &iov;
672        msg.msg_iovlen = 1;
673       
674        msg.msg_control = ccmsg;
675        msg.msg_controllen = sizeof( ccmsg );
676        cmsg = CMSG_FIRSTHDR( &msg );
677        cmsg->cmsg_level = SOL_SOCKET;
678        cmsg->cmsg_type = SCM_RIGHTS;
679        cmsg->cmsg_len = CMSG_LEN( sizeof( send_fd ) );
680        *(int*)CMSG_DATA( cmsg ) = send_fd;
681        msg.msg_controllen = cmsg->cmsg_len;
682       
683        return sendmsg( fd, &msg, 0 ) == 6;
684}
685
[2face62]686void ipc_master_free_one( struct bitlbee_child *c )
687{
[ba9edaa]688        b_event_remove( c->ipc_inpa );
[2face62]689        closesocket( c->ipc_fd );
690       
[6c2404e]691        if( c->to_fd != -1 )
692                close( c->to_fd );
693       
[2face62]694        g_free( c->host );
695        g_free( c->nick );
696        g_free( c->realname );
[6c2404e]697        g_free( c->password );
[2face62]698        g_free( c );
699}
700
[171ef85]701void ipc_master_free_fd( int fd )
702{
703        GSList *l;
704        struct bitlbee_child *c;
705       
706        for( l = child_list; l; l = l->next )
707        {
708                c = l->data;
709                if( c->ipc_fd == fd )
710                {
711                        ipc_master_free_one( c );
712                        child_list = g_slist_remove( child_list, c );
713                        break;
714                }
715        }
716}
717
[2face62]718void ipc_master_free_all()
719{
720        GSList *l;
721       
722        for( l = child_list; l; l = l->next )
723                ipc_master_free_one( l->data );
724       
725        g_slist_free( child_list );
726        child_list = NULL;
[0431ea1]727}
[54879ab]728
[171ef85]729void ipc_child_disable()
730{
731        b_event_remove( global.listen_watch_source_id );
732        close( global.listen_socket );
733       
734        global.listen_socket = -1;
735}
736
[80c1e4d]737#ifndef _WIN32
[54879ab]738char *ipc_master_save_state()
739{
740        char *fn = g_strdup( "/tmp/bee-restart.XXXXXX" );
741        int fd = mkstemp( fn );
742        GSList *l;
743        FILE *fp;
744        int i;
745       
746        if( fd == -1 )
747        {
748                log_message( LOGLVL_ERROR, "Could not create temporary file: %s", strerror( errno ) );
749                g_free( fn );
750                return NULL;
751        }
752       
753        /* This is more convenient now. */
754        fp = fdopen( fd, "w" );
755       
756        for( l = child_list, i = 0; l; l = l->next )
757                i ++;
758       
759        /* Number of client processes. */
760        fprintf( fp, "%d\n", i );
761       
762        for( l = child_list; l; l = l->next )
[52744f8]763                fprintf( fp, "%d %d\n", (int) ((struct bitlbee_child*)l->data)->pid,
[54879ab]764                                        ((struct bitlbee_child*)l->data)->ipc_fd );
765       
766        if( fclose( fp ) == 0 )
767        {
768                return fn;
769        }
770        else
771        {
772                unlink( fn );
773                g_free( fn );
774                return NULL;
775        }
776}
777
[6dff9d4]778
[ba9edaa]779static gboolean new_ipc_client( gpointer data, gint serversock, b_input_condition cond )
[6dff9d4]780{
781        struct bitlbee_child *child = g_new0( struct bitlbee_child, 1 );
[a0d04d6]782       
[6c2404e]783        child->to_fd = -1;
[a0d04d6]784        child->ipc_fd = accept( serversock, NULL, 0 );
785        if( child->ipc_fd == -1 )
786        {
[8a56e52]787                log_message( LOGLVL_WARNING, "Unable to accept connection on UNIX domain socket: %s", strerror(errno) );
788                return TRUE;
789        }
[6dff9d4]790               
[e046390]791        child->ipc_inpa = b_input_add( child->ipc_fd, B_EV_IO_READ, ipc_master_read, child );
[a0d04d6]792       
[6c2404e]793        child_list = g_slist_prepend( child_list, child );
[ba9edaa]794       
[6dff9d4]795        return TRUE;
796}
797
798int ipc_master_listen_socket()
799{
800        struct sockaddr_un un_addr;
801        int serversock;
802
803        /* Clean up old socket files that were hanging around.. */
[8a56e52]804        if (unlink(IPCSOCKET) == -1 && errno != ENOENT) {
805                log_message( LOGLVL_ERROR, "Could not remove old IPC socket at %s: %s", IPCSOCKET, strerror(errno) );
806                return 0;
807        }
[6dff9d4]808
809        un_addr.sun_family = AF_UNIX;
810        strcpy(un_addr.sun_path, IPCSOCKET);
811
812        serversock = socket(AF_UNIX, SOCK_STREAM, PF_UNIX);
813
[8a56e52]814        if (serversock == -1) {
815                log_message( LOGLVL_WARNING, "Unable to create UNIX socket: %s", strerror(errno) );
816                return 0;
817        }
[6dff9d4]818
[5d6c178]819        if (bind(serversock, (struct sockaddr *)&un_addr, sizeof(un_addr)) == -1) {
[8a56e52]820                log_message( LOGLVL_WARNING, "Unable to bind UNIX socket to %s: %s", IPCSOCKET, strerror(errno) );
821                return 0;
822        }
823
824        if (listen(serversock, 5) == -1) {
825                log_message( LOGLVL_WARNING, "Unable to listen on UNIX socket: %s", strerror(errno) );
826                return 0;
827        }
[6dff9d4]828       
[e046390]829        b_input_add( serversock, B_EV_IO_READ, new_ipc_client, NULL );
[8a56e52]830       
[6dff9d4]831        return 1;
832}
833#else
[1cda4f3]834int ipc_master_listen_socket()
835{
[6dff9d4]836        /* FIXME: Open named pipe \\.\BITLBEE */
[1cda4f3]837        return 0;
838}
[6dff9d4]839#endif
840
[cd63d58]841int ipc_master_load_state( char *statefile )
[54879ab]842{
843        struct bitlbee_child *child;
844        FILE *fp;
845        int i, n;
846       
847        if( statefile == NULL )
848                return 0;
[cd63d58]849       
[54879ab]850        fp = fopen( statefile, "r" );
851        unlink( statefile );    /* Why do it later? :-) */
852        if( fp == NULL )
853                return 0;
854       
855        if( fscanf( fp, "%d", &n ) != 1 )
856        {
857                log_message( LOGLVL_WARNING, "Could not import state information for child processes." );
858                fclose( fp );
859                return 0;
860        }
861       
862        log_message( LOGLVL_INFO, "Importing information for %d child processes.", n );
863        for( i = 0; i < n; i ++ )
864        {
865                child = g_new0( struct bitlbee_child, 1 );
866               
[52744f8]867                if( fscanf( fp, "%d %d", (int *) &child->pid, &child->ipc_fd ) != 2 )
[54879ab]868                {
869                        log_message( LOGLVL_WARNING, "Unexpected end of file: Only processed %d clients.", i );
870                        g_free( child );
871                        fclose( fp );
872                        return 0;
873                }
[e046390]874                child->ipc_inpa = b_input_add( child->ipc_fd, B_EV_IO_READ, ipc_master_read, child );
[6c2404e]875                child->to_fd = -1;
[54879ab]876               
[6c2404e]877                child_list = g_slist_prepend( child_list, child );
[54879ab]878        }
879       
880        ipc_to_children_str( "HELLO\r\n" );
881        ipc_to_children_str( "OPERMSG :New BitlBee master process started (version " BITLBEE_VERSION ")\r\n" );
882       
[dd89a55]883        fclose( fp );
[54879ab]884        return 1;
885}
Note: See TracBrowser for help on using the repository browser.