Changeset f3b6764


Ignore:
Timestamp:
2010-07-09T23:39:25Z (14 years ago)
Author:
Wilmer van der Gaast <wilmer@…>
Branches:
master
Children:
534e406
Parents:
9a9b520 (diff), b556e46 (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the (diff) links above to see all the changes relative to each parent.
Message:

Only available in ForkDaemon mode for now: If a user connects (and
identifies) while he's already logged in, offer to take over the old
connection.

Files:
7 edited

Legend:

Unmodified
Added
Removed
  • bitlbee.c

    r9a9b520 rf3b6764  
    318318                        struct bitlbee_child *child;
    319319                       
     320                        /* TODO: Stuff like this belongs in ipc.c. */
    320321                        child = g_new0( struct bitlbee_child, 1 );
    321322                        child->pid = client_pid;
    322323                        child->ipc_fd = fds[0];
    323324                        child->ipc_inpa = b_input_add( child->ipc_fd, B_EV_IO_READ, ipc_master_read, child );
     325                        child->to_fd = -1;
    324326                        child_list = g_slist_append( child_list, child );
    325327                       
  • bitlbee.h

    r9a9b520 rf3b6764  
    163163void root_command_string( irc_t *irc, char *command );
    164164void root_command( irc_t *irc, char *command[] );
     165gboolean cmd_identify_finish( gpointer data, gint fd, b_input_condition cond );
    165166gboolean bitlbee_shutdown( gpointer data, gint fd, b_input_condition cond );
    166167
  • ipc.c

    r9a9b520 rf3b6764  
    3333
    3434GSList *child_list = NULL;
     35static int ipc_child_recv_fd = -1;
     36
     37static void ipc_master_takeover_fail( struct bitlbee_child *child, gboolean both );
     38static gboolean ipc_send_fd( int fd, int send_fd );
    3539
    3640static void ipc_master_cmd_client( irc_t *data, char **cmd )
     
    4953        }
    5054       
     55        /* CLIENT == On initial connects, HELLO is after /RESTARTs. */
    5156        if( g_strcasecmp( cmd[0], "CLIENT" ) == 0 )
    5257                ipc_to_children_str( "OPERMSG :Client connecting (PID=%d): %s@%s (%s)\r\n",
     
    5459}
    5560
     61static void ipc_master_cmd_nick( irc_t *data, char **cmd )
     62{
     63        struct bitlbee_child *child = (void*) data;
     64       
     65        if( child && cmd[1] )
     66        {
     67                g_free( child->nick );
     68                child->nick = g_strdup( cmd[1] );
     69        }
     70}
     71
    5672static void ipc_master_cmd_die( irc_t *data, char **cmd )
    5773{
     
    110126        global.restart = -1;
    111127        bitlbee_shutdown( NULL, -1, 0 );
     128}
     129
     130void ipc_master_cmd_identify( irc_t *data, char **cmd )
     131{
     132        struct bitlbee_child *child = (void*) data, *old = NULL;
     133        char *resp;
     134        GSList *l;
     135       
     136        if( strcmp( child->nick, cmd[1] ) != 0 )
     137                return;
     138       
     139        g_free( child->password );
     140        child->password = g_strdup( cmd[2] );
     141       
     142        for( l = child_list; l; l = l->next )
     143        {
     144                old = l->data;
     145                if( nick_cmp( old->nick, child->nick ) == 0 && child != old &&
     146                    old->password && strcmp( old->password, child->password ) == 0 )
     147                        break;
     148        }
     149       
     150        if( l && !child->to_child && !old->to_child )
     151        {
     152                resp = "TAKEOVER INIT\r\n";
     153                child->to_child = old;
     154                old->to_child = child;
     155        }
     156        else
     157        {
     158                /* Won't need the fd since we can't send it anywhere. */
     159                closesocket( child->to_fd );
     160                child->to_fd = -1;
     161                resp = "TAKEOVER NO\r\n";
     162        }
     163       
     164        if( write( child->ipc_fd, resp, strlen( resp ) ) != strlen( resp ) )
     165                ipc_master_free_one( child );
     166}
     167
     168
     169void ipc_master_cmd_takeover( irc_t *data, char **cmd )
     170{
     171        struct bitlbee_child *child = (void*) data;
     172        char *fwd = NULL;
     173       
     174        if( child->to_child == NULL ||
     175            g_slist_find( child_list, child->to_child ) == NULL )
     176                return ipc_master_takeover_fail( child, FALSE );
     177       
     178        if( strcmp( cmd[1], "AUTH" ) == 0 )
     179        {
     180                /* New connection -> Master */
     181                if( child->to_child &&
     182                    child->nick && child->to_child->nick && cmd[2] &&
     183                    child->password && child->to_child->password && cmd[3] &&
     184                    strcmp( child->nick, child->to_child->nick ) == 0 &&
     185                    strcmp( child->nick, cmd[2] ) == 0 &&
     186                    strcmp( child->password, child->to_child->password ) == 0 &&
     187                    strcmp( child->password, cmd[3] ) == 0 )
     188                {
     189                        ipc_send_fd( child->to_child->ipc_fd, child->to_fd );
     190                       
     191                        fwd = irc_build_line( cmd );
     192                        if( write( child->to_child->ipc_fd, fwd, strlen( fwd ) ) != strlen( fwd ) )
     193                                ipc_master_free_one( child );
     194                        g_free( fwd );
     195                }
     196                else
     197                        return ipc_master_takeover_fail( child, TRUE );
     198        }
     199        else if( strcmp( cmd[1], "DONE" ) == 0 || strcmp( cmd[1], "FAIL" ) == 0 )
     200        {
     201                /* Old connection -> Master */
     202                int fd;
     203               
     204                /* The copy was successful (or not), we don't need it anymore. */
     205                closesocket( child->to_fd );
     206                child->to_fd = -1;
     207               
     208                /* Pass it through to the other party, and flush all state. */
     209                fwd = irc_build_line( cmd );
     210                fd = child->to_child->ipc_fd;
     211                child->to_child->to_child = NULL;
     212                child->to_child = NULL;
     213                if( write( fd, fwd, strlen( fwd ) ) != strlen( fwd ) )
     214                        ipc_master_free_one( child );
     215                g_free( fwd );
     216        }
    112217}
    113218
     
    115220        { "client",     3, ipc_master_cmd_client,     0 },
    116221        { "hello",      0, ipc_master_cmd_client,     0 },
     222        { "nick",       1, ipc_master_cmd_nick,       0 },
    117223        { "die",        0, ipc_master_cmd_die,        0 },
    118224        { "deaf",       0, ipc_master_cmd_deaf,       0 },
     
    123229        { "kill",       2, NULL,                      IPC_CMD_TO_CHILDREN },
    124230        { "restart",    0, ipc_master_cmd_restart,    0 },
     231        { "identify",   2, ipc_master_cmd_identify,   0 },
     232        { "takeover",   1, ipc_master_cmd_takeover,   0 },
    125233        { NULL }
    126234};
     
    189297        else
    190298                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 );
    191407}
    192408
     
    199415        { "kill",       2, ipc_child_cmd_kill,        0 },
    200416        { "hello",      0, ipc_child_cmd_hello,       0 },
     417        { "takeover",   1, ipc_child_cmd_takeover,    0 },
    201418        { NULL }
    202419};
    203420
     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_master_takeover_fail( struct bitlbee_child *child, gboolean both )
     437{
     438        if( child == NULL || g_slist_find( child_list, child ) == NULL )
     439                return;
     440       
     441        if( both && child->to_child != NULL )
     442                ipc_master_takeover_fail( child->to_child, FALSE );
     443       
     444        if( child->to_fd > -1 )
     445        {
     446                /* Send this error only to the new connection, which can be
     447                   recognised by to_fd being set. */
     448                if( write( child->ipc_fd, "TAKEOVER FAIL\r\n", 15 ) != 15 )
     449                {
     450                        ipc_master_free_one( child );
     451                        return;
     452                }
     453                close( child->to_fd );
     454                child->to_fd = -1;
     455        }
     456        child->to_child = NULL;
     457}
    204458
    205459static void ipc_command_exec( void *data, char **cmd, const command_t *commands )
     
    230484/* Return just one line. Returns NULL if something broke, an empty string
    231485   on temporary "errors" (EAGAIN and friends). */
    232 static char *ipc_readline( int fd )
    233 {
     486static char *ipc_readline( int fd, int *recv_fd )
     487{
     488        struct msghdr msg;
     489        struct iovec iov;
     490        char ccmsg[CMSG_SPACE(sizeof(recv_fd))];
     491        struct cmsghdr *cmsg;
    234492        char buf[513], *eol;
    235493        int size;
     
    253511                size = eol - buf + 2;
    254512       
    255         if( recv( fd, buf, size, 0 ) != size )
     513        iov.iov_base = buf;
     514        iov.iov_len = size;
     515       
     516        memset( &msg, 0, sizeof( msg ) );
     517        msg.msg_iov = &iov;
     518        msg.msg_iovlen = 1;
     519        msg.msg_control = ccmsg;
     520        msg.msg_controllen = sizeof( ccmsg );
     521       
     522        if( recvmsg( fd, &msg, 0 ) != size )
    256523                return NULL;
    257         else
    258                 return g_strndup( buf, size - 2 );
     524       
     525        if( recv_fd )
     526                for( cmsg = CMSG_FIRSTHDR( &msg ); cmsg; cmsg = CMSG_NXTHDR( &msg, cmsg ) )
     527                        if( cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS )
     528                        {
     529                                /* Getting more than one shouldn't happen but if it does,
     530                                   make sure we don't leave them around. */
     531                                if( *recv_fd != -1 )
     532                                        close( *recv_fd );
     533                               
     534                                *recv_fd = *(int*) CMSG_DATA( cmsg );
     535                                fprintf( stderr, "pid %d received fd %d\n", (int) getpid(), *recv_fd );
     536                        }
     537       
     538        fprintf( stderr, "pid %d received: %s", (int) getpid(), buf );
     539        return g_strndup( buf, size - 2 );
    259540}
    260541
    261542gboolean ipc_master_read( gpointer data, gint source, b_input_condition cond )
    262543{
     544        struct bitlbee_child *child = data;
    263545        char *buf, **cmd;
    264546       
    265         if( ( buf = ipc_readline( source ) ) )
     547        if( ( buf = ipc_readline( source, &child->to_fd ) ) )
    266548        {
    267549                cmd = irc_parse_line( buf );
    268550                if( cmd )
    269551                {
    270                         ipc_command_exec( data, cmd, ipc_master_commands );
     552                        ipc_command_exec( child, cmd, ipc_master_commands );
    271553                        g_free( cmd );
    272554                }
     
    285567        char *buf, **cmd;
    286568       
    287         if( ( buf = ipc_readline( source ) ) )
     569        if( ( buf = ipc_readline( source, &ipc_child_recv_fd ) ) )
    288570        {
    289571                cmd = irc_parse_line( buf );
     
    392674                        next = l->next;
    393675                        if( write( c->ipc_fd, msg_buf, msg_len ) <= 0 )
    394                         {
    395676                                ipc_master_free_one( c );
    396                                 child_list = g_slist_remove( child_list, c );
    397                         }
    398677                }
    399678        }
     
    413692}
    414693
     694static gboolean ipc_send_fd( int fd, int send_fd )
     695{
     696        struct msghdr msg;
     697        struct iovec iov;
     698        char ccmsg[CMSG_SPACE(sizeof(fd))];
     699        struct cmsghdr *cmsg;
     700       
     701        memset( &msg, 0, sizeof( msg ) );
     702        iov.iov_base = "0x90\r\n";         /* Ja, noppes */
     703        iov.iov_len = 6;
     704        msg.msg_iov = &iov;
     705        msg.msg_iovlen = 1;
     706       
     707        msg.msg_control = ccmsg;
     708        msg.msg_controllen = sizeof( ccmsg );
     709        cmsg = CMSG_FIRSTHDR( &msg );
     710        cmsg->cmsg_level = SOL_SOCKET;
     711        cmsg->cmsg_type = SCM_RIGHTS;
     712        cmsg->cmsg_len = CMSG_LEN( sizeof( send_fd ) );
     713        *(int*)CMSG_DATA( cmsg ) = send_fd;
     714        msg.msg_controllen = cmsg->cmsg_len;
     715       
     716        return sendmsg( fd, &msg, 0 ) == 6;
     717}
     718
    415719void ipc_master_free_one( struct bitlbee_child *c )
    416720{
     721        GSList *l;
     722       
    417723        b_event_remove( c->ipc_inpa );
    418724        closesocket( c->ipc_fd );
     725       
     726        if( c->to_fd != -1 )
     727                close( c->to_fd );
    419728       
    420729        g_free( c->host );
    421730        g_free( c->nick );
    422731        g_free( c->realname );
     732        g_free( c->password );
    423733        g_free( c );
     734       
     735        child_list = g_slist_remove( child_list, c );
     736       
     737        /* Also, if any child has a reference to this one, remove it. */
     738        for( l = child_list; l; l = l->next )
     739        {
     740                struct bitlbee_child *oc = l->data;
     741               
     742                if( oc->to_child == c )
     743                        ipc_master_takeover_fail( oc, FALSE );
     744        }
    424745}
    425746
     
    435756                {
    436757                        ipc_master_free_one( c );
    437                         child_list = g_slist_remove( child_list, c );
    438758                        break;
    439759                }
     
    443763void ipc_master_free_all()
    444764{
    445         GSList *l;
    446        
    447         for( l = child_list; l; l = l->next )
    448                 ipc_master_free_one( l->data );
    449        
    450         g_slist_free( child_list );
    451         child_list = NULL;
     765        while( child_list )
     766                ipc_master_free_one( child_list->data );
    452767}
    453768
     
    506821        struct bitlbee_child *child = g_new0( struct bitlbee_child, 1 );
    507822       
     823        child->to_fd = -1;
    508824        child->ipc_fd = accept( serversock, NULL, 0 );
    509        
    510825        if( child->ipc_fd == -1 )
    511826        {
     
    516831        child->ipc_inpa = b_input_add( child->ipc_fd, B_EV_IO_READ, ipc_master_read, child );
    517832       
    518         child_list = g_slist_append( child_list, child );
     833        child_list = g_slist_prepend( child_list, child );
    519834       
    520835        return TRUE;
     
    598913                }
    599914                child->ipc_inpa = b_input_add( child->ipc_fd, B_EV_IO_READ, ipc_master_read, child );
    600                
    601                 child_list = g_slist_append( child_list, child );
     915                child->to_fd = -1;
     916               
     917                child_list = g_slist_prepend( child_list, child );
    602918        }
    603919       
  • ipc.h

    r9a9b520 rf3b6764  
    3737        char *nick;
    3838        char *realname;
     39       
     40        char *password;
     41       
     42        /* For takeovers: */
     43        struct bitlbee_child *to_child;
     44        int to_fd;
    3945};
    4046
  • irc.h

    r9a9b520 rf3b6764  
    8787        gint w_watch_source_id;
    8888        gint ping_source_id;
     89        gint login_source_id; /* To slightly delay some events at login time. */
    8990       
    9091        struct bee *b;
  • irc_user.c

    r9a9b520 rf3b6764  
    2525
    2626#include "bitlbee.h"
     27#include "ipc.h"
    2728
    2829irc_user_t *irc_user_new( irc_t *irc, const char *nick )
     
    160161        irc->users = g_slist_insert_sorted( irc->users, iu, irc_user_cmp );
    161162       
     163        if( iu == irc->user )
     164                ipc_to_master_str( "NICK :%s\r\n", new );
     165       
    162166        return 1;
    163167}
  • root_commands.c

    r9a9b520 rf3b6764  
    105105{
    106106        storage_status_t status;
    107         char *account_on[] = { "account", "on", NULL };
    108107        gboolean load = TRUE;
    109108        char *password = cmd[1];
     
    158157                irc_umode_set( irc, "+R", 1 );
    159158                irc_channel_auto_joins( irc, NULL );
    160                 if( load && set_getbool( &irc->b->set, "auto_connect" ) )
    161                         cmd_account( irc, account_on );
     159               
     160                if( ipc_child_identify( irc ) )
     161                {
     162                        if( load && set_getbool( &irc->b->set, "auto_connect" ) )
     163                                irc->login_source_id = b_timeout_add( 200,
     164                                        cmd_identify_finish, irc );
     165                }
     166                else if( load && set_getbool( &irc->b->set, "auto_connect" ) )
     167                        cmd_identify_finish( irc, 0, 0 );
     168               
    162169                break;
    163170        case STORAGE_OTHER_ERROR:
     
    166173                break;
    167174        }
     175}
     176
     177gboolean cmd_identify_finish( gpointer data, gint fd, b_input_condition cond )
     178{
     179        char *account_on[] = { "account", "on", NULL };
     180        irc_t *irc = data;
     181       
     182        cmd_account( irc, account_on );
     183       
     184        irc->login_source_id = -1;
     185        return FALSE;
    168186}
    169187
     
    672690        else if( iu == irc->user )
    673691        {
    674                 irc_usermsg( irc, "Nick `%s' can't be changed", cmd[1] );
     692                irc_usermsg( irc, "Use /nick to change your own nickname" );
    675693        }
    676694        else if( !nick_ok( cmd[2] ) )
Note: See TracChangeset for help on using the changeset viewer.