- Timestamp:
- 2010-07-09T23:39:25Z (14 years ago)
- 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. - File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
ipc.c
r9a9b520 rf3b6764 33 33 34 34 GSList *child_list = NULL; 35 static int ipc_child_recv_fd = -1; 36 37 static void ipc_master_takeover_fail( struct bitlbee_child *child, gboolean both ); 38 static gboolean ipc_send_fd( int fd, int send_fd ); 35 39 36 40 static void ipc_master_cmd_client( irc_t *data, char **cmd ) … … 49 53 } 50 54 55 /* CLIENT == On initial connects, HELLO is after /RESTARTs. */ 51 56 if( g_strcasecmp( cmd[0], "CLIENT" ) == 0 ) 52 57 ipc_to_children_str( "OPERMSG :Client connecting (PID=%d): %s@%s (%s)\r\n", … … 54 59 } 55 60 61 static 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 56 72 static void ipc_master_cmd_die( irc_t *data, char **cmd ) 57 73 { … … 110 126 global.restart = -1; 111 127 bitlbee_shutdown( NULL, -1, 0 ); 128 } 129 130 void 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 169 void 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 } 112 217 } 113 218 … … 115 220 { "client", 3, ipc_master_cmd_client, 0 }, 116 221 { "hello", 0, ipc_master_cmd_client, 0 }, 222 { "nick", 1, ipc_master_cmd_nick, 0 }, 117 223 { "die", 0, ipc_master_cmd_die, 0 }, 118 224 { "deaf", 0, ipc_master_cmd_deaf, 0 }, … … 123 229 { "kill", 2, NULL, IPC_CMD_TO_CHILDREN }, 124 230 { "restart", 0, ipc_master_cmd_restart, 0 }, 231 { "identify", 2, ipc_master_cmd_identify, 0 }, 232 { "takeover", 1, ipc_master_cmd_takeover, 0 }, 125 233 { NULL } 126 234 }; … … 189 297 else 190 298 ipc_to_master_str( "HELLO %s %s :%s\r\n", irc->user->host, irc->user->nick, irc->user->fullname ); 299 } 300 301 static void ipc_child_cmd_takeover_yes( void *data ); 302 static void ipc_child_cmd_takeover_no( void *data ); 303 304 static 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 376 static 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 403 static 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 ); 191 407 } 192 408 … … 199 415 { "kill", 2, ipc_child_cmd_kill, 0 }, 200 416 { "hello", 0, ipc_child_cmd_hello, 0 }, 417 { "takeover", 1, ipc_child_cmd_takeover, 0 }, 201 418 { NULL } 202 419 }; 203 420 421 gboolean 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 436 static 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 } 204 458 205 459 static void ipc_command_exec( void *data, char **cmd, const command_t *commands ) … … 230 484 /* Return just one line. Returns NULL if something broke, an empty string 231 485 on temporary "errors" (EAGAIN and friends). */ 232 static char *ipc_readline( int fd ) 233 { 486 static 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; 234 492 char buf[513], *eol; 235 493 int size; … … 253 511 size = eol - buf + 2; 254 512 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 ) 256 523 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 ); 259 540 } 260 541 261 542 gboolean ipc_master_read( gpointer data, gint source, b_input_condition cond ) 262 543 { 544 struct bitlbee_child *child = data; 263 545 char *buf, **cmd; 264 546 265 if( ( buf = ipc_readline( source ) ) )547 if( ( buf = ipc_readline( source, &child->to_fd ) ) ) 266 548 { 267 549 cmd = irc_parse_line( buf ); 268 550 if( cmd ) 269 551 { 270 ipc_command_exec( data, cmd, ipc_master_commands );552 ipc_command_exec( child, cmd, ipc_master_commands ); 271 553 g_free( cmd ); 272 554 } … … 285 567 char *buf, **cmd; 286 568 287 if( ( buf = ipc_readline( source ) ) )569 if( ( buf = ipc_readline( source, &ipc_child_recv_fd ) ) ) 288 570 { 289 571 cmd = irc_parse_line( buf ); … … 392 674 next = l->next; 393 675 if( write( c->ipc_fd, msg_buf, msg_len ) <= 0 ) 394 {395 676 ipc_master_free_one( c ); 396 child_list = g_slist_remove( child_list, c );397 }398 677 } 399 678 } … … 413 692 } 414 693 694 static 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 415 719 void ipc_master_free_one( struct bitlbee_child *c ) 416 720 { 721 GSList *l; 722 417 723 b_event_remove( c->ipc_inpa ); 418 724 closesocket( c->ipc_fd ); 725 726 if( c->to_fd != -1 ) 727 close( c->to_fd ); 419 728 420 729 g_free( c->host ); 421 730 g_free( c->nick ); 422 731 g_free( c->realname ); 732 g_free( c->password ); 423 733 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 } 424 745 } 425 746 … … 435 756 { 436 757 ipc_master_free_one( c ); 437 child_list = g_slist_remove( child_list, c );438 758 break; 439 759 } … … 443 763 void ipc_master_free_all() 444 764 { 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 ); 452 767 } 453 768 … … 506 821 struct bitlbee_child *child = g_new0( struct bitlbee_child, 1 ); 507 822 823 child->to_fd = -1; 508 824 child->ipc_fd = accept( serversock, NULL, 0 ); 509 510 825 if( child->ipc_fd == -1 ) 511 826 { … … 516 831 child->ipc_inpa = b_input_add( child->ipc_fd, B_EV_IO_READ, ipc_master_read, child ); 517 832 518 child_list = g_slist_ append( child_list, child );833 child_list = g_slist_prepend( child_list, child ); 519 834 520 835 return TRUE; … … 598 913 } 599 914 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 ); 602 918 } 603 919
Note: See TracChangeset
for help on using the changeset viewer.