diff options
-rw-r--r-- | ipc.c | 79 |
1 files changed, 53 insertions, 26 deletions
@@ -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() |