From 6c2404e051cb6a235f985797c149af0791f44bbd Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Tue, 6 Jul 2010 22:44:52 +0100 Subject: First part of the handshake, including sending a file descriptor to the IPC master. --- bitlbee.c | 2 + bitlbee.h | 1 + ipc.c | 119 +++++++++++++++++++++++++++++++++++++++++++++++++++----- ipc.h | 6 +++ irc.h | 1 + root_commands.c | 25 ++++++++++-- 6 files changed, 140 insertions(+), 14 deletions(-) diff --git a/bitlbee.c b/bitlbee.c index d0d95e67..78f4b4e5 100644 --- a/bitlbee.c +++ b/bitlbee.c @@ -317,10 +317,12 @@ static gboolean bitlbee_io_new_client( gpointer data, gint fd, b_input_condition { struct bitlbee_child *child; + /* TODO: Stuff like this belongs in ipc.c. */ child = g_new0( struct bitlbee_child, 1 ); child->pid = client_pid; child->ipc_fd = fds[0]; child->ipc_inpa = b_input_add( child->ipc_fd, B_EV_IO_READ, ipc_master_read, child ); + child->to_fd = -1; child_list = g_slist_append( child_list, child ); log_message( LOGLVL_INFO, "Creating new subprocess with pid %d.", (int) client_pid ); diff --git a/bitlbee.h b/bitlbee.h index 5610d95b..9b35810f 100644 --- a/bitlbee.h +++ b/bitlbee.h @@ -162,6 +162,7 @@ gboolean bitlbee_io_current_client_write( gpointer data, gint source, b_input_co void root_command_string( irc_t *irc, char *command ); void root_command( irc_t *irc, char *command[] ); +gboolean cmd_identify_finish( gpointer data, gint fd, b_input_condition cond ); gboolean bitlbee_shutdown( gpointer data, gint fd, b_input_condition cond ); char *set_eval_root_nick( set_t *set, char *new_nick ); diff --git a/ipc.c b/ipc.c index 81c5b8b4..cdf6539d 100644 --- a/ipc.c +++ b/ipc.c @@ -111,6 +111,31 @@ void ipc_master_cmd_restart( irc_t *data, char **cmd ) bitlbee_shutdown( NULL, -1, 0 ); } +void ipc_master_cmd_identify( irc_t *data, char **cmd ) +{ + struct bitlbee_child *child = (void*) data, *old = NULL; + GSList *l; + + if( strcmp( child->nick, cmd[1] ) != 0 ) + return; + + g_free( child->password ); + child->password = g_strdup( cmd[2] ); + + for( l = child_list; l; l = l->next ) + { + old = l->data; + if( nick_cmp( old->nick, child->nick ) == 0 && child != old && + old->password && strcmp( old->password, child->password ) ) + break; + } + + if( old == NULL ) + return; + + child->to_child = old; +} + static const command_t ipc_master_commands[] = { { "client", 3, ipc_master_cmd_client, 0 }, { "hello", 0, ipc_master_cmd_client, 0 }, @@ -122,6 +147,7 @@ static const command_t ipc_master_commands[] = { { "rehash", 0, ipc_master_cmd_rehash, 0 }, { "kill", 2, NULL, IPC_CMD_TO_CHILDREN }, { "restart", 0, ipc_master_cmd_restart, 0 }, + { "identify", 2, ipc_master_cmd_identify, 0 }, { NULL } }; @@ -201,6 +227,22 @@ static const command_t ipc_child_commands[] = { { NULL } }; +static gboolean ipc_send_fd( int fd, int send_fd ); + +gboolean ipc_child_identify( irc_t *irc ) +{ + if( global.conf->runmode == RUNMODE_FORKDAEMON ) + { + if( !ipc_send_fd( global.listen_socket, irc->fd ) ) + ipc_child_disable(); + + ipc_to_master_str( "IDENTIFY %s :%s\r\n", irc->user->nick, irc->password ); + + return TRUE; + } + else + return FALSE; +} static void ipc_command_exec( void *data, char **cmd, const command_t *commands ) { @@ -229,8 +271,12 @@ static void ipc_command_exec( void *data, char **cmd, const command_t *commands /* Return just one line. Returns NULL if something broke, an empty string on temporary "errors" (EAGAIN and friends). */ -static char *ipc_readline( int fd ) +static char *ipc_readline( int fd, int *recv_fd ) { + struct msghdr msg; + struct iovec iov; + char ccmsg[CMSG_SPACE(sizeof(recv_fd))]; + struct cmsghdr *cmsg; char buf[513], *eol; int size; @@ -252,22 +298,44 @@ static char *ipc_readline( int fd ) else size = eol - buf + 2; - if( recv( fd, buf, size, 0 ) != size ) + iov.iov_base = buf; + iov.iov_len = size; + + memset( &msg, 0, sizeof( msg ) ); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = ccmsg; + msg.msg_controllen = sizeof( ccmsg ); + + if( recvmsg( fd, &msg, 0 ) != size ) return NULL; - else - return g_strndup( buf, size - 2 ); + + if( recv_fd ) + for( cmsg = CMSG_FIRSTHDR( &msg ); cmsg; cmsg = CMSG_NXTHDR( &msg, cmsg ) ) + if( cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS ) + { + /* Getting more than one shouldn't happen but if it does, + make sure we don't leave them around. */ + if( *recv_fd != -1 ) + close( *recv_fd ); + + *recv_fd = *(int*) CMSG_DATA( cmsg ); + } + + return g_strndup( buf, size - 2 ); } gboolean ipc_master_read( gpointer data, gint source, b_input_condition cond ) { + struct bitlbee_child *child = data; char *buf, **cmd; - if( ( buf = ipc_readline( source ) ) ) + if( ( buf = ipc_readline( source, &child->to_fd ) ) ) { cmd = irc_parse_line( buf ); if( cmd ) { - ipc_command_exec( data, cmd, ipc_master_commands ); + ipc_command_exec( child, cmd, ipc_master_commands ); g_free( cmd ); } g_free( buf ); @@ -283,8 +351,9 @@ gboolean ipc_master_read( gpointer data, gint source, b_input_condition cond ) gboolean ipc_child_read( gpointer data, gint source, b_input_condition cond ) { char *buf, **cmd; + int recv_fd = -1; - if( ( buf = ipc_readline( source ) ) ) + if( ( buf = ipc_readline( source, &recv_fd ) ) ) { cmd = irc_parse_line( buf ); if( cmd ) @@ -412,14 +481,43 @@ void ipc_to_children_str( char *format, ... ) g_free( msg_buf ); } +static gboolean ipc_send_fd( int fd, int send_fd ) +{ + struct msghdr msg; + struct iovec iov; + char ccmsg[CMSG_SPACE(sizeof(fd))]; + struct cmsghdr *cmsg; + + memset( &msg, 0, sizeof( msg ) ); + iov.iov_base = "0x90\r\n"; + iov.iov_len = 6; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + msg.msg_control = ccmsg; + msg.msg_controllen = sizeof( ccmsg ); + cmsg = CMSG_FIRSTHDR( &msg ); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN( sizeof( send_fd ) ); + *(int*)CMSG_DATA( cmsg ) = send_fd; + msg.msg_controllen = cmsg->cmsg_len; + + return sendmsg( fd, &msg, 0 ) == 6; +} + void ipc_master_free_one( struct bitlbee_child *c ) { b_event_remove( c->ipc_inpa ); closesocket( c->ipc_fd ); + if( c->to_fd != -1 ) + close( c->to_fd ); + g_free( c->host ); g_free( c->nick ); g_free( c->realname ); + g_free( c->password ); g_free( c ); } @@ -505,8 +603,8 @@ static gboolean new_ipc_client( gpointer data, gint serversock, b_input_conditio { struct bitlbee_child *child = g_new0( struct bitlbee_child, 1 ); + child->to_fd = -1; child->ipc_fd = accept( serversock, NULL, 0 ); - if( child->ipc_fd == -1 ) { log_message( LOGLVL_WARNING, "Unable to accept connection on UNIX domain socket: %s", strerror(errno) ); @@ -515,7 +613,7 @@ static gboolean new_ipc_client( gpointer data, gint serversock, b_input_conditio child->ipc_inpa = b_input_add( child->ipc_fd, B_EV_IO_READ, ipc_master_read, child ); - child_list = g_slist_append( child_list, child ); + child_list = g_slist_prepend( child_list, child ); return TRUE; } @@ -597,8 +695,9 @@ int ipc_master_load_state( char *statefile ) return 0; } child->ipc_inpa = b_input_add( child->ipc_fd, B_EV_IO_READ, ipc_master_read, child ); + child->to_fd = -1; - child_list = g_slist_append( child_list, child ); + child_list = g_slist_prepend( child_list, child ); } ipc_to_children_str( "HELLO\r\n" ); diff --git a/ipc.h b/ipc.h index 0e71c520..bf5f0454 100644 --- a/ipc.h +++ b/ipc.h @@ -36,6 +36,12 @@ struct bitlbee_child char *host; char *nick; char *realname; + + char *password; + + /* For takeovers: */ + struct bitlbee_child *to_child; + int to_fd; }; diff --git a/irc.h b/irc.h index 94738832..3b5e244f 100644 --- a/irc.h +++ b/irc.h @@ -86,6 +86,7 @@ typedef struct irc gint r_watch_source_id; gint w_watch_source_id; gint ping_source_id; + gint login_source_id; /* To slightly delay some events at login time. */ struct bee *b; } irc_t; diff --git a/root_commands.c b/root_commands.c index 62fe1e79..5d84c6d4 100644 --- a/root_commands.c +++ b/root_commands.c @@ -104,7 +104,6 @@ static void cmd_account( irc_t *irc, char **cmd ); static void cmd_identify( irc_t *irc, char **cmd ) { storage_status_t status; - char *account_on[] = { "account", "on", NULL }; gboolean load = TRUE; char *password = cmd[1]; @@ -157,8 +156,16 @@ static void cmd_identify( irc_t *irc, char **cmd ) irc->status |= USTATUS_IDENTIFIED; irc_umode_set( irc, "+R", 1 ); irc_channel_auto_joins( irc, NULL ); - if( load && set_getbool( &irc->b->set, "auto_connect" ) ) - cmd_account( irc, account_on ); + + if( ipc_child_identify( irc ) ) + { + if( load && set_getbool( &irc->b->set, "auto_connect" ) ) + irc->login_source_id = b_timeout_add( 200, + cmd_identify_finish, irc ); + } + else if( load && set_getbool( &irc->b->set, "auto_connect" ) ) + cmd_identify_finish( irc, 0, 0 ); + break; case STORAGE_OTHER_ERROR: default: @@ -167,6 +174,16 @@ static void cmd_identify( irc_t *irc, char **cmd ) } } +gboolean cmd_identify_finish( gpointer data, gint fd, b_input_condition cond ) +{ + char *account_on[] = { "account", "on", NULL }; + irc_t *irc = data; + + cmd_account( irc, account_on ); + + return FALSE; +} + static void cmd_register( irc_t *irc, char **cmd ) { if( global.conf->authmode == AUTHMODE_REGISTERED ) @@ -671,7 +688,7 @@ static void cmd_rename( irc_t *irc, char **cmd ) } else if( iu == irc->user ) { - irc_usermsg( irc, "Nick `%s' can't be changed", cmd[1] ); + irc_usermsg( irc, "Use /nick to change your own nickname" ); } else if( !nick_ok( cmd[2] ) ) { -- cgit v1.2.3 From 0b09da0dbbdf294799bd5335b327c2601f263b01 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Wed, 7 Jul 2010 00:10:17 +0100 Subject: It works! Fragile like hell though, and without any confirmation or whatever. --- ipc.c | 106 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 98 insertions(+), 8 deletions(-) diff --git a/ipc.c b/ipc.c index cdf6539d..5bc22463 100644 --- a/ipc.c +++ b/ipc.c @@ -32,6 +32,7 @@ #endif GSList *child_list = NULL; +static int ipc_child_recv_fd = -1; static void ipc_master_cmd_client( irc_t *data, char **cmd ) { @@ -114,6 +115,7 @@ void ipc_master_cmd_restart( irc_t *data, char **cmd ) void ipc_master_cmd_identify( irc_t *data, char **cmd ) { struct bitlbee_child *child = (void*) data, *old = NULL; + char *resp; GSList *l; if( strcmp( child->nick, cmd[1] ) != 0 ) @@ -126,14 +128,61 @@ void ipc_master_cmd_identify( irc_t *data, char **cmd ) { old = l->data; if( nick_cmp( old->nick, child->nick ) == 0 && child != old && - old->password && strcmp( old->password, child->password ) ) + old->password && strcmp( old->password, child->password ) == 0 ) break; } - if( old == NULL ) - return; - child->to_child = old; + + if( l ) + { + resp = "TAKEOVER INIT\r\n"; + } + else + { + /* Won't need the fd since we can't send it anywhere. */ + close( child->to_fd ); + child->to_fd = -1; + resp = "TAKEOVER NO\r\n"; + } + + if( write( child->ipc_fd, resp, strlen( resp ) ) != strlen( resp ) ) + { + ipc_master_free_one( child ); + child_list = g_slist_remove( child_list, child ); + } +} + +static gboolean ipc_send_fd( int fd, int send_fd ); + +void ipc_master_cmd_takeover( irc_t *data, char **cmd ) +{ + struct bitlbee_child *child = (void*) data; + + /* TODO: Check if child->to_child is still valid, etc. */ + if( strcmp( cmd[1], "AUTH" ) == 0 ) + { + if( child->to_child && + child->nick && child->to_child->nick && cmd[2] && + child->password && child->to_child->password && cmd[3] && + strcmp( child->nick, child->to_child->nick ) == 0 && + strcmp( child->nick, cmd[2] ) == 0 && + strcmp( child->password, child->to_child->password ) == 0 && + strcmp( child->password, cmd[3] ) == 0 ) + { + char *s; + + ipc_send_fd( child->to_child->ipc_fd, child->to_fd ); + + s = irc_build_line( cmd ); + if( write( child->to_child->ipc_fd, s, strlen( s ) ) != strlen( s ) ) + { + ipc_master_free_one( child ); + child_list = g_slist_remove( child_list, child ); + } + g_free( s ); + } + } } static const command_t ipc_master_commands[] = { @@ -148,6 +197,7 @@ static const command_t ipc_master_commands[] = { { "kill", 2, NULL, IPC_CMD_TO_CHILDREN }, { "restart", 0, ipc_master_cmd_restart, 0 }, { "identify", 2, ipc_master_cmd_identify, 0 }, + { "takeover", 1, ipc_master_cmd_takeover, 0 }, { NULL } }; @@ -216,6 +266,46 @@ static void ipc_child_cmd_hello( irc_t *irc, char **cmd ) ipc_to_master_str( "HELLO %s %s :%s\r\n", irc->user->host, irc->user->nick, irc->user->fullname ); } +static void ipc_child_cmd_takeover( irc_t *irc, char **cmd ) +{ + if( strcmp( cmd[1], "NO" ) == 0 ) + { + /* No takeover, finish the login. */ + } + else if( strcmp( cmd[1], "INIT" ) == 0 ) + { + ipc_to_master_str( "TAKEOVER AUTH %s :%s\r\n", + irc->user->nick, irc->password ); + + /* Drop credentials, we'll shut down soon and shouldn't overwrite + any settings. */ + /* TODO: irc_setpass() should do all of this. */ + irc_usermsg( irc, "Trying to take over existing session" ); + /** NOT YET + irc_setpass( irc, NULL ); + irc->status &= ~USTATUS_IDENTIFIED; + irc_umode_set( irc, "-R", 1 ); + */ + } + else if( strcmp( cmd[1], "AUTH" ) == 0 ) + { + if( irc->password && cmd[2] && cmd[3] && + ipc_child_recv_fd != -1 && + strcmp( irc->user->nick, cmd[2] ) == 0 && + strcmp( irc->password, cmd[3] ) == 0 ) + { + fprintf( stderr, "TO\n" ); + b_event_remove( irc->r_watch_source_id ); + closesocket( irc->fd ); + irc->fd = ipc_child_recv_fd; + irc->r_watch_source_id = b_input_add( irc->fd, B_EV_IO_READ, bitlbee_io_current_client_read, irc ); + ipc_child_recv_fd = -1; + } + fprintf( stderr, "%s %s %s\n", irc->password, cmd[2], cmd[3] ); + fprintf( stderr, "%d %s %s\n", ipc_child_recv_fd, irc->user->nick, irc->password ); + } +} + static const command_t ipc_child_commands[] = { { "die", 0, ipc_child_cmd_die, 0 }, { "wallops", 1, ipc_child_cmd_wallops, 0 }, @@ -224,11 +314,10 @@ static const command_t ipc_child_commands[] = { { "rehash", 0, ipc_child_cmd_rehash, 0 }, { "kill", 2, ipc_child_cmd_kill, 0 }, { "hello", 0, ipc_child_cmd_hello, 0 }, + { "takeover", 1, ipc_child_cmd_takeover, 0 }, { NULL } }; -static gboolean ipc_send_fd( int fd, int send_fd ); - gboolean ipc_child_identify( irc_t *irc ) { if( global.conf->runmode == RUNMODE_FORKDAEMON ) @@ -320,8 +409,10 @@ static char *ipc_readline( int fd, int *recv_fd ) close( *recv_fd ); *recv_fd = *(int*) CMSG_DATA( cmsg ); + fprintf( stderr, "pid %d received fd %d\n", (int) getpid(), *recv_fd ); } + fprintf( stderr, "pid %d received: %s", (int) getpid(), buf ); return g_strndup( buf, size - 2 ); } @@ -351,9 +442,8 @@ gboolean ipc_master_read( gpointer data, gint source, b_input_condition cond ) gboolean ipc_child_read( gpointer data, gint source, b_input_condition cond ) { char *buf, **cmd; - int recv_fd = -1; - if( ( buf = ipc_readline( source, &recv_fd ) ) ) + if( ( buf = ipc_readline( source, &ipc_child_recv_fd ) ) ) { cmd = irc_parse_line( buf ); if( cmd ) -- cgit v1.2.3 From f545372483cef6a2414bc5bf21260579151a627e Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Fri, 9 Jul 2010 00:10:43 +0100 Subject: Ask for confirmation. Generally working fairly well now, but definitely fragile. --- ipc.c | 121 +++++++++++++++++++++++++++++++++++++++++++++----------- root_commands.c | 1 + 2 files changed, 100 insertions(+), 22 deletions(-) diff --git a/ipc.c b/ipc.c index 5bc22463..6903c898 100644 --- a/ipc.c +++ b/ipc.c @@ -132,11 +132,11 @@ void ipc_master_cmd_identify( irc_t *data, char **cmd ) break; } - child->to_child = old; - - if( l ) + if( l && !child->to_child && !old->to_child ) { resp = "TAKEOVER INIT\r\n"; + child->to_child = old; + old->to_child = child; } else { @@ -158,6 +158,7 @@ static gboolean ipc_send_fd( int fd, int send_fd ); void ipc_master_cmd_takeover( irc_t *data, char **cmd ) { struct bitlbee_child *child = (void*) data; + char *fwd = NULL; /* TODO: Check if child->to_child is still valid, etc. */ if( strcmp( cmd[1], "AUTH" ) == 0 ) @@ -170,19 +171,37 @@ void ipc_master_cmd_takeover( irc_t *data, char **cmd ) strcmp( child->password, child->to_child->password ) == 0 && strcmp( child->password, cmd[3] ) == 0 ) { - char *s; - ipc_send_fd( child->to_child->ipc_fd, child->to_fd ); - s = irc_build_line( cmd ); - if( write( child->to_child->ipc_fd, s, strlen( s ) ) != strlen( s ) ) + fwd = irc_build_line( cmd ); + if( write( child->to_child->ipc_fd, fwd, strlen( fwd ) ) != strlen( fwd ) ) { ipc_master_free_one( child ); child_list = g_slist_remove( child_list, child ); } - g_free( s ); + g_free( fwd ); } } + else if( strcmp( cmd[1], "DONE" ) == 0 || strcmp( cmd[1], "FAIL" ) == 0 ) + { + int fd; + + /* The copy was successful (or not), we don't need it anymore. */ + close( child->to_fd ); + child->to_fd = -1; + + /* Pass it through to the other party, and flush all state. */ + fwd = irc_build_line( cmd ); + fd = child->to_child->ipc_fd; + child->to_child->to_child = NULL; + child->to_child = NULL; + if( write( fd, fwd, strlen( fwd ) ) != strlen( fwd ) ) + { + ipc_master_free_one( child ); + child_list = g_slist_remove( child_list, child ); + } + g_free( fwd ); + } } static const command_t ipc_master_commands[] = { @@ -266,46 +285,104 @@ static void ipc_child_cmd_hello( irc_t *irc, char **cmd ) ipc_to_master_str( "HELLO %s %s :%s\r\n", irc->user->host, irc->user->nick, irc->user->fullname ); } +static void ipc_child_cmd_takeover_yes( void *data ); +static void ipc_child_cmd_takeover_no( void *data ); + static void ipc_child_cmd_takeover( irc_t *irc, char **cmd ) { if( strcmp( cmd[1], "NO" ) == 0 ) { + /* Master->New connection */ /* No takeover, finish the login. */ } else if( strcmp( cmd[1], "INIT" ) == 0 ) { - ipc_to_master_str( "TAKEOVER AUTH %s :%s\r\n", - irc->user->nick, irc->password ); + /* Master->New connection */ + /* Offer to take over the old session, unless for some reason + we're already logging into IM connections. */ + if( irc->login_source_id != -1 ) + query_add( irc, NULL, + "You're already connected to this server. " + "Would you like to take over this session?", + ipc_child_cmd_takeover_yes, + ipc_child_cmd_takeover_no, irc ); - /* Drop credentials, we'll shut down soon and shouldn't overwrite - any settings. */ - /* TODO: irc_setpass() should do all of this. */ - irc_usermsg( irc, "Trying to take over existing session" ); - /** NOT YET - irc_setpass( irc, NULL ); - irc->status &= ~USTATUS_IDENTIFIED; - irc_umode_set( irc, "-R", 1 ); - */ + /* This one's going to connect to accounts, avoid that. */ + b_event_remove( irc->login_source_id ); + irc->login_source_id = -1; } else if( strcmp( cmd[1], "AUTH" ) == 0 ) { + /* Master->Old connection */ if( irc->password && cmd[2] && cmd[3] && ipc_child_recv_fd != -1 && strcmp( irc->user->nick, cmd[2] ) == 0 && strcmp( irc->password, cmd[3] ) == 0 ) { - fprintf( stderr, "TO\n" ); + GSList *l; + b_event_remove( irc->r_watch_source_id ); closesocket( irc->fd ); irc->fd = ipc_child_recv_fd; irc->r_watch_source_id = b_input_add( irc->fd, B_EV_IO_READ, bitlbee_io_current_client_read, irc ); ipc_child_recv_fd = -1; + + for( l = irc->channels; l; l = l->next ) + { + irc_channel_t *ic = l->data; + if( ic->flags & IRC_CHANNEL_JOINED ) + irc_send_join( ic, irc->user ); + } + irc_usermsg( irc, "You've successfully taken over your old session" ); + + ipc_to_master_str( "TAKEOVER DONE\r\n" ); } - fprintf( stderr, "%s %s %s\n", irc->password, cmd[2], cmd[3] ); - fprintf( stderr, "%d %s %s\n", ipc_child_recv_fd, irc->user->nick, irc->password ); + else + { + ipc_to_master_str( "TAKEOVER FAIL\r\n" ); + } + } + else if( strcmp( cmd[1], "DONE" ) == 0 ) + { + /* Master->New connection (now taken over by old process) */ + irc_free( irc ); + } + else if( strcmp( cmd[1], "FAIL" ) == 0 ) + { + /* Master->New connection */ + irc_usermsg( irc, "Could not take over old session" ); } } +static void ipc_child_cmd_takeover_yes( void *data ) +{ + irc_t *irc = data; + GSList *l; + + /* Master->New connection */ + ipc_to_master_str( "TAKEOVER AUTH %s :%s\r\n", + irc->user->nick, irc->password ); + + /* Drop credentials, we'll shut down soon and shouldn't overwrite + any settings. */ + irc_usermsg( irc, "Trying to take over existing session" ); + + /* TODO: irc_setpass() should do all of this. */ + irc_setpass( irc, NULL ); + irc->status &= ~USTATUS_IDENTIFIED; + irc_umode_set( irc, "-R", 1 ); + + for( l = irc->channels; l; l = l->next ) + irc_channel_del_user( l->data, irc->user, IRC_CDU_KICK, + "Switching to old session" ); +} + +static void ipc_child_cmd_takeover_no( void *data ) +{ + ipc_to_master_str( "TAKEOVER NO\r\n" ); + cmd_identify_finish( data, 0, 0 ); +} + static const command_t ipc_child_commands[] = { { "die", 0, ipc_child_cmd_die, 0 }, { "wallops", 1, ipc_child_cmd_wallops, 0 }, diff --git a/root_commands.c b/root_commands.c index 5d84c6d4..daaebb95 100644 --- a/root_commands.c +++ b/root_commands.c @@ -181,6 +181,7 @@ gboolean cmd_identify_finish( gpointer data, gint fd, b_input_condition cond ) cmd_account( irc, account_on ); + irc->login_source_id = -1; return FALSE; } -- cgit v1.2.3 From 9595d2b9420afc1044c0d5ee93311b8c4ee6dec2 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Fri, 9 Jul 2010 09:25:25 +0100 Subject: Also sync umodes. --- ipc.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ipc.c b/ipc.c index 6903c898..aad05bbf 100644 --- a/ipc.c +++ b/ipc.c @@ -321,12 +321,18 @@ static void ipc_child_cmd_takeover( irc_t *irc, char **cmd ) { GSList *l; + /* TODO: Move this all into irc_switch_fd() or so and + irc_sync() */ b_event_remove( irc->r_watch_source_id ); closesocket( irc->fd ); irc->fd = ipc_child_recv_fd; irc->r_watch_source_id = b_input_add( irc->fd, B_EV_IO_READ, bitlbee_io_current_client_read, irc ); ipc_child_recv_fd = -1; + irc_write( irc, ":%s!%s@%s MODE %s :%s", irc->user->nick, + irc->user->user, irc->user->host, irc->user->nick, + irc->umode ); + for( l = irc->channels; l; l = l->next ) { irc_channel_t *ic = l->data; @@ -375,6 +381,10 @@ static void ipc_child_cmd_takeover_yes( void *data ) for( l = irc->channels; l; l = l->next ) irc_channel_del_user( l->data, irc->user, IRC_CDU_KICK, "Switching to old session" ); + + irc_write( irc, ":%s!%s@%s MODE %s :-%s", irc->user->nick, + irc->user->user, irc->user->host, irc->user->nick, + irc->umode ); } static void ipc_child_cmd_takeover_no( void *data ) -- cgit v1.2.3 From debe871bbd96a1d4215983265d97b8f0df7a4fbf Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Fri, 9 Jul 2010 22:06:38 +0100 Subject: Inform IPC master about nick changes. --- ipc.c | 13 +++++++++++++ irc_user.c | 4 ++++ 2 files changed, 17 insertions(+) diff --git a/ipc.c b/ipc.c index aad05bbf..3c98c41e 100644 --- a/ipc.c +++ b/ipc.c @@ -49,11 +49,23 @@ static void ipc_master_cmd_client( irc_t *data, char **cmd ) child->realname = g_strdup( cmd[3] ); } + /* CLIENT == On initial connects, HELLO is after /RESTARTs. */ if( g_strcasecmp( cmd[0], "CLIENT" ) == 0 ) ipc_to_children_str( "OPERMSG :Client connecting (PID=%d): %s@%s (%s)\r\n", (int) ( child ? child->pid : -1 ), cmd[2], cmd[1], cmd[3] ); } +static void ipc_master_cmd_nick( irc_t *data, char **cmd ) +{ + struct bitlbee_child *child = (void*) data; + + if( child && cmd[1] ) + { + g_free( child->nick ); + child->nick = g_strdup( cmd[1] ); + } +} + static void ipc_master_cmd_die( irc_t *data, char **cmd ) { if( global.conf->runmode == RUNMODE_FORKDAEMON ) @@ -207,6 +219,7 @@ void ipc_master_cmd_takeover( irc_t *data, char **cmd ) static const command_t ipc_master_commands[] = { { "client", 3, ipc_master_cmd_client, 0 }, { "hello", 0, ipc_master_cmd_client, 0 }, + { "nick", 1, ipc_master_cmd_nick, 0 }, { "die", 0, ipc_master_cmd_die, 0 }, { "deaf", 0, ipc_master_cmd_deaf, 0 }, { "wallops", 1, NULL, IPC_CMD_TO_CHILDREN }, diff --git a/irc_user.c b/irc_user.c index cb7ea1e7..70f324c7 100644 --- a/irc_user.c +++ b/irc_user.c @@ -24,6 +24,7 @@ */ #include "bitlbee.h" +#include "ipc.h" irc_user_t *irc_user_new( irc_t *irc, const char *nick ) { @@ -157,6 +158,9 @@ int irc_user_set_nick( irc_user_t *iu, const char *new ) g_hash_table_insert( irc->nick_user_hash, iu->key, iu ); irc->users = g_slist_insert_sorted( irc->users, iu, irc_user_cmp ); + if( iu == irc->user ) + ipc_to_master_str( "NICK :%s\r\n", new ); + return 1; } -- cgit v1.2.3 From c5bff810e6919dc3daf7f82f761197a27f04538b Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 10 Jul 2010 00:24:23 +0100 Subject: More state consistency checks/error handling. --- ipc.c | 79 +++++++++++++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 53 insertions(+), 26 deletions(-) diff --git a/ipc.c b/ipc.c index 3c98c41e..b4b31da1 100644 --- a/ipc.c +++ b/ipc.c @@ -34,6 +34,9 @@ GSList *child_list = NULL; static int ipc_child_recv_fd = -1; +static void ipc_master_takeover_fail( struct bitlbee_child *child, gboolean both ); +static gboolean ipc_send_fd( int fd, int send_fd ); + static void ipc_master_cmd_client( irc_t *data, char **cmd ) { /* Normally data points at an irc_t block, but for the IPC master @@ -153,28 +156,28 @@ void ipc_master_cmd_identify( irc_t *data, char **cmd ) else { /* Won't need the fd since we can't send it anywhere. */ - close( child->to_fd ); + closesocket( child->to_fd ); child->to_fd = -1; resp = "TAKEOVER NO\r\n"; } if( write( child->ipc_fd, resp, strlen( resp ) ) != strlen( resp ) ) - { ipc_master_free_one( child ); - child_list = g_slist_remove( child_list, child ); - } } -static gboolean ipc_send_fd( int fd, int send_fd ); void ipc_master_cmd_takeover( irc_t *data, char **cmd ) { struct bitlbee_child *child = (void*) data; char *fwd = NULL; - /* TODO: Check if child->to_child is still valid, etc. */ + if( child->to_child == NULL || + g_slist_find( child_list, child->to_child ) == NULL ) + return ipc_master_takeover_fail( child, FALSE ); + if( strcmp( cmd[1], "AUTH" ) == 0 ) { + /* New connection -> Master */ if( child->to_child && child->nick && child->to_child->nick && cmd[2] && child->password && child->to_child->password && cmd[3] && @@ -187,19 +190,19 @@ void ipc_master_cmd_takeover( irc_t *data, char **cmd ) fwd = irc_build_line( cmd ); if( write( child->to_child->ipc_fd, fwd, strlen( fwd ) ) != strlen( fwd ) ) - { ipc_master_free_one( child ); - child_list = g_slist_remove( child_list, child ); - } g_free( fwd ); } + else + return ipc_master_takeover_fail( child, TRUE ); } else if( strcmp( cmd[1], "DONE" ) == 0 || strcmp( cmd[1], "FAIL" ) == 0 ) { + /* Old connection -> Master */ int fd; /* The copy was successful (or not), we don't need it anymore. */ - close( child->to_fd ); + closesocket( child->to_fd ); child->to_fd = -1; /* Pass it through to the other party, and flush all state. */ @@ -208,10 +211,7 @@ void ipc_master_cmd_takeover( irc_t *data, char **cmd ) child->to_child->to_child = NULL; child->to_child = NULL; if( write( fd, fwd, strlen( fwd ) ) != strlen( fwd ) ) - { ipc_master_free_one( child ); - child_list = g_slist_remove( child_list, child ); - } g_free( fwd ); } } @@ -342,7 +342,7 @@ static void ipc_child_cmd_takeover( irc_t *irc, char **cmd ) irc->r_watch_source_id = b_input_add( irc->fd, B_EV_IO_READ, bitlbee_io_current_client_read, irc ); ipc_child_recv_fd = -1; - irc_write( irc, ":%s!%s@%s MODE %s :%s", irc->user->nick, + irc_write( irc, ":%s!%s@%s MODE %s :+%s", irc->user->nick, irc->user->user, irc->user->host, irc->user->nick, irc->umode ); @@ -433,6 +433,29 @@ gboolean ipc_child_identify( irc_t *irc ) return FALSE; } +static void ipc_master_takeover_fail( struct bitlbee_child *child, gboolean both ) +{ + if( child == NULL || g_slist_find( child_list, child ) == NULL ) + return; + + if( both && child->to_child != NULL ) + ipc_master_takeover_fail( child->to_child, FALSE ); + + if( child->to_fd > -1 ) + { + /* Send this error only to the new connection, which can be + recognised by to_fd being set. */ + if( write( child->ipc_fd, "TAKEOVER FAIL\r\n", 15 ) != 15 ) + { + ipc_master_free_one( child ); + return; + } + close( child->to_fd ); + child->to_fd = -1; + } + child->to_child = NULL; +} + static void ipc_command_exec( void *data, char **cmd, const command_t *commands ) { int i, j; @@ -650,10 +673,7 @@ void ipc_to_children_str( char *format, ... ) next = l->next; if( write( c->ipc_fd, msg_buf, msg_len ) <= 0 ) - { ipc_master_free_one( c ); - child_list = g_slist_remove( child_list, c ); - } } } else if( global.conf->runmode == RUNMODE_DAEMON ) @@ -679,7 +699,7 @@ static gboolean ipc_send_fd( int fd, int send_fd ) struct cmsghdr *cmsg; memset( &msg, 0, sizeof( msg ) ); - iov.iov_base = "0x90\r\n"; + iov.iov_base = "0x90\r\n"; /* Ja, noppes */ iov.iov_len = 6; msg.msg_iov = &iov; msg.msg_iovlen = 1; @@ -698,6 +718,8 @@ static gboolean ipc_send_fd( int fd, int send_fd ) void ipc_master_free_one( struct bitlbee_child *c ) { + GSList *l; + b_event_remove( c->ipc_inpa ); closesocket( c->ipc_fd ); @@ -709,6 +731,17 @@ void ipc_master_free_one( struct bitlbee_child *c ) g_free( c->realname ); g_free( c->password ); g_free( c ); + + child_list = g_slist_remove( child_list, c ); + + /* Also, if any child has a reference to this one, remove it. */ + for( l = child_list; l; l = l->next ) + { + struct bitlbee_child *oc = l->data; + + if( oc->to_child == c ) + ipc_master_takeover_fail( oc, FALSE ); + } } void ipc_master_free_fd( int fd ) @@ -722,7 +755,6 @@ void ipc_master_free_fd( int fd ) if( c->ipc_fd == fd ) { ipc_master_free_one( c ); - child_list = g_slist_remove( child_list, c ); break; } } @@ -730,13 +762,8 @@ void ipc_master_free_fd( int fd ) void ipc_master_free_all() { - GSList *l; - - for( l = child_list; l; l = l->next ) - ipc_master_free_one( l->data ); - - g_slist_free( child_list ); - child_list = NULL; + while( child_list ) + ipc_master_free_one( child_list->data ); } void ipc_child_disable() -- cgit v1.2.3