2 * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (C) 1999-2003 Internet Software Consortium.
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15 * PERFORMANCE OF THIS SOFTWARE.
18 /* $Id: main.c,v 1.119.2.3.2.22 2005/04/29 01:04:47 marka Exp $ */
27 #include <isc/commandline.h>
29 #include <isc/entropy.h>
33 #include <isc/platform.h>
34 #include <isc/resource.h>
35 #include <isc/stdio.h>
36 #include <isc/string.h>
38 #include <isc/timer.h>
41 #include <isccc/result.h>
43 #include <dns/dispatch.h>
45 #include <dns/result.h>
48 #include <dst/result.h>
51 * Defining NS_MAIN provides storage declarations (rather than extern)
52 * for variables in named/globals.h.
56 #include <named/builtin.h>
57 #include <named/control.h>
58 #include <named/globals.h> /* Explicit, though named/log.h includes it. */
59 #include <named/interfacemgr.h>
60 #include <named/log.h>
62 #include <named/server.h>
63 #include <named/lwresd.h>
64 #include <named/main.h>
66 #include <named/ns_smf_globals.h>
70 * Include header files for database drivers here.
72 /* #include "xxdb.h" */
74 static isc_boolean_t want_stats = ISC_FALSE;
75 static char program_name[ISC_DIR_NAMEMAX] = "named";
76 static char absolute_conffile[ISC_DIR_PATHMAX];
77 static char saved_command_line[512];
78 static char version[512];
81 ns_main_earlywarning(const char *format, ...) {
84 va_start(args, format);
85 if (ns_g_lctx != NULL) {
86 isc_log_vwrite(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
87 NS_LOGMODULE_MAIN, ISC_LOG_WARNING,
90 fprintf(stderr, "%s: ", program_name);
91 vfprintf(stderr, format, args);
92 fprintf(stderr, "\n");
99 ns_main_earlyfatal(const char *format, ...) {
102 va_start(args, format);
103 if (ns_g_lctx != NULL) {
104 isc_log_vwrite(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
105 NS_LOGMODULE_MAIN, ISC_LOG_CRITICAL,
107 isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
108 NS_LOGMODULE_MAIN, ISC_LOG_CRITICAL,
109 "exiting (due to early fatal error)");
111 fprintf(stderr, "%s: ", program_name);
112 vfprintf(stderr, format, args);
113 fprintf(stderr, "\n");
122 assertion_failed(const char *file, int line, isc_assertiontype_t type,
126 * Handle assertion failures.
129 if (ns_g_lctx != NULL) {
131 * Reset the assetion callback in case it is the log
132 * routines causing the assertion.
134 isc_assertion_setcallback(NULL);
136 isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
137 NS_LOGMODULE_MAIN, ISC_LOG_CRITICAL,
138 "%s:%d: %s(%s) failed", file, line,
139 isc_assertion_typetotext(type), cond);
140 isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
141 NS_LOGMODULE_MAIN, ISC_LOG_CRITICAL,
142 "exiting (due to assertion failure)");
144 fprintf(stderr, "%s:%d: %s(%s) failed\n",
145 file, line, isc_assertion_typetotext(type), cond);
155 library_fatal_error(const char *file, int line, const char *format,
156 va_list args) ISC_FORMAT_PRINTF(3, 0);
159 library_fatal_error(const char *file, int line, const char *format,
163 * Handle isc_error_fatal() calls from our libraries.
166 if (ns_g_lctx != NULL) {
168 * Reset the error callback in case it is the log
169 * routines causing the assertion.
171 isc_error_setfatal(NULL);
173 isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
174 NS_LOGMODULE_MAIN, ISC_LOG_CRITICAL,
175 "%s:%d: fatal error:", file, line);
176 isc_log_vwrite(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
177 NS_LOGMODULE_MAIN, ISC_LOG_CRITICAL,
179 isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
180 NS_LOGMODULE_MAIN, ISC_LOG_CRITICAL,
181 "exiting (due to fatal error in library)");
183 fprintf(stderr, "%s:%d: fatal error: ", file, line);
184 vfprintf(stderr, format, args);
185 fprintf(stderr, "\n");
195 library_unexpected_error(const char *file, int line, const char *format,
196 va_list args) ISC_FORMAT_PRINTF(3, 0);
199 library_unexpected_error(const char *file, int line, const char *format,
203 * Handle isc_error_unexpected() calls from our libraries.
206 if (ns_g_lctx != NULL) {
207 isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
208 NS_LOGMODULE_MAIN, ISC_LOG_ERROR,
209 "%s:%d: unexpected error:", file, line);
210 isc_log_vwrite(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
211 NS_LOGMODULE_MAIN, ISC_LOG_ERROR,
214 fprintf(stderr, "%s:%d: fatal error: ", file, line);
215 vfprintf(stderr, format, args);
216 fprintf(stderr, "\n");
224 "usage: lwresd [-4|-6] [-c conffile | -C resolvconffile] "
226 " [-f|-g] [-n number_of_cpus] [-p port] "
227 "[-P listen-port] [-s]\n"
228 " [-t chrootdir] [-u username] [-i pidfile]\n"
229 " [-m {usage|trace|record}]\n");
234 if (ns_g_lwresdonly) {
239 "usage: named [-4|-6] [-c conffile] [-d debuglevel] "
240 "[-f|-g] [-n number_of_cpus]\n"
241 " [-p port] [-s] [-t chrootdir] [-u username]\n"
242 " [-m {usage|trace|record}]\n");
246 save_command_line(int argc, char *argv[]) {
251 const char truncated[] = "...";
252 isc_boolean_t quoted = ISC_FALSE;
254 dst = saved_command_line;
255 eob = saved_command_line + sizeof(saved_command_line);
257 for (i = 1; i < argc && dst < eob; i++) {
261 while (*src != '\0' && dst < eob) {
263 * This won't perfectly produce a shell-independent
264 * pastable command line in all circumstances, but
265 * comes close, and for practical purposes will
266 * nearly always be fine.
268 if (quoted || isalnum(*src & 0xff) ||
269 *src == '-' || *src == '_' ||
270 *src == '.' || *src == '/') {
280 INSIST(sizeof(saved_command_line) >= sizeof(truncated));
283 strcpy(eob - sizeof(truncated), truncated);
289 parse_int(char *arg, const char *desc) {
294 ltmp = strtol(arg, &endp, 10);
297 ns_main_earlyfatal("%s '%s' must be numeric", desc, arg);
298 if (tmp < 0 || tmp != ltmp)
299 ns_main_earlyfatal("%s '%s' out of range", desc, arg);
303 static struct flag_def {
306 } mem_debug_flags[] = {
307 { "trace", ISC_MEM_DEBUGTRACE },
308 { "record", ISC_MEM_DEBUGRECORD },
309 { "usage", ISC_MEM_DEBUGUSAGE },
314 set_flags(const char *arg, struct flag_def *defs, unsigned int *ret) {
316 const struct flag_def *def;
317 const char *end = strchr(arg, ',');
320 end = arg + strlen(arg);
322 for (def = defs; def->name != NULL; def++) {
323 if (arglen == (int)strlen(def->name) &&
324 memcmp(arg, def->name, arglen) == 0) {
329 ns_main_earlyfatal("unrecognized flag '%.*s'", arglen, arg);
338 parse_command_line(int argc, char *argv[]) {
341 isc_boolean_t disable6 = ISC_FALSE;
342 isc_boolean_t disable4 = ISC_FALSE;
344 save_command_line(argc, argv);
346 isc_commandline_errprint = ISC_FALSE;
347 while ((ch = isc_commandline_parse(argc, argv,
348 "46c:C:d:fgi:lm:n:N:p:P:st:u:vx:")) != -1) {
352 ns_main_earlyfatal("cannot specify -4 and -6");
353 if (isc_net_probeipv4() != ISC_R_SUCCESS)
354 ns_main_earlyfatal("IPv4 not supported by OS");
355 isc_net_disableipv6();
360 ns_main_earlyfatal("cannot specify -4 and -6");
361 if (isc_net_probeipv6() != ISC_R_SUCCESS)
362 ns_main_earlyfatal("IPv6 not supported by OS");
363 isc_net_disableipv4();
367 ns_g_conffile = isc_commandline_argument;
368 lwresd_g_conffile = isc_commandline_argument;
369 if (lwresd_g_useresolvconf)
370 ns_main_earlyfatal("cannot specify -c and -C");
371 ns_g_conffileset = ISC_TRUE;
374 lwresd_g_resolvconffile = isc_commandline_argument;
375 if (ns_g_conffileset)
376 ns_main_earlyfatal("cannot specify -c and -C");
377 lwresd_g_useresolvconf = ISC_TRUE;
380 ns_g_debuglevel = parse_int(isc_commandline_argument,
384 ns_g_foreground = ISC_TRUE;
387 ns_g_foreground = ISC_TRUE;
388 ns_g_logstderr = ISC_TRUE;
390 /* XXXBEW -i should be removed */
392 lwresd_g_defaultpidfile = isc_commandline_argument;
395 ns_g_lwresdonly = ISC_TRUE;
398 set_flags(isc_commandline_argument, mem_debug_flags,
401 case 'N': /* Deprecated. */
403 ns_g_cpus = parse_int(isc_commandline_argument,
409 port = parse_int(isc_commandline_argument, "port");
410 if (port < 1 || port > 65535)
411 ns_main_earlyfatal("port '%s' out of range",
412 isc_commandline_argument);
415 /* XXXBEW Should -P be removed? */
417 port = parse_int(isc_commandline_argument, "port");
418 if (port < 1 || port > 65535)
419 ns_main_earlyfatal("port '%s' out of range",
420 isc_commandline_argument);
421 lwresd_g_listenport = port;
424 /* XXXRTH temporary syntax */
425 want_stats = ISC_TRUE;
428 /* XXXJAB should we make a copy? */
429 ns_g_chrootdir = isc_commandline_argument;
432 ns_g_username = isc_commandline_argument;
435 printf("BIND %s\n", ns_g_version);
439 ns_main_earlyfatal("unknown option '-%c'",
440 isc_commandline_option);
442 ns_main_earlyfatal("parsing options returned %d", ch);
446 argc -= isc_commandline_index;
447 argv += isc_commandline_index;
451 ns_main_earlyfatal("extra command line arguments");
456 create_managers(void) {
458 #ifdef ISC_PLATFORM_USETHREADS
459 unsigned int cpus_detected;
462 #ifdef ISC_PLATFORM_USETHREADS
463 cpus_detected = isc_os_ncpus();
465 ns_g_cpus = cpus_detected;
466 isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
467 ISC_LOG_INFO, "found %u CPU%s, using %u worker thread%s",
468 cpus_detected, cpus_detected == 1 ? "" : "s",
469 ns_g_cpus, ns_g_cpus == 1 ? "" : "s");
473 result = isc_taskmgr_create(ns_g_mctx, ns_g_cpus, 0, &ns_g_taskmgr);
474 if (result != ISC_R_SUCCESS) {
475 UNEXPECTED_ERROR(__FILE__, __LINE__,
476 "ns_taskmgr_create() failed: %s",
477 isc_result_totext(result));
478 return (ISC_R_UNEXPECTED);
481 result = isc_timermgr_create(ns_g_mctx, &ns_g_timermgr);
482 if (result != ISC_R_SUCCESS) {
483 UNEXPECTED_ERROR(__FILE__, __LINE__,
484 "ns_timermgr_create() failed: %s",
485 isc_result_totext(result));
486 return (ISC_R_UNEXPECTED);
489 result = isc_socketmgr_create(ns_g_mctx, &ns_g_socketmgr);
490 if (result != ISC_R_SUCCESS) {
491 UNEXPECTED_ERROR(__FILE__, __LINE__,
492 "isc_socketmgr_create() failed: %s",
493 isc_result_totext(result));
494 return (ISC_R_UNEXPECTED);
497 result = isc_entropy_create(ns_g_mctx, &ns_g_entropy);
498 if (result != ISC_R_SUCCESS) {
499 UNEXPECTED_ERROR(__FILE__, __LINE__,
500 "isc_entropy_create() failed: %s",
501 isc_result_totext(result));
502 return (ISC_R_UNEXPECTED);
505 result = isc_hash_create(ns_g_mctx, ns_g_entropy, DNS_NAME_MAXWIRE);
506 if (result != ISC_R_SUCCESS) {
507 UNEXPECTED_ERROR(__FILE__, __LINE__,
508 "isc_hash_create() failed: %s",
509 isc_result_totext(result));
510 return (ISC_R_UNEXPECTED);
513 return (ISC_R_SUCCESS);
517 destroy_managers(void) {
518 ns_lwresd_shutdown();
520 isc_entropy_detach(&ns_g_entropy);
521 if (ns_g_fallbackentropy != NULL)
522 isc_entropy_detach(&ns_g_fallbackentropy);
525 * isc_taskmgr_destroy() will block until all tasks have exited,
527 isc_taskmgr_destroy(&ns_g_taskmgr);
528 isc_timermgr_destroy(&ns_g_timermgr);
529 isc_socketmgr_destroy(&ns_g_socketmgr);
532 * isc_hash_destroy() cannot be called as long as a resolver may be
533 * running. Calling this after isc_taskmgr_destroy() ensures the
543 char *instance = NULL;
547 * Get the user and group information before changing the root
548 * directory, so the administrator does not need to keep a copy
549 * of the user and group databases in the chroot'ed environment.
551 ns_os_inituserinfo(ns_g_username);
554 * Initialize time conversion information
561 /* Check if named is under smf control, before chroot. */
562 result = ns_smf_get_instance(&instance, 0, ns_g_mctx);
563 /* We don't care about instance, just check if we got one. */
564 if (result == ISC_R_SUCCESS)
565 ns_smf_got_instance = 1;
567 ns_smf_got_instance = 0;
568 if (instance != NULL)
569 isc_mem_free(ns_g_mctx, instance);
570 #endif /* HAVE_LIBSCF */
572 #ifdef PATH_RANDOMDEV
574 * Initialize system's random device as fallback entropy source
575 * if running chroot'ed.
577 if (ns_g_chrootdir != NULL) {
578 result = isc_entropy_create(ns_g_mctx, &ns_g_fallbackentropy);
579 if (result != ISC_R_SUCCESS)
580 ns_main_earlyfatal("isc_entropy_create() failed: %s",
581 isc_result_totext(result));
583 result = isc_entropy_createfilesource(ns_g_fallbackentropy,
585 if (result != ISC_R_SUCCESS) {
586 ns_main_earlywarning("could not open pre-chroot "
587 "entropy source %s: %s",
589 isc_result_totext(result));
590 isc_entropy_detach(&ns_g_fallbackentropy);
595 ns_os_chroot(ns_g_chrootdir);
598 * For operating systems which have a capability mechanism, now
599 * is the time to switch to minimal privs and change our user id.
600 * On traditional UNIX systems, this call will be a no-op, and we
601 * will change the user ID after reading the config file the first
602 * time. (We need to read the config file to know which possibly
603 * privileged ports to bind() to.)
607 result = ns_log_init(ISC_TF(ns_g_username != NULL));
608 if (result != ISC_R_SUCCESS)
609 ns_main_earlyfatal("ns_log_init() failed: %s",
610 isc_result_totext(result));
613 * Now is the time to daemonize (if we're not running in the
614 * foreground). We waited until now because we wanted to get
615 * a valid logging context setup. We cannot daemonize any later,
616 * because calling create_managers() will create threads, which
617 * would be lost after fork().
619 if (!ns_g_foreground)
623 * We call isc_app_start() here as some versions of FreeBSD's fork()
624 * destroys all the signal handling it sets up.
626 result = isc_app_start();
627 if (result != ISC_R_SUCCESS)
628 ns_main_earlyfatal("isc_app_start() failed: %s",
629 isc_result_totext(result));
631 isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_MAIN,
632 ISC_LOG_NOTICE, "starting BIND %s%s", ns_g_version,
636 * Get the initial resource limits.
638 (void)isc_resource_getlimit(isc_resource_stacksize,
639 &ns_g_initstacksize);
640 (void)isc_resource_getlimit(isc_resource_datasize,
642 (void)isc_resource_getlimit(isc_resource_coresize,
644 (void)isc_resource_getlimit(isc_resource_openfiles,
645 &ns_g_initopenfiles);
648 * If the named configuration filename is relative, prepend the current
649 * directory's name before possibly changing to another directory.
651 if (! isc_file_isabsolute(ns_g_conffile)) {
652 result = isc_file_absolutepath(ns_g_conffile,
654 sizeof(absolute_conffile));
655 if (result != ISC_R_SUCCESS)
656 ns_main_earlyfatal("could not construct absolute path of "
657 "configuration file: %s",
658 isc_result_totext(result));
659 ns_g_conffile = absolute_conffile;
662 result = create_managers();
663 if (result != ISC_R_SUCCESS)
664 ns_main_earlyfatal("create_managers() failed: %s",
665 isc_result_totext(result));
670 * Add calls to register sdb drivers here.
674 ns_server_create(ns_g_mctx, &ns_g_server);
681 ns_server_destroy(&ns_g_server);
686 * Add calls to unregister sdb drivers here.
690 isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_MAIN,
691 ISC_LOG_NOTICE, "exiting");
695 static char *memstats = NULL;
698 ns_main_setmemstats(const char *filename) {
700 * Caller has to ensure locking.
703 if (memstats != NULL) {
707 if (filename == NULL)
709 memstats = malloc(strlen(filename) + 1);
711 strcpy(memstats, filename);
716 * Get FMRI for the named process.
719 ns_smf_get_instance(char **ins_name, int debug, isc_mem_t *mctx) {
720 scf_handle_t *h = NULL;
724 REQUIRE(ins_name != NULL && *ins_name == NULL);
726 if ((h = scf_handle_create(SCF_VERSION)) == NULL) {
728 UNEXPECTED_ERROR(__FILE__, __LINE__,
729 "scf_handle_create() failed: %s",
730 scf_strerror(scf_error()));
731 return (ISC_R_FAILURE);
734 if (scf_handle_bind(h) == -1) {
736 UNEXPECTED_ERROR(__FILE__, __LINE__,
737 "scf_handle_bind() failed: %s",
738 scf_strerror(scf_error()));
739 scf_handle_destroy(h);
740 return (ISC_R_FAILURE);
743 if ((namelen = scf_myname(h, NULL, 0)) == -1) {
745 UNEXPECTED_ERROR(__FILE__, __LINE__,
746 "scf_myname() failed: %s",
747 scf_strerror(scf_error()));
748 scf_handle_destroy(h);
749 return (ISC_R_FAILURE);
752 if ((instance = isc_mem_allocate(mctx, namelen + 1)) == NULL) {
753 UNEXPECTED_ERROR(__FILE__, __LINE__,
754 "ns_smf_get_instance memory "
755 "allocation failed: %s",
756 isc_result_totext(ISC_R_NOMEMORY));
757 scf_handle_destroy(h);
758 return (ISC_R_FAILURE);
761 if (scf_myname(h, instance, namelen + 1) == -1) {
763 UNEXPECTED_ERROR(__FILE__, __LINE__,
764 "scf_myname() failed: %s",
765 scf_strerror(scf_error()));
766 scf_handle_destroy(h);
767 isc_mem_free(mctx, instance);
768 return (ISC_R_FAILURE);
771 scf_handle_destroy(h);
772 *ins_name = instance;
773 return (ISC_R_SUCCESS);
775 #endif /* HAVE_LIBSCF */
778 main(int argc, char *argv[]) {
781 char *instance = NULL;
785 * Record version in core image.
786 * strings named.core | grep "named version:"
790 "named version: BIND " VERSION " (" __DATE__ ")",
792 "named version: BIND " VERSION,
795 result = isc_file_progname(*argv, program_name, sizeof(program_name));
796 if (result != ISC_R_SUCCESS)
797 ns_main_earlyfatal("program name too long");
799 if (strcmp(program_name, "lwresd") == 0)
800 ns_g_lwresdonly = ISC_TRUE;
802 isc_assertion_setcallback(assertion_failed);
803 isc_error_setfatal(library_fatal_error);
804 isc_error_setunexpected(library_unexpected_error);
806 ns_os_init(program_name);
808 dns_result_register();
809 dst_result_register();
810 isccc_result_register();
812 parse_command_line(argc, argv);
815 * Warn about common configuration error.
817 if (ns_g_chrootdir != NULL) {
818 int len = strlen(ns_g_chrootdir);
819 if (strncmp(ns_g_chrootdir, ns_g_conffile, len) == 0 &&
820 (ns_g_conffile[len] == '/' || ns_g_conffile[len] == '\\'))
821 ns_main_earlywarning("config filename (-c %s) contains "
822 "chroot path (-t %s)",
823 ns_g_conffile, ns_g_chrootdir);
826 result = isc_mem_create(0, 0, &ns_g_mctx);
827 if (result != ISC_R_SUCCESS)
828 ns_main_earlyfatal("isc_mem_create() failed: %s",
829 isc_result_totext(result));
834 * Start things running and then wait for a shutdown request
838 result = isc_app_run();
840 if (result == ISC_R_RELOAD) {
841 ns_server_reloadwanted(ns_g_server);
842 } else if (result != ISC_R_SUCCESS) {
843 UNEXPECTED_ERROR(__FILE__, __LINE__,
845 isc_result_totext(result));
849 result = ISC_R_SUCCESS;
851 } while (result != ISC_R_SUCCESS);
854 if (ns_smf_want_disable == 1) {
855 result = ns_smf_get_instance(&instance, 1, ns_g_mctx);
856 if (result == ISC_R_SUCCESS && instance != NULL) {
857 if (smf_disable_instance(instance, 0) != 0)
858 UNEXPECTED_ERROR(__FILE__, __LINE__,
859 "smf_disable_instance() ",
860 "failed for %s : %s",
862 scf_strerror(scf_error()));
864 if (instance != NULL)
865 isc_mem_free(ns_g_mctx, instance);
867 #endif /* HAVE_LIBSCF */
872 isc_mem_stats(ns_g_mctx, stdout);
873 isc_mutex_stats(stdout);
875 if (memstats != NULL) {
877 result = isc_stdio_open(memstats, "w", &fp);
878 if (result == ISC_R_SUCCESS) {
879 isc_mem_stats(ns_g_mctx, fp);
884 isc_mem_destroy(&ns_g_mctx);
886 ns_main_setmemstats(NULL);
890 ns_os_closedevnull();