source: branches/samba-3.0/source/nmbd/nmbd.c@ 104

Last change on this file since 104 was 1, checked in by Paul Smedley, 18 years ago

Initial code import

File size: 21.5 KB
Line 
1/*
2 Unix SMB/CIFS implementation.
3 NBT netbios routines and daemon - version 2
4 Copyright (C) Andrew Tridgell 1994-1998
5 Copyright (C) Jeremy Allison 1997-2002
6 Copyright (C) Jelmer Vernooij 2002,2003 (Conversion to popt)
7
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21
22*/
23
24#include "includes.h"
25
26int ClientNMB = -1;
27int ClientDGRAM = -1;
28int global_nmb_port = -1;
29
30extern BOOL rescan_listen_set;
31extern struct in_addr loopback_ip;
32extern BOOL global_in_nmbd;
33
34extern BOOL override_logfile;
35
36/* are we running as a daemon ? */
37static BOOL is_daemon;
38
39/* fork or run in foreground ? */
40static BOOL Fork = True;
41
42/* log to standard output ? */
43static BOOL log_stdout;
44
45/* have we found LanMan clients yet? */
46BOOL found_lm_clients = False;
47
48/* what server type are we currently */
49
50time_t StartupTime = 0;
51
52/**************************************************************************** **
53 Handle a SIGTERM in band.
54 **************************************************************************** */
55
56static void terminate(void)
57{
58 DEBUG(0,("Got SIGTERM: going down...\n"));
59
60 /* Write out wins.dat file if samba is a WINS server */
61 wins_write_database(0,False);
62
63 /* Remove all SELF registered names from WINS */
64 release_wins_names();
65
66 /* Announce all server entries as 0 time-to-live, 0 type. */
67 announce_my_servers_removed();
68
69 /* If there was an async dns child - kill it. */
70 kill_async_dns_child();
71
72 exit(0);
73}
74
75/**************************************************************************** **
76 Handle a SHUTDOWN message from smbcontrol.
77 **************************************************************************** */
78
79static void nmbd_terminate(int msg_type, struct process_id src,
80 void *buf, size_t len, void *private_data)
81{
82 terminate();
83}
84
85/**************************************************************************** **
86 Catch a SIGTERM signal.
87 **************************************************************************** */
88
89static SIG_ATOMIC_T got_sig_term;
90
91static void sig_term(int sig)
92{
93 got_sig_term = 1;
94 sys_select_signal(SIGTERM);
95}
96
97/**************************************************************************** **
98 Catch a SIGHUP signal.
99 **************************************************************************** */
100
101static SIG_ATOMIC_T reload_after_sighup;
102
103static void sig_hup(int sig)
104{
105 reload_after_sighup = 1;
106 sys_select_signal(SIGHUP);
107}
108
109/**************************************************************************** **
110 Possibly continue after a fault.
111 **************************************************************************** */
112
113static void fault_continue(void)
114{
115 dump_core();
116}
117
118/**************************************************************************** **
119 Expire old names from the namelist and server list.
120 **************************************************************************** */
121
122static void expire_names_and_servers(time_t t)
123{
124 static time_t lastrun = 0;
125
126 if ( !lastrun )
127 lastrun = t;
128 if ( t < (lastrun + 5) )
129 return;
130 lastrun = t;
131
132 /*
133 * Expire any timed out names on all the broadcast
134 * subnets and those registered with the WINS server.
135 * (nmbd_namelistdb.c)
136 */
137
138 expire_names(t);
139
140 /*
141 * Go through all the broadcast subnets and for each
142 * workgroup known on that subnet remove any expired
143 * server names. If a workgroup has an empty serverlist
144 * and has itself timed out then remove the workgroup.
145 * (nmbd_workgroupdb.c)
146 */
147
148 expire_workgroups_and_servers(t);
149}
150
151/************************************************************************** **
152 Reload the list of network interfaces.
153 ************************************************************************** */
154
155static BOOL reload_interfaces(time_t t)
156{
157 static time_t lastt;
158 int n;
159 struct subnet_record *subrec;
160
161 if (t && ((t - lastt) < NMBD_INTERFACES_RELOAD)) return False;
162 lastt = t;
163
164 if (!interfaces_changed()) return False;
165
166 /* the list of probed interfaces has changed, we may need to add/remove
167 some subnets */
168 load_interfaces();
169
170 /* find any interfaces that need adding */
171 for (n=iface_count() - 1; n >= 0; n--) {
172 struct interface *iface = get_interface(n);
173
174 if (!iface) {
175 DEBUG(2,("reload_interfaces: failed to get interface %d\n", n));
176 continue;
177 }
178
179 /*
180 * We don't want to add a loopback interface, in case
181 * someone has added 127.0.0.1 for smbd, nmbd needs to
182 * ignore it here. JRA.
183 */
184
185 if (ip_equal(iface->ip, loopback_ip)) {
186 DEBUG(2,("reload_interfaces: Ignoring loopback interface %s\n", inet_ntoa(iface->ip)));
187 continue;
188 }
189
190 for (subrec=subnetlist; subrec; subrec=subrec->next) {
191 if (ip_equal(iface->ip, subrec->myip) &&
192 ip_equal(iface->nmask, subrec->mask_ip)) break;
193 }
194
195 if (!subrec) {
196 /* it wasn't found! add it */
197 DEBUG(2,("Found new interface %s\n",
198 inet_ntoa(iface->ip)));
199 subrec = make_normal_subnet(iface);
200 if (subrec)
201 register_my_workgroup_one_subnet(subrec);
202 }
203 }
204
205 /* find any interfaces that need deleting */
206 for (subrec=subnetlist; subrec; subrec=subrec->next) {
207 for (n=iface_count() - 1; n >= 0; n--) {
208 struct interface *iface = get_interface(n);
209 if (ip_equal(iface->ip, subrec->myip) &&
210 ip_equal(iface->nmask, subrec->mask_ip)) break;
211 }
212 if (n == -1) {
213 /* oops, an interface has disapeared. This is
214 tricky, we don't dare actually free the
215 interface as it could be being used, so
216 instead we just wear the memory leak and
217 remove it from the list of interfaces without
218 freeing it */
219 DEBUG(2,("Deleting dead interface %s\n",
220 inet_ntoa(subrec->myip)));
221 close_subnet(subrec);
222 }
223 }
224
225 rescan_listen_set = True;
226
227 /* We need to shutdown if there are no subnets... */
228 if (FIRST_SUBNET == NULL) {
229 DEBUG(0,("reload_interfaces: No subnets to listen to. Shutting down...\n"));
230 return True;
231 }
232 return False;
233}
234
235/**************************************************************************** **
236 Reload the services file.
237 **************************************************************************** */
238
239static BOOL reload_nmbd_services(BOOL test)
240{
241 BOOL ret;
242
243 set_remote_machine_name("nmbd", False);
244
245 if ( lp_loaded() ) {
246 pstring fname;
247 pstrcpy( fname,lp_configfile());
248 if (file_exist(fname,NULL) && !strcsequal(fname,dyn_CONFIGFILE)) {
249 pstrcpy(dyn_CONFIGFILE,fname);
250 test = False;
251 }
252 }
253
254 if ( test && !lp_file_list_changed() )
255 return(True);
256
257 ret = lp_load( dyn_CONFIGFILE, True , False, False, True);
258
259 /* perhaps the config filename is now set */
260 if ( !test ) {
261 DEBUG( 3, ( "services not loaded\n" ) );
262 reload_nmbd_services( True );
263 }
264
265 return(ret);
266}
267
268/**************************************************************************** **
269 * React on 'smbcontrol nmbd reload-config' in the same way as to SIGHUP
270 * We use buf here to return BOOL result to process() when reload_interfaces()
271 * detects that there are no subnets.
272 **************************************************************************** */
273
274static void msg_reload_nmbd_services(int msg_type, struct process_id src,
275 void *buf, size_t len, void *private_data)
276{
277 write_browse_list( 0, True );
278 dump_all_namelists();
279 reload_nmbd_services( True );
280 reopen_logs();
281
282 if(buf) {
283 /* We were called from process() */
284 /* If reload_interfaces() returned True */
285 /* we need to shutdown if there are no subnets... */
286 /* pass this info back to process() */
287 *((BOOL*)buf) = reload_interfaces(0);
288 }
289}
290
291static void msg_nmbd_send_packet(int msg_type, struct process_id src,
292 void *buf, size_t len, void *private_data)
293{
294 struct packet_struct *p = (struct packet_struct *)buf;
295 struct subnet_record *subrec;
296 struct in_addr *local_ip;
297
298 DEBUG(10, ("Received send_packet from %d\n", procid_to_pid(&src)));
299
300 if (len != sizeof(struct packet_struct)) {
301 DEBUG(2, ("Discarding invalid packet length from %d\n",
302 procid_to_pid(&src)));
303 return;
304 }
305
306 if ((p->packet_type != NMB_PACKET) &&
307 (p->packet_type != DGRAM_PACKET)) {
308 DEBUG(2, ("Discarding invalid packet type from %d: %d\n",
309 procid_to_pid(&src), p->packet_type));
310 return;
311 }
312
313 local_ip = iface_ip(p->ip);
314
315 if (local_ip == NULL) {
316 DEBUG(2, ("Could not find ip for packet from %d\n",
317 procid_to_pid(&src)));
318 return;
319 }
320
321 subrec = FIRST_SUBNET;
322
323 p->fd = (p->packet_type == NMB_PACKET) ?
324 subrec->nmb_sock : subrec->dgram_sock;
325
326 for (subrec = FIRST_SUBNET; subrec != NULL;
327 subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) {
328 if (ip_equal(*local_ip, subrec->myip)) {
329 p->fd = (p->packet_type == NMB_PACKET) ?
330 subrec->nmb_sock : subrec->dgram_sock;
331 break;
332 }
333 }
334
335 if (p->packet_type == DGRAM_PACKET) {
336 p->port = 138;
337 p->packet.dgram.header.source_ip.s_addr = local_ip->s_addr;
338 p->packet.dgram.header.source_port = 138;
339 }
340
341 send_packet(p);
342}
343
344/**************************************************************************** **
345 The main select loop.
346 **************************************************************************** */
347
348static void process(void)
349{
350 BOOL run_election;
351 BOOL no_subnets;
352
353 while( True ) {
354 time_t t = time(NULL);
355
356 /* Check for internal messages */
357
358 message_dispatch();
359
360 /*
361 * Check all broadcast subnets to see if
362 * we need to run an election on any of them.
363 * (nmbd_elections.c)
364 */
365
366 run_election = check_elections();
367
368 /*
369 * Read incoming UDP packets.
370 * (nmbd_packets.c)
371 */
372
373 if(listen_for_packets(run_election))
374 return;
375
376 /*
377 * Handle termination inband.
378 */
379
380 if (got_sig_term) {
381 got_sig_term = 0;
382 terminate();
383 }
384
385 /*
386 * Process all incoming packets
387 * read above. This calls the success and
388 * failure functions registered when response
389 * packets arrrive, and also deals with request
390 * packets from other sources.
391 * (nmbd_packets.c)
392 */
393
394 run_packet_queue();
395
396 /*
397 * Run any elections - initiate becoming
398 * a local master browser if we have won.
399 * (nmbd_elections.c)
400 */
401
402 run_elections(t);
403
404 /*
405 * Send out any broadcast announcements
406 * of our server names. This also announces
407 * the workgroup name if we are a local
408 * master browser.
409 * (nmbd_sendannounce.c)
410 */
411
412 announce_my_server_names(t);
413
414 /*
415 * Send out any LanMan broadcast announcements
416 * of our server names.
417 * (nmbd_sendannounce.c)
418 */
419
420 announce_my_lm_server_names(t);
421
422 /*
423 * If we are a local master browser, periodically
424 * announce ourselves to the domain master browser.
425 * This also deals with syncronising the domain master
426 * browser server lists with ourselves as a local
427 * master browser.
428 * (nmbd_sendannounce.c)
429 */
430
431 announce_myself_to_domain_master_browser(t);
432
433 /*
434 * Fullfill any remote announce requests.
435 * (nmbd_sendannounce.c)
436 */
437
438 announce_remote(t);
439
440 /*
441 * Fullfill any remote browse sync announce requests.
442 * (nmbd_sendannounce.c)
443 */
444
445 browse_sync_remote(t);
446
447 /*
448 * Scan the broadcast subnets, and WINS client
449 * namelists and refresh any that need refreshing.
450 * (nmbd_mynames.c)
451 */
452
453 refresh_my_names(t);
454
455 /*
456 * Scan the subnet namelists and server lists and
457 * expire thos that have timed out.
458 * (nmbd.c)
459 */
460
461 expire_names_and_servers(t);
462
463 /*
464 * Write out a snapshot of our current browse list into
465 * the browse.dat file. This is used by smbd to service
466 * incoming NetServerEnum calls - used to synchronise
467 * browse lists over subnets.
468 * (nmbd_serverlistdb.c)
469 */
470
471 write_browse_list(t, False);
472
473 /*
474 * If we are a domain master browser, we have a list of
475 * local master browsers we should synchronise browse
476 * lists with (these are added by an incoming local
477 * master browser announcement packet). Expire any of
478 * these that are no longer current, and pull the server
479 * lists from each of these known local master browsers.
480 * (nmbd_browsesync.c)
481 */
482
483 dmb_expire_and_sync_browser_lists(t);
484
485 /*
486 * Check that there is a local master browser for our
487 * workgroup for all our broadcast subnets. If one
488 * is not found, start an election (which we ourselves
489 * may or may not participate in, depending on the
490 * setting of the 'local master' parameter.
491 * (nmbd_elections.c)
492 */
493
494 check_master_browser_exists(t);
495
496 /*
497 * If we are configured as a logon server, attempt to
498 * register the special NetBIOS names to become such
499 * (WORKGROUP<1c> name) on all broadcast subnets and
500 * with the WINS server (if used). If we are configured
501 * to become a domain master browser, attempt to register
502 * the special NetBIOS name (WORKGROUP<1b> name) to
503 * become such.
504 * (nmbd_become_dmb.c)
505 */
506
507 add_domain_names(t);
508
509 /*
510 * If we are a WINS server, do any timer dependent
511 * processing required.
512 * (nmbd_winsserver.c)
513 */
514
515 initiate_wins_processing(t);
516
517 /*
518 * If we are a domain master browser, attempt to contact the
519 * WINS server to get a list of all known WORKGROUPS/DOMAINS.
520 * This will only work to a Samba WINS server.
521 * (nmbd_browsesync.c)
522 */
523
524 if (lp_enhanced_browsing())
525 collect_all_workgroup_names_from_wins_server(t);
526
527 /*
528 * Go through the response record queue and time out or re-transmit
529 * and expired entries.
530 * (nmbd_packets.c)
531 */
532
533 retransmit_or_expire_response_records(t);
534
535 /*
536 * check to see if any remote browse sync child processes have completed
537 */
538
539 sync_check_completion();
540
541 /*
542 * regularly sync with any other DMBs we know about
543 */
544
545 if (lp_enhanced_browsing())
546 sync_all_dmbs(t);
547
548 /*
549 * clear the unexpected packet queue
550 */
551
552 clear_unexpected(t);
553
554 /*
555 * Reload the services file if we got a sighup.
556 */
557
558 if(reload_after_sighup) {
559 DEBUG( 0, ( "Got SIGHUP dumping debug info.\n" ) );
560 msg_reload_nmbd_services(MSG_SMB_CONF_UPDATED,
561 pid_to_procid(0), (void*) &no_subnets, 0, NULL);
562 if(no_subnets)
563 return;
564 reload_after_sighup = 0;
565 }
566
567 /* check for new network interfaces */
568
569 if(reload_interfaces(t))
570 return;
571
572 /* free up temp memory */
573 lp_TALLOC_FREE();
574 }
575}
576
577/**************************************************************************** **
578 Open the socket communication.
579 **************************************************************************** */
580
581static BOOL open_sockets(BOOL isdaemon, int port)
582{
583 /*
584 * The sockets opened here will be used to receive broadcast
585 * packets *only*. Interface specific sockets are opened in
586 * make_subnet() in namedbsubnet.c. Thus we bind to the
587 * address "0.0.0.0". The parameter 'socket address' is
588 * now deprecated.
589 */
590
591 if ( isdaemon )
592 ClientNMB = open_socket_in(SOCK_DGRAM, port,
593 0, interpret_addr(lp_socket_address()),
594 True);
595 else
596 ClientNMB = 0;
597
598 ClientDGRAM = open_socket_in(SOCK_DGRAM, DGRAM_PORT,
599 3, interpret_addr(lp_socket_address()),
600 True);
601
602 if ( ClientNMB == -1 )
603 return( False );
604
605 /* we are never interested in SIGPIPE */
606 BlockSignals(True,SIGPIPE);
607
608 set_socket_options( ClientNMB, "SO_BROADCAST" );
609 set_socket_options( ClientDGRAM, "SO_BROADCAST" );
610
611 /* Ensure we're non-blocking. */
612 set_blocking( ClientNMB, False);
613 set_blocking( ClientDGRAM, False);
614
615 DEBUG( 3, ( "open_sockets: Broadcast sockets opened.\n" ) );
616 return( True );
617}
618
619/**************************************************************************** **
620 main program
621 **************************************************************************** */
622 int main(int argc, const char *argv[])
623{
624 pstring logfile;
625 static BOOL opt_interactive;
626 poptContext pc;
627#ifndef __OS2__
628 static char *p_lmhosts = dyn_LMHOSTSFILE;
629#else
630 char *p_lmhosts = dyn_LMHOSTSFILE;
631#endif /* __OS2__ */
632 static BOOL no_process_group = False;
633 struct poptOption long_options[] = {
634 POPT_AUTOHELP
635 {"daemon", 'D', POPT_ARG_VAL, &is_daemon, True, "Become a daemon(default)" },
636 {"interactive", 'i', POPT_ARG_VAL, &opt_interactive, True, "Run interactive (not a daemon)" },
637 {"foreground", 'F', POPT_ARG_VAL, &Fork, False, "Run daemon in foreground (for daemontools & etc)" },
638 {"no-process-group", 0, POPT_ARG_VAL, &no_process_group, True, "Don't create a new process group" },
639 {"log-stdout", 'S', POPT_ARG_VAL, &log_stdout, True, "Log to stdout" },
640 {"hosts", 'H', POPT_ARG_STRING, &p_lmhosts, 'H', "Load a netbios hosts file"},
641 {"port", 'p', POPT_ARG_INT, &global_nmb_port, NMB_PORT, "Listen on the specified port" },
642 POPT_COMMON_SAMBA
643 { NULL }
644 };
645
646 load_case_tables();
647
648 global_nmb_port = NMB_PORT;
649
650 pc = poptGetContext("nmbd", argc, argv, long_options, 0);
651 while (poptGetNextOpt(pc) != -1) {};
652 poptFreeContext(pc);
653
654 global_in_nmbd = True;
655
656 StartupTime = time(NULL);
657
658 sys_srandom(time(NULL) ^ sys_getpid());
659
660 if (!override_logfile) {
661 slprintf(logfile, sizeof(logfile)-1, "%s/log.nmbd", dyn_LOGFILEBASE);
662 lp_set_logfile(logfile);
663 }
664
665 fault_setup((void (*)(void *))fault_continue );
666 dump_core_setup("nmbd");
667
668 /* POSIX demands that signals are inherited. If the invoking process has
669 * these signals masked, we will have problems, as we won't receive them. */
670 BlockSignals(False, SIGHUP);
671 BlockSignals(False, SIGUSR1);
672 BlockSignals(False, SIGTERM);
673
674 CatchSignal( SIGHUP, SIGNAL_CAST sig_hup );
675 CatchSignal( SIGTERM, SIGNAL_CAST sig_term );
676
677#if defined(SIGFPE)
678 /* we are never interested in SIGFPE */
679 BlockSignals(True,SIGFPE);
680#endif
681
682 /* We no longer use USR2... */
683#if defined(SIGUSR2)
684 BlockSignals(True, SIGUSR2);
685#endif
686
687 if ( opt_interactive ) {
688 Fork = False;
689 log_stdout = True;
690 }
691
692 if ( log_stdout && Fork ) {
693 DEBUG(0,("ERROR: Can't log to stdout (-S) unless daemon is in foreground (-F) or interactive (-i)\n"));
694 exit(1);
695 }
696
697 setup_logging( argv[0], log_stdout );
698
699 reopen_logs();
700
701 DEBUG( 0, ( "Netbios nameserver version %s started.\n", SAMBA_VERSION_STRING) );
702 DEBUGADD( 0, ( "%s\n", COPYRIGHT_STARTUP_MESSAGE ) );
703
704 if ( !reload_nmbd_services(False) )
705 return(-1);
706
707 if(!init_names())
708 return -1;
709
710 reload_nmbd_services( True );
711
712 if (strequal(lp_workgroup(),"*")) {
713 DEBUG(0,("ERROR: a workgroup name of * is no longer supported\n"));
714 exit(1);
715 }
716
717 set_samba_nb_type();
718
719 if (!is_daemon && !is_a_socket(0)) {
720 DEBUG(0,("standard input is not a socket, assuming -D option\n"));
721 is_daemon = True;
722 }
723
724 if (is_daemon && !opt_interactive) {
725 DEBUG( 2, ( "Becoming a daemon.\n" ) );
726 become_daemon(Fork, no_process_group);
727 }
728
729#if HAVE_SETPGID
730 /*
731 * If we're interactive we want to set our own process group for
732 * signal management.
733 */
734 if (opt_interactive && !no_process_group)
735 setpgid( (pid_t)0, (pid_t)0 );
736#endif
737
738#ifndef SYNC_DNS
739 /* Setup the async dns. We do it here so it doesn't have all the other
740 stuff initialised and thus chewing memory and sockets */
741 if(lp_we_are_a_wins_server() && lp_dns_proxy()) {
742 start_async_dns();
743 }
744#endif
745
746 if (!directory_exist(lp_lockdir(), NULL)) {
747 mkdir(lp_lockdir(), 0755);
748 }
749
750 pidfile_create("nmbd");
751 message_init();
752 message_register(MSG_FORCE_ELECTION, nmbd_message_election, NULL);
753#if 0
754 /* Until winsrepl is done. */
755 message_register(MSG_WINS_NEW_ENTRY, nmbd_wins_new_entry, NULL);
756#endif
757 message_register(MSG_SHUTDOWN, nmbd_terminate, NULL);
758 message_register(MSG_SMB_CONF_UPDATED, msg_reload_nmbd_services, NULL);
759 message_register(MSG_SEND_PACKET, msg_nmbd_send_packet, NULL);
760
761 TimeInit();
762
763 DEBUG( 3, ( "Opening sockets %d\n", global_nmb_port ) );
764
765 if ( !open_sockets( is_daemon, global_nmb_port ) ) {
766 kill_async_dns_child();
767 return 1;
768 }
769
770 /* Determine all the IP addresses we have. */
771 load_interfaces();
772
773 /* Create an nmbd subnet record for each of the above. */
774 if( False == create_subnets() ) {
775 DEBUG(0,("ERROR: Failed when creating subnet lists. Exiting.\n"));
776 kill_async_dns_child();
777 exit(1);
778 }
779
780 /* Load in any static local names. */
781 load_lmhosts_file(p_lmhosts);
782 DEBUG(3,("Loaded hosts file %s\n", p_lmhosts));
783
784 /* If we are acting as a WINS server, initialise data structures. */
785 if( !initialise_wins() ) {
786 DEBUG( 0, ( "nmbd: Failed when initialising WINS server.\n" ) );
787 kill_async_dns_child();
788 exit(1);
789 }
790
791 /*
792 * Register nmbd primary workgroup and nmbd names on all
793 * the broadcast subnets, and on the WINS server (if specified).
794 * Also initiate the startup of our primary workgroup (start
795 * elections if we are setup as being able to be a local
796 * master browser.
797 */
798
799 if( False == register_my_workgroup_and_names() ) {
800 DEBUG(0,("ERROR: Failed when creating my my workgroup. Exiting.\n"));
801 kill_async_dns_child();
802 exit(1);
803 }
804
805 /* We can only take signals in the select. */
806 BlockSignals( True, SIGTERM );
807
808 process();
809
810 if (dbf)
811 x_fclose(dbf);
812 kill_async_dns_child();
813 return(0);
814}
Note: See TracBrowser for help on using the repository browser.