Mercurial > pidgin
comparison libpurple/protocols/gg/lib/resolver.c @ 32819:2c6510167895 default tip
propagate from branch 'im.pidgin.pidgin.2.x.y' (head 3315c5dfbd0ad16511bdcf865e5b07c02d07df24)
to branch 'im.pidgin.pidgin' (head cbd1eda6bcbf0565ae7766396bb8f6f419cb6a9a)
| author | Elliott Sales de Andrade <qulogic@pidgin.im> |
|---|---|
| date | Sat, 02 Jun 2012 02:30:49 +0000 |
| parents | e2ff2ac0e022 |
| children |
comparison
equal
deleted
inserted
replaced
| 32818:01ff09d4a463 | 32819:2c6510167895 |
|---|---|
| 24 * \file resolver.c | 24 * \file resolver.c |
| 25 * | 25 * |
| 26 * \brief Funkcje rozwiązywania nazw | 26 * \brief Funkcje rozwiązywania nazw |
| 27 */ | 27 */ |
| 28 | 28 |
| 29 #ifndef _WIN32 | |
| 30 # include <sys/wait.h> | |
| 31 # include <netdb.h> | |
| 32 #endif | |
| 33 #include <errno.h> | 29 #include <errno.h> |
| 34 #include <stdlib.h> | 30 #include <stdlib.h> |
| 35 #include <string.h> | 31 #include <string.h> |
| 36 #include <unistd.h> | 32 #include <unistd.h> |
| 37 #ifndef _WIN32 | |
| 38 # include <signal.h> | |
| 39 # include <netinet/in.h> | |
| 40 # include <arpa/inet.h> | |
| 41 #endif | |
| 42 | 33 |
| 43 #include "libgadu.h" | 34 #include "libgadu.h" |
| 44 #include "libgadu-config.h" | |
| 45 #include "resolver.h" | 35 #include "resolver.h" |
| 46 #include "compat.h" | 36 #include "compat.h" |
| 47 #include "session.h" | 37 #include "session.h" |
| 38 | |
| 39 #ifdef GG_CONFIG_HAVE_FORK | |
| 40 #include <sys/wait.h> | |
| 41 #include <signal.h> | |
| 42 #endif | |
| 48 | 43 |
| 49 /** Sposób rozwiązywania nazw serwerów */ | 44 /** Sposób rozwiązywania nazw serwerów */ |
| 50 static gg_resolver_t gg_global_resolver_type = GG_RESOLVER_DEFAULT; | 45 static gg_resolver_t gg_global_resolver_type = GG_RESOLVER_DEFAULT; |
| 51 | 46 |
| 52 /** Funkcja rozpoczynająca rozwiązywanie nazwy */ | 47 /** Funkcja rozpoczynająca rozwiązywanie nazwy */ |
| 247 | 242 |
| 248 return 0; | 243 return 0; |
| 249 #endif /* GG_CONFIG_HAVE_GETHOSTBYNAME_R */ | 244 #endif /* GG_CONFIG_HAVE_GETHOSTBYNAME_R */ |
| 250 } | 245 } |
| 251 | 246 |
| 252 #if defined(GG_CONFIG_HAVE_PTHREAD) || !defined(_WIN32) | 247 #if defined(GG_CONFIG_HAVE_PTHREAD) || defined(GG_CONFIG_HAVE_FORK) |
| 248 | |
| 253 /** | 249 /** |
| 254 * \internal Rozwiązuje nazwę i zapisuje wynik do podanego desktyptora. | 250 * \internal Rozwiązuje nazwę i zapisuje wynik do podanego desktyptora. |
| 255 * | 251 * |
| 256 * \param fd Deskryptor | 252 * \param fd Deskryptor |
| 257 * \param hostname Nazwa serwera | 253 * \param hostname Nazwa serwera |
| 285 if (addr_list != addr_ip) | 281 if (addr_list != addr_ip) |
| 286 free(addr_list); | 282 free(addr_list); |
| 287 | 283 |
| 288 return res; | 284 return res; |
| 289 } | 285 } |
| 290 #endif | 286 |
| 287 #endif /* GG_CONFIG_HAVE_PTHREAD || GG_CONFIG_HAVE_FORK */ | |
| 291 | 288 |
| 292 /** | 289 /** |
| 293 * \internal Odpowiednik \c gethostbyname zapewniający współbieżność. | 290 * \internal Odpowiednik \c gethostbyname zapewniający współbieżność. |
| 294 * | 291 * |
| 295 * Jeśli dany system dostarcza \c gethostbyname_r, używa się tej wersji, jeśli | 292 * Jeśli dany system dostarcza \c gethostbyname_r, używa się tej wersji, jeśli |
| 310 return NULL; | 307 return NULL; |
| 311 | 308 |
| 312 return result; | 309 return result; |
| 313 } | 310 } |
| 314 | 311 |
| 312 #ifdef GG_CONFIG_HAVE_FORK | |
| 313 | |
| 315 /** | 314 /** |
| 316 * \internal Struktura przekazywana do wątku rozwiązującego nazwę. | 315 * \internal Struktura przekazywana do wątku rozwiązującego nazwę. |
| 317 */ | 316 */ |
| 318 struct gg_resolver_fork_data { | 317 struct gg_resolver_fork_data { |
| 319 int pid; /*< Identyfikator procesu */ | 318 int pid; /*< Identyfikator procesu */ |
| 320 }; | 319 }; |
| 321 | 320 |
| 322 #ifdef _WIN32 | |
| 323 /** | |
| 324 * Deal with the fact that you can't select() on a win32 file fd. | |
| 325 * This makes it practically impossible to tie into purple's event loop. | |
| 326 * | |
| 327 * -This is thanks to Tor Lillqvist. | |
| 328 * XXX - Move this to where the rest of the the win32 compatiblity stuff goes when we push the changes back to libgadu. | |
| 329 */ | |
| 330 static int | |
| 331 socket_pipe (int *fds) | |
| 332 { | |
| 333 SOCKET temp, socket1 = -1, socket2 = -1; | |
| 334 struct sockaddr_in saddr; | |
| 335 int len; | |
| 336 u_long arg; | |
| 337 fd_set read_set, write_set; | |
| 338 struct timeval tv; | |
| 339 | |
| 340 temp = socket(AF_INET, SOCK_STREAM, 0); | |
| 341 | |
| 342 if (temp == INVALID_SOCKET) { | |
| 343 goto out0; | |
| 344 } | |
| 345 | |
| 346 arg = 1; | |
| 347 if (ioctlsocket(temp, FIONBIO, &arg) == SOCKET_ERROR) { | |
| 348 goto out0; | |
| 349 } | |
| 350 | |
| 351 memset(&saddr, 0, sizeof(saddr)); | |
| 352 saddr.sin_family = AF_INET; | |
| 353 saddr.sin_port = 0; | |
| 354 saddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); | |
| 355 | |
| 356 if (bind(temp, (struct sockaddr *)&saddr, sizeof (saddr))) { | |
| 357 goto out0; | |
| 358 } | |
| 359 | |
| 360 if (listen(temp, 1) == SOCKET_ERROR) { | |
| 361 goto out0; | |
| 362 } | |
| 363 | |
| 364 len = sizeof(saddr); | |
| 365 if (getsockname(temp, (struct sockaddr *)&saddr, &len)) { | |
| 366 goto out0; | |
| 367 } | |
| 368 | |
| 369 socket1 = socket(AF_INET, SOCK_STREAM, 0); | |
| 370 | |
| 371 if (socket1 == INVALID_SOCKET) { | |
| 372 goto out0; | |
| 373 } | |
| 374 | |
| 375 arg = 1; | |
| 376 if (ioctlsocket(socket1, FIONBIO, &arg) == SOCKET_ERROR) { | |
| 377 goto out1; | |
| 378 } | |
| 379 | |
| 380 if (connect(socket1, (struct sockaddr *)&saddr, len) != SOCKET_ERROR || | |
| 381 WSAGetLastError() != WSAEWOULDBLOCK) { | |
| 382 goto out1; | |
| 383 } | |
| 384 | |
| 385 FD_ZERO(&read_set); | |
| 386 FD_SET(temp, &read_set); | |
| 387 | |
| 388 tv.tv_sec = 0; | |
| 389 tv.tv_usec = 0; | |
| 390 | |
| 391 if (select(0, &read_set, NULL, NULL, NULL) == SOCKET_ERROR) { | |
| 392 goto out1; | |
| 393 } | |
| 394 | |
| 395 if (!FD_ISSET(temp, &read_set)) { | |
| 396 goto out1; | |
| 397 } | |
| 398 | |
| 399 socket2 = accept(temp, (struct sockaddr *) &saddr, &len); | |
| 400 if (socket2 == INVALID_SOCKET) { | |
| 401 goto out1; | |
| 402 } | |
| 403 | |
| 404 FD_ZERO(&write_set); | |
| 405 FD_SET(socket1, &write_set); | |
| 406 | |
| 407 tv.tv_sec = 0; | |
| 408 tv.tv_usec = 0; | |
| 409 | |
| 410 if (select(0, NULL, &write_set, NULL, NULL) == SOCKET_ERROR) { | |
| 411 goto out2; | |
| 412 } | |
| 413 | |
| 414 if (!FD_ISSET(socket1, &write_set)) { | |
| 415 goto out2; | |
| 416 } | |
| 417 | |
| 418 arg = 0; | |
| 419 if (ioctlsocket(socket1, FIONBIO, &arg) == SOCKET_ERROR) { | |
| 420 goto out2; | |
| 421 } | |
| 422 | |
| 423 arg = 0; | |
| 424 if (ioctlsocket(socket2, FIONBIO, &arg) == SOCKET_ERROR) { | |
| 425 goto out2; | |
| 426 } | |
| 427 | |
| 428 fds[0] = socket1; | |
| 429 fds[1] = socket2; | |
| 430 | |
| 431 closesocket (temp); | |
| 432 | |
| 433 return 0; | |
| 434 | |
| 435 out2: | |
| 436 closesocket (socket2); | |
| 437 out1: | |
| 438 closesocket (socket1); | |
| 439 out0: | |
| 440 closesocket (temp); | |
| 441 errno = EIO; /* XXX */ | |
| 442 | |
| 443 return -1; | |
| 444 } | |
| 445 #endif | |
| 446 | |
| 447 | |
| 448 | |
| 449 #ifdef _WIN32 | |
| 450 struct gg_resolve_win32thread_data { | |
| 451 char *hostname; | |
| 452 int fd; | |
| 453 }; | |
| 454 | |
| 455 static DWORD WINAPI gg_resolve_win32thread_thread(LPVOID arg) | |
| 456 { | |
| 457 struct gg_resolve_win32thread_data *d = arg; | |
| 458 struct in_addr addr_ip[2], *addr_list; | |
| 459 int addr_count; | |
| 460 | |
| 461 gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread_thread() host: %s, fd: %i called\n", d->hostname, d->fd); | |
| 462 | |
| 463 if ((addr_ip[0].s_addr = inet_addr(d->hostname)) == INADDR_NONE) { | |
| 464 /* W przypadku błędu gg_gethostbyname_real() zwróci -1 | |
| 465 * i nie zmieni &addr. Tam jest już INADDR_NONE, | |
| 466 * więc nie musimy robić nic więcej. */ | |
| 467 if (gg_gethostbyname_real(d->hostname, &addr_list, &addr_count, 0) == -1) | |
| 468 { | |
| 469 addr_list = addr_ip; | |
| 470 } | |
| 471 } else { | |
| 472 addr_list = addr_ip; | |
| 473 addr_ip[1].s_addr = INADDR_NONE; | |
| 474 addr_count = 1; | |
| 475 } | |
| 476 | |
| 477 gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread_thread() count = %d\n", addr_count); | |
| 478 | |
| 479 write(d->fd, addr_list, (addr_count+1) * sizeof(struct in_addr)); | |
| 480 close(d->fd); | |
| 481 | |
| 482 free(d->hostname); | |
| 483 d->hostname = NULL; | |
| 484 | |
| 485 free(d); | |
| 486 | |
| 487 if (addr_list != addr_ip) | |
| 488 free(addr_list); | |
| 489 | |
| 490 gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread_thread() done\n"); | |
| 491 | |
| 492 return 0; | |
| 493 } | |
| 494 | |
| 495 | |
| 496 static int gg_resolve_win32thread(int *fd, void **resolver, const char *hostname) | |
| 497 { | |
| 498 struct gg_resolve_win32thread_data *d = NULL; | |
| 499 HANDLE h; | |
| 500 DWORD dwTId; | |
| 501 int pipes[2], new_errno; | |
| 502 | |
| 503 gg_debug(GG_DEBUG_FUNCTION, "** gg_resolve_win32thread(%p, %p, \"%s\");\n", fd, resolver, hostname); | |
| 504 | |
| 505 if (!resolver || !fd || !hostname) { | |
| 506 gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() invalid arguments\n"); | |
| 507 errno = EFAULT; | |
| 508 return -1; | |
| 509 } | |
| 510 | |
| 511 if (socket_pipe(pipes) == -1) { | |
| 512 gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() unable to create pipes (errno=%d, %s)\n", errno, strerror(errno)); | |
| 513 return -1; | |
| 514 } | |
| 515 | |
| 516 if (!(d = malloc(sizeof(*d)))) { | |
| 517 gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() out of memory\n"); | |
| 518 new_errno = errno; | |
| 519 goto cleanup; | |
| 520 } | |
| 521 | |
| 522 d->hostname = NULL; | |
| 523 | |
| 524 if (!(d->hostname = strdup(hostname))) { | |
| 525 gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() out of memory\n"); | |
| 526 new_errno = errno; | |
| 527 goto cleanup; | |
| 528 } | |
| 529 | |
| 530 d->fd = pipes[1]; | |
| 531 | |
| 532 h = CreateThread(NULL, 0, gg_resolve_win32thread_thread, | |
| 533 d, 0, &dwTId); | |
| 534 | |
| 535 if (h == NULL) { | |
| 536 gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() unable to create thread\n"); | |
| 537 new_errno = errno; | |
| 538 goto cleanup; | |
| 539 } | |
| 540 | |
| 541 *resolver = h; | |
| 542 *fd = pipes[0]; | |
| 543 | |
| 544 gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() done\n"); | |
| 545 | |
| 546 return 0; | |
| 547 | |
| 548 cleanup: | |
| 549 if (d) { | |
| 550 free(d->hostname); | |
| 551 free(d); | |
| 552 } | |
| 553 | |
| 554 close(pipes[0]); | |
| 555 close(pipes[1]); | |
| 556 | |
| 557 errno = new_errno; | |
| 558 | |
| 559 return -1; | |
| 560 | |
| 561 } | |
| 562 | |
| 563 static void gg_resolve_win32thread_cleanup(void **priv_data, int force) | |
| 564 { | |
| 565 struct gg_resolve_win32thread_data *data; | |
| 566 | |
| 567 gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread_cleanup() force: %i called\n", force); | |
| 568 | |
| 569 if (priv_data == NULL || *priv_data == NULL) | |
| 570 gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread_cleanup() priv_data: NULL\n"); | |
| 571 return; | |
| 572 | |
| 573 data = (struct gg_resolve_win32thread_data*) *priv_data; | |
| 574 gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread_cleanup() data: %s called\n", data->hostname); | |
| 575 *priv_data = NULL; | |
| 576 | |
| 577 if (force) { | |
| 578 gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread_cleanup() force called\n", force); | |
| 579 //pthread_cancel(data->thread); | |
| 580 //pthread_join(data->thread, NULL); | |
| 581 } | |
| 582 | |
| 583 free(data->hostname); | |
| 584 data->hostname = NULL; | |
| 585 | |
| 586 if (data->fd != -1) { | |
| 587 close(data->fd); | |
| 588 data->fd = -1; | |
| 589 } | |
| 590 gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread_cleanup() done\n"); | |
| 591 free(data); | |
| 592 } | |
| 593 #endif | |
| 594 | |
| 595 #ifndef _WIN32 | |
| 596 /** | 321 /** |
| 597 * \internal Rozwiązuje nazwę serwera w osobnym procesie. | 322 * \internal Rozwiązuje nazwę serwera w osobnym procesie. |
| 598 * | 323 * |
| 599 * Połączenia asynchroniczne nie mogą blokować procesu w trakcie rozwiązywania | 324 * Połączenia asynchroniczne nie mogą blokować procesu w trakcie rozwiązywania |
| 600 * nazwy serwera. W tym celu tworzony jest potok, nowy proces i dopiero w nim | 325 * nazwy serwera. W tym celu tworzony jest potok, nowy proces i dopiero w nim |
| 642 new_errno = errno; | 367 new_errno = errno; |
| 643 goto cleanup; | 368 goto cleanup; |
| 644 } | 369 } |
| 645 | 370 |
| 646 if (data->pid == 0) { | 371 if (data->pid == 0) { |
| 372 int status; | |
| 373 | |
| 647 close(pipes[0]); | 374 close(pipes[0]); |
| 648 | 375 |
| 649 if (gg_resolver_run(pipes[1], hostname) == -1) | 376 status = (gg_resolver_run(pipes[1], hostname) == -1) ? 1 : 0; |
| 650 _exit(1); | 377 |
| 651 else | 378 #ifdef GG_CONFIG_HAVE__EXIT |
| 652 _exit(0); | 379 _exit(status); |
| 380 #else | |
| 381 exit(status); | |
| 382 #endif | |
| 653 } | 383 } |
| 654 | 384 |
| 655 close(pipes[1]); | 385 close(pipes[1]); |
| 656 | 386 |
| 657 gg_debug(GG_DEBUG_MISC, "// gg_resolver_fork_start() %p\n", data); | 387 gg_debug(GG_DEBUG_MISC, "// gg_resolver_fork_start() %p\n", data); |
| 696 | 426 |
| 697 waitpid(data->pid, NULL, WNOHANG); | 427 waitpid(data->pid, NULL, WNOHANG); |
| 698 | 428 |
| 699 free(data); | 429 free(data); |
| 700 } | 430 } |
| 701 #endif | 431 |
| 432 #endif /* GG_CONFIG_HAVE_FORK */ | |
| 702 | 433 |
| 703 #ifdef GG_CONFIG_HAVE_PTHREAD | 434 #ifdef GG_CONFIG_HAVE_PTHREAD |
| 704 | 435 |
| 705 /** | 436 /** |
| 706 * \internal Struktura przekazywana do wątku rozwiązującego nazwę. | 437 * \internal Struktura przekazywana do wątku rozwiązującego nazwę. |
| 866 gs->resolver_start = gg_global_resolver_start; | 597 gs->resolver_start = gg_global_resolver_start; |
| 867 gs->resolver_cleanup = gg_global_resolver_cleanup; | 598 gs->resolver_cleanup = gg_global_resolver_cleanup; |
| 868 return 0; | 599 return 0; |
| 869 } | 600 } |
| 870 | 601 |
| 871 #if !defined(GG_CONFIG_HAVE_PTHREAD) || !defined(GG_CONFIG_PTHREAD_DEFAULT) | 602 #if defined(GG_CONFIG_HAVE_PTHREAD) && defined(GG_CONFIG_PTHREAD_DEFAULT) |
| 872 # ifdef _WIN32 | 603 type = GG_RESOLVER_PTHREAD; |
| 873 type = GG_RESOLVER_WIN32; | 604 #elif defined(GG_CONFIG_HAVE_FORK) |
| 874 # else | |
| 875 type = GG_RESOLVER_FORK; | 605 type = GG_RESOLVER_FORK; |
| 876 # endif | |
| 877 #else | |
| 878 type = GG_RESOLVER_PTHREAD; | |
| 879 #endif | 606 #endif |
| 880 } | 607 } |
| 881 | 608 |
| 882 switch (type) { | 609 switch (type) { |
| 883 #ifdef _WIN32 | 610 #ifdef GG_CONFIG_HAVE_FORK |
| 884 case GG_RESOLVER_WIN32: | |
| 885 gs->resolver_type = type; | |
| 886 gs->resolver_start = gg_resolve_win32thread; | |
| 887 gs->resolver_cleanup = gg_resolve_win32thread_cleanup; | |
| 888 return 0; | |
| 889 #else | |
| 890 case GG_RESOLVER_FORK: | 611 case GG_RESOLVER_FORK: |
| 891 gs->resolver_type = type; | 612 gs->resolver_type = type; |
| 892 gs->resolver_start = gg_resolver_fork_start; | 613 gs->resolver_start = gg_resolver_fork_start; |
| 893 gs->resolver_cleanup = gg_resolver_fork_cleanup; | 614 gs->resolver_cleanup = gg_resolver_fork_cleanup; |
| 894 return 0; | 615 return 0; |
| 987 gh->resolver_start = gg_global_resolver_start; | 708 gh->resolver_start = gg_global_resolver_start; |
| 988 gh->resolver_cleanup = gg_global_resolver_cleanup; | 709 gh->resolver_cleanup = gg_global_resolver_cleanup; |
| 989 return 0; | 710 return 0; |
| 990 } | 711 } |
| 991 | 712 |
| 992 #if !defined(GG_CONFIG_HAVE_PTHREAD) || !defined(GG_CONFIG_PTHREAD_DEFAULT) | 713 #if defined(GG_CONFIG_HAVE_PTHREAD) && defined(GG_CONFIG_PTHREAD_DEFAULT) |
| 993 # ifdef _WIN32 | 714 type = GG_RESOLVER_PTHREAD; |
| 994 type = GG_RESOLVER_WIN32; | 715 #elif defined(GG_CONFIG_HAVE_FORK) |
| 995 # else | |
| 996 type = GG_RESOLVER_FORK; | 716 type = GG_RESOLVER_FORK; |
| 997 # endif | |
| 998 #else | |
| 999 type = GG_RESOLVER_PTHREAD; | |
| 1000 #endif | 717 #endif |
| 1001 } | 718 } |
| 1002 | 719 |
| 1003 switch (type) { | 720 switch (type) { |
| 1004 #ifdef _WIN32 | 721 #ifdef GG_CONFIG_HAVE_FORK |
| 1005 case GG_RESOLVER_WIN32: | |
| 1006 gh->resolver_type = type; | |
| 1007 gh->resolver_start = gg_resolve_win32thread; | |
| 1008 gh->resolver_cleanup = gg_resolve_win32thread_cleanup; | |
| 1009 return 0; | |
| 1010 #else | |
| 1011 case GG_RESOLVER_FORK: | 722 case GG_RESOLVER_FORK: |
| 1012 gh->resolver_type = type; | 723 gh->resolver_type = type; |
| 1013 gh->resolver_start = gg_resolver_fork_start; | 724 gh->resolver_start = gg_resolver_fork_start; |
| 1014 gh->resolver_cleanup = gg_resolver_fork_cleanup; | 725 gh->resolver_cleanup = gg_resolver_fork_cleanup; |
| 1015 return 0; | 726 return 0; |
| 1083 gg_global_resolver_type = type; | 794 gg_global_resolver_type = type; |
| 1084 gg_global_resolver_start = NULL; | 795 gg_global_resolver_start = NULL; |
| 1085 gg_global_resolver_cleanup = NULL; | 796 gg_global_resolver_cleanup = NULL; |
| 1086 return 0; | 797 return 0; |
| 1087 | 798 |
| 1088 #ifdef _WIN32 | 799 #ifdef GG_CONFIG_HAVE_FORK |
| 1089 case GG_RESOLVER_WIN32: | |
| 1090 gg_global_resolver_type = type; | |
| 1091 gg_global_resolver_start = gg_resolve_win32thread; | |
| 1092 gg_global_resolver_cleanup = gg_resolve_win32thread_cleanup; | |
| 1093 return 0; | |
| 1094 #else | |
| 1095 case GG_RESOLVER_FORK: | 800 case GG_RESOLVER_FORK: |
| 1096 gg_global_resolver_type = type; | 801 gg_global_resolver_type = type; |
| 1097 gg_global_resolver_start = gg_resolver_fork_start; | 802 gg_global_resolver_start = gg_resolver_fork_start; |
| 1098 gg_global_resolver_cleanup = gg_resolver_fork_cleanup; | 803 gg_global_resolver_cleanup = gg_resolver_fork_cleanup; |
| 1099 return 0; | 804 return 0; |
