Beispiel #1
0
static int kill_pid (char *pidfile, int sig)
{
  pid_t pid = readpid (pidfile);
  int r = 0;

  if (!pid || (r = kill (pid, sig)))
    {
      logger (LOG_ERR, ""PACKAGE" not running");
      unlink (pidfile);
    }

  return r;
}
Beispiel #2
0
/**
 * check old pid file.
 * @param pidfile: the file name of the pid file.
 * @param inchroot: if pidfile is inchroot and we can thus expect to
 *	be able to delete it.
 */
static void
checkoldpid(char* pidfile, int inchroot)
{
	pid_t old;
	if((old = readpid(pidfile)) != -1) {
		/* see if it is still alive */
		if(kill(old, 0) == 0 || errno == EPERM)
			log_warn("unbound is already running as pid %u.", 
				(unsigned)old);
		else	if(inchroot)
			log_warn("did not exit gracefully last time (%u)", 
				(unsigned)old);
	}
}
Beispiel #3
0
static void check_daemon(void)
{
	int pid;

	if ((pid = readpid()) < 0 || kill(pid, 0) < 0)
	{
		/* daemon ping failed, try to start it up */
		if (run_daemon())
		{
			fprintf(stderr,
				"Failed to ping daemon and unable to start it up: %s\n",
				strerror(errno));

			exit(1);
		}
	}
	else if (kill(pid, SIGUSR1))
	{
		fprintf(stderr, "Failed to send signal: %s\n", strerror(errno));
		exit(2);
	}
}
Beispiel #4
0
int
main(int argc, char *argv[])
{
	/* Scratch variables... */
	int c;
	pid_t	oldpid;
	size_t i;
	struct sigaction action;
#ifdef HAVE_GETPWNAM
	struct passwd *pwd = NULL;
#endif /* HAVE_GETPWNAM */

	struct addrinfo hints[2];
	int hints_in_use = 1;
	char** nodes = NULL; /* array of address strings, size nsd.ifs */
	const char *udp_port = 0;
	const char *tcp_port = 0;

	const char *configfile = CONFIGFILE;

	char* argv0 = (argv0 = strrchr(argv[0], '/')) ? argv0 + 1 : argv[0];

	log_init(argv0);

	/* Initialize the server handler... */
	memset(&nsd, 0, sizeof(struct nsd));
	nsd.region      = region_create(xalloc, free);
	nsd.dbfile	= 0;
	nsd.pidfile	= 0;
	nsd.server_kind = NSD_SERVER_MAIN;
	memset(&hints, 0, sizeof(*hints)*2);
	hints[0].ai_family = DEFAULT_AI_FAMILY;
	hints[0].ai_flags = AI_PASSIVE;
	hints[1].ai_family = DEFAULT_AI_FAMILY;
	hints[1].ai_flags = AI_PASSIVE;
	nsd.identity	= 0;
	nsd.version	= VERSION;
	nsd.username	= 0;
	nsd.chrootdir	= 0;
	nsd.nsid 	= NULL;
	nsd.nsid_len 	= 0;

	nsd.child_count = 0;
	nsd.maximum_tcp_count = 0;
	nsd.current_tcp_count = 0;
	nsd.grab_ip6_optional = 0;
	nsd.file_rotation_ok = 0;

	/* Set up our default identity to gethostname(2) */
	if (gethostname(hostname, MAXHOSTNAMELEN) == 0) {
		nsd.identity = hostname;
	} else {
		log_msg(LOG_ERR,
			"failed to get the host name: %s - using default identity",
			strerror(errno));
		nsd.identity = IDENTITY;
	}

	/* Parse the command line... */
	while ((c = getopt(argc, argv, "46a:c:df:hi:I:l:N:n:P:p:s:u:t:X:V:v"
#ifndef NDEBUG /* <mattthijs> only when configured with --enable-checking */
		"F:L:"
#endif /* NDEBUG */
		)) != -1) {
		switch (c) {
		case '4':
			hints[0].ai_family = AF_INET;
			break;
		case '6':
#ifdef INET6
			hints[0].ai_family = AF_INET6;
#else /* !INET6 */
			error("IPv6 support not enabled.");
#endif /* INET6 */
			break;
		case 'a':
			add_interface(&nodes, &nsd, optarg);
			break;
		case 'c':
			configfile = optarg;
			break;
		case 'd':
			nsd.debug = 1;
			break;
		case 'f':
			nsd.dbfile = optarg;
			break;
		case 'h':
			usage();
			exit(0);
		case 'i':
			nsd.identity = optarg;
			break;
		case 'I':
			if (nsd.nsid_len != 0) {
				/* can only be given once */
				break;
			}
			if (strncasecmp(optarg, "ascii_", 6) == 0) {
				nsd.nsid = xalloc(strlen(optarg+6));
				nsd.nsid_len = strlen(optarg+6);
				memmove(nsd.nsid, optarg+6, nsd.nsid_len);
			} else {
				if (strlen(optarg) % 2 != 0) {
					error("the NSID must be a hex string of an even length.");
				}
				nsd.nsid = xalloc(strlen(optarg) / 2);
				nsd.nsid_len = strlen(optarg) / 2;
				if (hex_pton(optarg, nsd.nsid, nsd.nsid_len) == -1) {
					error("hex string cannot be parsed '%s' in NSID.", optarg);
				}
			}
			break;
		case 'l':
			nsd.log_filename = optarg;
			break;
		case 'N':
			i = atoi(optarg);
			if (i <= 0) {
				error("number of child servers must be greater than zero.");
			} else {
				nsd.child_count = i;
			}
			break;
		case 'n':
			i = atoi(optarg);
			if (i <= 0) {
				error("number of concurrent TCP connections must greater than zero.");
			} else {
				nsd.maximum_tcp_count = i;
			}
			break;
		case 'P':
			nsd.pidfile = optarg;
			break;
		case 'p':
			if (atoi(optarg) == 0) {
				error("port argument must be numeric.");
			}
			tcp_port = optarg;
			udp_port = optarg;
			break;
		case 's':
#ifdef BIND8_STATS
			nsd.st.period = atoi(optarg);
#else /* !BIND8_STATS */
			error("BIND 8 statistics not enabled.");
#endif /* BIND8_STATS */
			break;
		case 't':
#ifdef HAVE_CHROOT
			nsd.chrootdir = optarg;
#else /* !HAVE_CHROOT */
			error("chroot not supported on this platform.");
#endif /* HAVE_CHROOT */
			break;
		case 'u':
			nsd.username = optarg;
			break;
		case 'V':
			verbosity = atoi(optarg);
			break;
		case 'v':
			version();
			/* version exits */
			break;
#ifndef NDEBUG
		case 'F':
			sscanf(optarg, "%x", &nsd_debug_facilities);
			break;
		case 'L':
			sscanf(optarg, "%d", &nsd_debug_level);
			break;
#endif /* NDEBUG */
		case '?':
		default:
			usage();
			exit(1);
		}
	}
	argc -= optind;
	/* argv += optind; */

	/* Commandline parse error */
	if (argc != 0) {
		usage();
		exit(1);
	}

	if (strlen(nsd.identity) > UCHAR_MAX) {
		error("server identity too long (%u characters)",
		      (unsigned) strlen(nsd.identity));
	}
	if(!tsig_init(nsd.region))
		error("init tsig failed");

	/* Read options */
	nsd.options = nsd_options_create(region_create_custom(xalloc, free,
		DEFAULT_CHUNK_SIZE, DEFAULT_LARGE_OBJECT_SIZE,
		DEFAULT_INITIAL_CLEANUP_SIZE, 1));
	if(!parse_options_file(nsd.options, configfile, NULL, NULL)) {
		error("could not read config: %s\n", configfile);
	}
	if(!parse_zone_list_file(nsd.options)) {
		error("could not read zonelist file %s\n",
			nsd.options->zonelistfile);
	}
	if(nsd.options->do_ip4 && !nsd.options->do_ip6) {
		hints[0].ai_family = AF_INET;
	}
#ifdef INET6
	if(nsd.options->do_ip6 && !nsd.options->do_ip4) {
		hints[0].ai_family = AF_INET6;
	}
#endif /* INET6 */
	if(nsd.options->ip_addresses)
	{
		ip_address_option_type* ip = nsd.options->ip_addresses;
		while(ip) {
			add_interface(&nodes, &nsd, ip->address);
			ip = ip->next;
		}
	}
	if (verbosity == 0)
		verbosity = nsd.options->verbosity;
#ifndef NDEBUG
	if (nsd_debug_level > 0 && verbosity == 0)
		verbosity = nsd_debug_level;
#endif /* NDEBUG */
	if(nsd.options->debug_mode) nsd.debug=1;
	if(!nsd.dbfile)
	{
		if(nsd.options->database)
			nsd.dbfile = nsd.options->database;
		else
			nsd.dbfile = DBFILE;
	}
	if(!nsd.pidfile)
	{
		if(nsd.options->pidfile)
			nsd.pidfile = nsd.options->pidfile;
		else
			nsd.pidfile = PIDFILE;
	}
	if(strcmp(nsd.identity, hostname)==0 || strcmp(nsd.identity,IDENTITY)==0)
	{
		if(nsd.options->identity)
			nsd.identity = nsd.options->identity;
	}
	if(nsd.options->version) {
		nsd.version = nsd.options->version;
	}
	if (nsd.options->logfile && !nsd.log_filename) {
		nsd.log_filename = nsd.options->logfile;
	}
	if(nsd.child_count == 0) {
		nsd.child_count = nsd.options->server_count;
	}
#ifdef SO_REUSEPORT
	if(nsd.options->reuseport && nsd.child_count > 1) {
		nsd.reuseport = nsd.child_count;
	}
#endif /* SO_REUSEPORT */
	if(nsd.maximum_tcp_count == 0) {
		nsd.maximum_tcp_count = nsd.options->tcp_count;
	}
	nsd.tcp_timeout = nsd.options->tcp_timeout;
	nsd.tcp_query_count = nsd.options->tcp_query_count;
	nsd.tcp_mss = nsd.options->tcp_mss;
	nsd.outgoing_tcp_mss = nsd.options->outgoing_tcp_mss;
	nsd.ipv4_edns_size = nsd.options->ipv4_edns_size;
	nsd.ipv6_edns_size = nsd.options->ipv6_edns_size;

	if(udp_port == 0)
	{
		if(nsd.options->port != 0) {
			udp_port = nsd.options->port;
			tcp_port = nsd.options->port;
		} else {
			udp_port = UDP_PORT;
			tcp_port = TCP_PORT;
		}
	}
#ifdef BIND8_STATS
	if(nsd.st.period == 0) {
		nsd.st.period = nsd.options->statistics;
	}
#endif /* BIND8_STATS */
#ifdef HAVE_CHROOT
	if(nsd.chrootdir == 0) nsd.chrootdir = nsd.options->chroot;
#ifdef CHROOTDIR
	/* if still no chrootdir, fallback to default */
	if(nsd.chrootdir == 0) nsd.chrootdir = CHROOTDIR;
#endif /* CHROOTDIR */
#endif /* HAVE_CHROOT */
	if(nsd.username == 0) {
		if(nsd.options->username) nsd.username = nsd.options->username;
		else nsd.username = USER;
	}
	if(nsd.options->zonesdir && nsd.options->zonesdir[0]) {
		if(chdir(nsd.options->zonesdir)) {
			error("cannot chdir to '%s': %s",
				nsd.options->zonesdir, strerror(errno));
		}
		DEBUG(DEBUG_IPC,1, (LOG_INFO, "changed directory to %s",
			nsd.options->zonesdir));
	}

	/* EDNS0 */
	edns_init_data(&nsd.edns_ipv4, nsd.options->ipv4_edns_size);
#if defined(INET6)
#if defined(IPV6_USE_MIN_MTU) || defined(IPV6_MTU)
	edns_init_data(&nsd.edns_ipv6, nsd.options->ipv6_edns_size);
#else /* no way to set IPV6 MTU, send no bigger than that. */
	if (nsd.options->ipv6_edns_size < IPV6_MIN_MTU)
		edns_init_data(&nsd.edns_ipv6, nsd.options->ipv6_edns_size);
	else
		edns_init_data(&nsd.edns_ipv6, IPV6_MIN_MTU);
#endif /* IPV6 MTU) */
#endif /* defined(INET6) */

	if (nsd.nsid_len == 0 && nsd.options->nsid) {
		if (strlen(nsd.options->nsid) % 2 != 0) {
			error("the NSID must be a hex string of an even length.");
		}
		nsd.nsid = xalloc(strlen(nsd.options->nsid) / 2);
		nsd.nsid_len = strlen(nsd.options->nsid) / 2;
		if (hex_pton(nsd.options->nsid, nsd.nsid, nsd.nsid_len) == -1) {
			error("hex string cannot be parsed '%s' in NSID.", nsd.options->nsid);
		}
	}
	edns_init_nsid(&nsd.edns_ipv4, nsd.nsid_len);
#if defined(INET6)
	edns_init_nsid(&nsd.edns_ipv6, nsd.nsid_len);
#endif /* defined(INET6) */

	/* Number of child servers to fork.  */
	nsd.children = (struct nsd_child *) region_alloc_array(
		nsd.region, nsd.child_count, sizeof(struct nsd_child));
	for (i = 0; i < nsd.child_count; ++i) {
		nsd.children[i].kind = NSD_SERVER_BOTH;
		nsd.children[i].pid = -1;
		nsd.children[i].child_fd = -1;
		nsd.children[i].parent_fd = -1;
		nsd.children[i].handler = NULL;
		nsd.children[i].need_to_send_STATS = 0;
		nsd.children[i].need_to_send_QUIT = 0;
		nsd.children[i].need_to_exit = 0;
		nsd.children[i].has_exited = 0;
#ifdef  BIND8_STATS
		nsd.children[i].query_count = 0;
#endif
	}

	nsd.this_child = NULL;

	/* We need at least one active interface */
	if (nsd.ifs == 0) {
		add_interface(&nodes, &nsd, NULL);

		/*
		 * With IPv6 we'd like to open two separate sockets,
		 * one for IPv4 and one for IPv6, both listening to
		 * the wildcard address (unless the -4 or -6 flags are
		 * specified).
		 *
		 * However, this is only supported on platforms where
		 * we can turn the socket option IPV6_V6ONLY _on_.
		 * Otherwise we just listen to a single IPv6 socket
		 * and any incoming IPv4 connections will be
		 * automatically mapped to our IPv6 socket.
		 */
#ifdef INET6
		if (hints[0].ai_family == AF_UNSPEC) {
#ifdef IPV6_V6ONLY
			add_interface(&nodes, &nsd, NULL);
			hints[0].ai_family = AF_INET6;
			hints[1].ai_family = AF_INET;
			hints_in_use = 2;
			nsd.grab_ip6_optional = 1;
#else /* !IPV6_V6ONLY */
			hints[0].ai_family = AF_INET6;
#endif	/* IPV6_V6ONLY */
		}
#endif /* INET6 */
	}

	/* Set up the address info structures with real interface/port data */
	assert(nodes);
	for (i = 0; i < nsd.ifs; ++i) {
		int r;
		const char* node = NULL;
		const char* service = NULL;
		int h = ((hints_in_use == 1)?0:i%hints_in_use);

		/* We don't perform name-lookups */
		if (nodes[i] != NULL)
			hints[h].ai_flags |= AI_NUMERICHOST;
		get_ip_port_frm_str(nodes[i], &node, &service);

		hints[h].ai_socktype = SOCK_DGRAM;
		if ((r=getaddrinfo(node, (service?service:udp_port), &hints[h], &nsd.udp[i].addr)) != 0) {
#ifdef INET6
			if(nsd.grab_ip6_optional && hints[0].ai_family == AF_INET6) {
				log_msg(LOG_WARNING, "No IPv6, fallback to IPv4. getaddrinfo: %s",
				r==EAI_SYSTEM?strerror(errno):gai_strerror(r));
				continue;
			}
#endif
			error("cannot parse address '%s': getaddrinfo: %s %s",
				nodes[i]?nodes[i]:"(null)",
				gai_strerror(r),
				r==EAI_SYSTEM?strerror(errno):"");
		}

		hints[h].ai_socktype = SOCK_STREAM;
		if ((r=getaddrinfo(node, (service?service:tcp_port), &hints[h], &nsd.tcp[i].addr)) != 0) {
			error("cannot parse address '%s': getaddrinfo: %s %s",
				nodes[i]?nodes[i]:"(null)",
				gai_strerror(r),
				r==EAI_SYSTEM?strerror(errno):"");
		}
	}

	/* Parse the username into uid and gid */
	nsd.gid = getgid();
	nsd.uid = getuid();
#ifdef HAVE_GETPWNAM
	/* Parse the username into uid and gid */
	if (*nsd.username) {
		if (isdigit((unsigned char)*nsd.username)) {
			char *t;
			nsd.uid = strtol(nsd.username, &t, 10);
			if (*t != 0) {
				if (*t != '.' || !isdigit((unsigned char)*++t)) {
					error("-u user or -u uid or -u uid.gid");
				}
				nsd.gid = strtol(t, &t, 10);
			} else {
				/* Lookup the group id in /etc/passwd */
				if ((pwd = getpwuid(nsd.uid)) == NULL) {
					error("user id %u does not exist.", (unsigned) nsd.uid);
				} else {
					nsd.gid = pwd->pw_gid;
				}
			}
		} else {
			/* Lookup the user id in /etc/passwd */
			if ((pwd = getpwnam(nsd.username)) == NULL) {
				error("user '%s' does not exist.", nsd.username);
			} else {
				nsd.uid = pwd->pw_uid;
				nsd.gid = pwd->pw_gid;
			}
		}
	}
	/* endpwent(); */
#endif /* HAVE_GETPWNAM */

#if defined(HAVE_SSL)
	key_options_tsig_add(nsd.options);
#endif

	append_trailing_slash(&nsd.options->xfrdir, nsd.options->region);
	/* Check relativity of pathnames to chroot */
	if (nsd.chrootdir && nsd.chrootdir[0]) {
		/* existing chrootdir: append trailing slash for strncmp checking */
		append_trailing_slash(&nsd.chrootdir, nsd.region);
		append_trailing_slash(&nsd.options->zonesdir, nsd.options->region);

		/* zonesdir must be absolute and within chroot,
		 * all other pathnames may be relative to zonesdir */
		if (strncmp(nsd.options->zonesdir, nsd.chrootdir, strlen(nsd.chrootdir)) != 0) {
			error("zonesdir %s has to be an absolute path that starts with the chroot path %s",
				nsd.options->zonesdir, nsd.chrootdir);
		} else if (!file_inside_chroot(nsd.pidfile, nsd.chrootdir)) {
			error("pidfile %s is not relative to %s: chroot not possible",
				nsd.pidfile, nsd.chrootdir);
		} else if (!file_inside_chroot(nsd.dbfile, nsd.chrootdir)) {
			error("database %s is not relative to %s: chroot not possible",
				nsd.dbfile, nsd.chrootdir);
		} else if (!file_inside_chroot(nsd.options->xfrdfile, nsd.chrootdir)) {
			error("xfrdfile %s is not relative to %s: chroot not possible",
				nsd.options->xfrdfile, nsd.chrootdir);
		} else if (!file_inside_chroot(nsd.options->zonelistfile, nsd.chrootdir)) {
			error("zonelistfile %s is not relative to %s: chroot not possible",
				nsd.options->zonelistfile, nsd.chrootdir);
		} else if (!file_inside_chroot(nsd.options->xfrdir, nsd.chrootdir)) {
			error("xfrdir %s is not relative to %s: chroot not possible",
				nsd.options->xfrdir, nsd.chrootdir);
		}
	}

	/* Set up the logging */
	log_open(LOG_PID, FACILITY, nsd.log_filename);
	if (!nsd.log_filename)
		log_set_log_function(log_syslog);
	else if (nsd.uid && nsd.gid) {
		if(chown(nsd.log_filename, nsd.uid, nsd.gid) != 0)
			VERBOSITY(2, (LOG_WARNING, "chown %s failed: %s",
				nsd.log_filename, strerror(errno)));
	}
	log_msg(LOG_NOTICE, "%s starting (%s)", argv0, PACKAGE_STRING);

	/* Do we have a running nsd? */
	if ((oldpid = readpid(nsd.pidfile)) == -1) {
		if (errno != ENOENT) {
			log_msg(LOG_ERR, "can't read pidfile %s: %s",
				nsd.pidfile, strerror(errno));
		}
	} else {
		if (kill(oldpid, 0) == 0 || errno == EPERM) {
			log_msg(LOG_WARNING,
				"%s is already running as %u, continuing",
				argv0, (unsigned) oldpid);
		} else {
			log_msg(LOG_ERR,
				"...stale pid file from process %u",
				(unsigned) oldpid);
		}
	}

	/* Setup the signal handling... */
	action.sa_handler = sig_handler;
	sigfillset(&action.sa_mask);
	action.sa_flags = 0;
	sigaction(SIGTERM, &action, NULL);
	sigaction(SIGHUP, &action, NULL);
	sigaction(SIGINT, &action, NULL);
	sigaction(SIGILL, &action, NULL);
	sigaction(SIGUSR1, &action, NULL);
	sigaction(SIGALRM, &action, NULL);
	sigaction(SIGCHLD, &action, NULL);
	action.sa_handler = SIG_IGN;
	sigaction(SIGPIPE, &action, NULL);

	/* Initialize... */
	nsd.mode = NSD_RUN;
	nsd.signal_hint_child = 0;
	nsd.signal_hint_reload = 0;
	nsd.signal_hint_reload_hup = 0;
	nsd.signal_hint_quit = 0;
	nsd.signal_hint_shutdown = 0;
	nsd.signal_hint_stats = 0;
	nsd.signal_hint_statsusr = 0;
	nsd.quit_sync_done = 0;

	/* Initialize the server... */
	if (server_init(&nsd) != 0) {
		error("server initialization failed, %s could "
			"not be started", argv0);
	}
#if defined(HAVE_SSL)
	if(nsd.options->control_enable) {
		/* read ssl keys while superuser and outside chroot */
		if(!(nsd.rc = daemon_remote_create(nsd.options)))
			error("could not perform remote control setup");
	}
#endif /* HAVE_SSL */

	/* Unless we're debugging, fork... */
	if (!nsd.debug) {
		int fd;

		/* Take off... */
		switch ((nsd.pid = fork())) {
		case 0:
			/* Child */
			break;
		case -1:
			error("fork() failed: %s", strerror(errno));
			break;
		default:
			/* Parent is done */
			server_close_all_sockets(nsd.udp, nsd.ifs);
			server_close_all_sockets(nsd.tcp, nsd.ifs);
			exit(0);
		}

		/* Detach ourselves... */
		if (setsid() == -1) {
			error("setsid() failed: %s", strerror(errno));
		}

		if ((fd = open("/dev/null", O_RDWR, 0)) != -1) {
			(void)dup2(fd, STDIN_FILENO);
			(void)dup2(fd, STDOUT_FILENO);
			(void)dup2(fd, STDERR_FILENO);
			if (fd > 2)
				(void)close(fd);
		}
	}

	/* Get our process id */
	nsd.pid = getpid();

	/* Set user context */
#ifdef HAVE_GETPWNAM
	if (*nsd.username) {
#ifdef HAVE_SETUSERCONTEXT
		/* setusercontext does initgroups, setuid, setgid, and
		 * also resource limits from login config, but we
		 * still call setresuid, setresgid to be sure to set all uid */
		if (setusercontext(NULL, pwd, nsd.uid,
			LOGIN_SETALL & ~LOGIN_SETUSER & ~LOGIN_SETGROUP) != 0)
			log_msg(LOG_WARNING, "unable to setusercontext %s: %s",
				nsd.username, strerror(errno));
#endif /* HAVE_SETUSERCONTEXT */
	}
#endif /* HAVE_GETPWNAM */

	/* Chroot */
#ifdef HAVE_CHROOT
	if (nsd.chrootdir && nsd.chrootdir[0]) {
		int l = strlen(nsd.chrootdir)-1; /* ends in trailing slash */

		if (file_inside_chroot(nsd.log_filename, nsd.chrootdir))
			nsd.file_rotation_ok = 1;

		/* strip chroot from pathnames if they're absolute */
		nsd.options->zonesdir += l;
		if (nsd.log_filename){
			if (nsd.log_filename[0] == '/')
				nsd.log_filename += l;
		}
		if (nsd.pidfile[0] == '/')
			nsd.pidfile += l;
		if (nsd.dbfile[0] == '/')
			nsd.dbfile += l;
		if (nsd.options->xfrdfile[0] == '/')
			nsd.options->xfrdfile += l;
		if (nsd.options->zonelistfile[0] == '/')
			nsd.options->zonelistfile += l;
		if (nsd.options->xfrdir[0] == '/')
			nsd.options->xfrdir += l;

		/* strip chroot from pathnames of "include:" statements
		 * on subsequent repattern commands */
		cfg_parser->chroot = nsd.chrootdir;

#ifdef HAVE_TZSET
		/* set timezone whilst not yet in chroot */
		tzset();
#endif
		if (chroot(nsd.chrootdir)) {
			error("unable to chroot: %s", strerror(errno));
		}
		if (chdir("/")) {
			error("unable to chdir to chroot: %s", strerror(errno));
		}
		DEBUG(DEBUG_IPC,1, (LOG_INFO, "changed root directory to %s",
			nsd.chrootdir));
		/* chdir to zonesdir again after chroot */
		if(nsd.options->zonesdir && nsd.options->zonesdir[0]) {
			if(chdir(nsd.options->zonesdir)) {
				error("unable to chdir to '%s': %s",
					nsd.options->zonesdir, strerror(errno));
			}
			DEBUG(DEBUG_IPC,1, (LOG_INFO, "changed directory to %s",
				nsd.options->zonesdir));
		}
	}
	else
#endif /* HAVE_CHROOT */
		nsd.file_rotation_ok = 1;

	DEBUG(DEBUG_IPC,1, (LOG_INFO, "file rotation on %s %sabled",
		nsd.log_filename, nsd.file_rotation_ok?"en":"dis"));

	/* Write pidfile */
	if (writepid(&nsd) == -1) {
		log_msg(LOG_ERR, "cannot overwrite the pidfile %s: %s",
			nsd.pidfile, strerror(errno));
	}

	/* Drop the permissions */
#ifdef HAVE_GETPWNAM
	if (*nsd.username) {
#ifdef HAVE_INITGROUPS
		if(initgroups(nsd.username, nsd.gid) != 0)
			log_msg(LOG_WARNING, "unable to initgroups %s: %s",
				nsd.username, strerror(errno));
#endif /* HAVE_INITGROUPS */
		endpwent();

#ifdef HAVE_SETRESGID
		if(setresgid(nsd.gid,nsd.gid,nsd.gid) != 0)
#elif defined(HAVE_SETREGID) && !defined(DARWIN_BROKEN_SETREUID)
			if(setregid(nsd.gid,nsd.gid) != 0)
#else /* use setgid */
				if(setgid(nsd.gid) != 0)
#endif /* HAVE_SETRESGID */
					error("unable to set group id of %s: %s",
						nsd.username, strerror(errno));

#ifdef HAVE_SETRESUID
		if(setresuid(nsd.uid,nsd.uid,nsd.uid) != 0)
#elif defined(HAVE_SETREUID) && !defined(DARWIN_BROKEN_SETREUID)
			if(setreuid(nsd.uid,nsd.uid) != 0)
#else /* use setuid */
				if(setuid(nsd.uid) != 0)
#endif /* HAVE_SETRESUID */
					error("unable to set user id of %s: %s",
						nsd.username, strerror(errno));

		DEBUG(DEBUG_IPC,1, (LOG_INFO, "dropped user privileges, run as %s",
			nsd.username));
	}
#endif /* HAVE_GETPWNAM */
	xfrd_make_tempdir(&nsd);
#ifdef USE_ZONE_STATS
	options_zonestatnames_create(nsd.options);
	server_zonestat_alloc(&nsd);
#endif /* USE_ZONE_STATS */
#ifdef USE_DNSTAP
	if(nsd.options->dnstap_enable) {
		nsd.dt_collector = dt_collector_create(&nsd);
		dt_collector_start(nsd.dt_collector, &nsd);
	}
#endif /* USE_DNSTAP */

	if(nsd.server_kind == NSD_SERVER_MAIN) {
		server_prepare_xfrd(&nsd);
		/* xfrd forks this before reading database, so it does not get
		 * the memory size of the database */
		server_start_xfrd(&nsd, 0, 0);
		/* close zonelistfile in non-xfrd processes */
		zone_list_close(nsd.options);
	}
	if (server_prepare(&nsd) != 0) {
		unlinkpid(nsd.pidfile);
		error("server preparation failed, %s could "
			"not be started", argv0);
	}
	if(nsd.server_kind == NSD_SERVER_MAIN) {
		server_send_soa_xfrd(&nsd, 0);
	}

	/* Really take off */
	log_msg(LOG_NOTICE, "%s started (%s), pid %d",
		argv0, PACKAGE_STRING, (int) nsd.pid);

	if (nsd.server_kind == NSD_SERVER_MAIN) {
		server_main(&nsd);
	} else {
		server_child(&nsd);
	}

	/* NOTREACH */
	exit(0);
}
Beispiel #5
0
int main(int argc, char **argv)
{
  options_t options;

  /* Sanitize our fd's */
  int zero;
  if ((zero = open (_PATH_DEVNULL, O_RDWR, 0)) >= 0)
    {
      while (zero < 3)
	zero = dup (zero);
      close(zero);
    }

  openlog (PACKAGE, LOG_PID, LOG_LOCAL0);

  memset (&options, 0, sizeof (options_t));
  options.script = DEFAULT_SCRIPT;
  snprintf (options.classid, CLASS_ID_MAX_LEN, "%s %s", PACKAGE, VERSION); 

  options.doarp = false;
  options.dodns = true;
  options.dontp = true;
  options.dogateway = true;
  options.timeout = DEFAULT_TIMEOUT;

  int doversion = 0;
  int dohelp = 0;
  int userclasses = 0;

  const struct option longopts[] =
    {
	{"arp", no_argument, NULL, 'a'},
	{"script",required_argument, NULL, 'c'},
	{"debug", no_argument, NULL, 'd'},
	{"hostname", required_argument, NULL, 'h'},
	{"classid", required_argument, NULL, 'i'},
	{"release", no_argument, NULL, 'k'},
	{"leasetime", required_argument, NULL, 'l'},
	{"metric", required_argument, NULL, 'm'},
	{"renew", no_argument, NULL, 'n'},
	{"persistent", no_argument, NULL, 'p'},
	{"request", required_argument, NULL, 's'},
	{"timeout", required_argument, NULL, 't'},
	{"userclass", required_argument, NULL, 'u'},
	{"fqdn", optional_argument, NULL, 'F'},
	{"nogateway", no_argument, NULL, 'G'},
	{"sethostname", no_argument, NULL, 'H'},
	{"clientid", required_argument, NULL, 'I'},
	{"nontp", no_argument, NULL, 'N'},
	{"nodns", no_argument, NULL, 'R'},
	{"nonis", no_argument, NULL, 'Y'},
	{"help", no_argument, &dohelp, 1},
	{"version", no_argument, &doversion, 1},
	{NULL, 0, NULL, 0}
    };

  int ch;
  int option_index = 0;
  while ((ch = getopt_long(argc, argv, "ac:dh:i:kl:m:nps:t:u:F:GHI:NRY", longopts,
			   &option_index)) != -1)
    switch (ch)
      {
      case 0:
	if (longopts[option_index].flag)
	  break;
	logger (LOG_ERR, "option `%s' should set a flag",
		longopts[option_index].name);
	exit (EXIT_FAILURE);
	break;

      case 'a':
	options.doarp = true;
	break;
      case 'c':
	options.script = optarg;
	break;
      case 'd':
	setloglevel(LOG_DEBUG);
	break;
      case 'h':
	if (strlen (optarg) > HOSTNAME_MAX_LEN)
	  {
	    logger(LOG_ERR, "`%s' too long for HostName string, max is %d",
		   optarg, HOSTNAME_MAX_LEN);
	    exit (EXIT_FAILURE);
	  }
	else
	  options.hostname = optarg;
	break;
      case 'i':
	if (strlen(optarg) > CLASS_ID_MAX_LEN)
	  {
	    logger (LOG_ERR, "`%s' too long for ClassID string, max is %d",
		    optarg, CLASS_ID_MAX_LEN);
	    exit (EXIT_FAILURE);
	  }
	else
	  sprintf(options.classid, "%s", optarg);
	break;
      case 'k':
	options.signal = SIGHUP;
	break;
      case 'l':
	STRINGINT (optarg, options.leasetime);
	if (options.leasetime <= 0)
	  {
	    logger (LOG_ERR, "leasetime must be a positive value");
	    exit (EXIT_FAILURE);
	  }
	break;
      case 'm':
	STRINGINT(optarg, options.metric);
	break;
      case 'n':
	options.signal = SIGALRM;
	break;
      case 'p':
	options.persistent = true;
	break;
      case 's':
	if (! inet_aton (optarg, &options.requestaddress))
	  {
	    logger (LOG_ERR, "`%s' is not a valid IP address", optarg);
	    exit (EXIT_FAILURE);
	  }
	break;
      case 't':
	STRINGINT (optarg, options.timeout);
	if (options.timeout < 0)
	  {
	    logger (LOG_ERR, "timeout must be a positive value");
	    exit (EXIT_FAILURE);
	  }
	break;
      case 'u':
	  {
	    int i;
	    int offset = 0;
	    for (i = 0; i < userclasses; i++)
	      offset += (int) options.userclass[offset] + 1;
	    if (offset + 1 + strlen (optarg) > USERCLASS_MAX_LEN)
	      {
		logger (LOG_ERR, "userclass overrun, max is %d",
			USERCLASS_MAX_LEN);
		exit (EXIT_FAILURE);
	      }
	    userclasses++;
	    memcpy (options.userclass + offset + 1 , optarg, strlen (optarg));
	    options.userclass[offset] = strlen (optarg);
	  }
	break;
      case 'F':
	if (strcmp (optarg, "none") == 0)
	  options.fqdn = FQDN_NONE;
	else if (strcmp (optarg, "ptr") == 0)
	  options.fqdn = FQDN_PTR;
	else if (strcmp (optarg, "both") == 0)
	  options.fqdn = FQDN_BOTH;
	else
	  {
	    logger (LOG_ERR, "invalid value `%s' for FQDN", optarg);
	    exit (EXIT_FAILURE);
	  }
	break;
      case 'G':
	options.dogateway = false;
	break;
      case 'H':
	options.dohostname = true;
	break;
      case 'I':
	if (strlen (optarg) > CLIENT_ID_MAX_LEN)
	  {
	    logger (LOG_ERR, "`%s' is too long for ClientID, max is %d",
		    optarg, CLIENT_ID_MAX_LEN);
	    exit (EXIT_FAILURE);
	  }
	else
	  sprintf(options.clientid, "%s", optarg);
	break;
      case 'N':
	options.dontp = false;
	break;
      case 'R':
	options.dodns = false;
	break;
      case 'Y':
	options.donis = false;
	break;
      case '?':
	usage ();
	exit (EXIT_FAILURE);
      default:
	usage ();
	exit (EXIT_FAILURE);
      }

  if (doversion)
    printf (""PACKAGE" "VERSION"\n");

  if (dohelp)
    usage ();

  if (optind < argc)
    {
      if (strlen (argv[optind]) > IF_NAMESIZE)
	{
	  logger (LOG_ERR, "`%s' is too long for an interface name (max=%d)",
		  argv[optind], IF_NAMESIZE);
	  exit (EXIT_FAILURE);
	}
      options.interface = argv[optind];
    }
  else
    {
      /* If only version was requested then exit now */
      if (doversion || dohelp)
	exit (EXIT_SUCCESS);

      logger (LOG_ERR, "no interface specified", options.interface);
      exit (EXIT_FAILURE);
    }

  if (geteuid ())
    {
      logger (LOG_ERR, "you need to be root to run "PACKAGE);
      exit (EXIT_FAILURE);
    }

  char prefix[IF_NAMESIZE + 3];
  snprintf (prefix, IF_NAMESIZE, "%s: ", options.interface);
  setlogprefix (prefix);
  snprintf (options.pidfile, sizeof (options.pidfile), PIDFILE,
	    options.interface);

  if (options.signal != 0)
    exit (kill_pid (options.pidfile, options.signal));

  umask (022);

  if (readpid (options.pidfile))
    {
      logger (LOG_ERR, ""PACKAGE" already running (%s)", options.pidfile);
      exit (EXIT_FAILURE);
    }

  if (mkdir (CONFIGDIR, S_IRUSR |S_IWUSR |S_IXUSR | S_IRGRP | S_IXGRP
	     | S_IROTH | S_IXOTH) && errno != EEXIST )
    {
      logger( LOG_ERR, "mkdir(\"%s\",0): %m\n", CONFIGDIR);
      exit (EXIT_FAILURE);
    }

  if (mkdir (ETCDIR, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP
	     | S_IROTH | S_IXOTH) && errno != EEXIST )
    {
      logger (LOG_ERR, "mkdir(\"%s\",0): %m\n", ETCDIR);
      exit (EXIT_FAILURE);
    }

  logger (LOG_INFO, PACKAGE " " VERSION " starting");
  if (dhcp_run (&options))
    exit (EXIT_FAILURE);

  exit (EXIT_SUCCESS);
}
Beispiel #6
0
int main(int argc, char** argv)
{
	int option;
	int option_index = 0;

	char *interface = NULL;
	char *service = NULL;

#ifdef CONFIG_DAEMON
	int killdaemon = 0;
	pid_t pid;
#endif /* CONFIG_DAEMON */

	struct sockaddr_storage clientaddr;
	socklen_t clientsize = sizeof(clientaddr);
	int client;
	request_t *request;
	response_t *response;

#ifdef CONFIG_DEBUG
	char clientstr[INET6_ADDRSTRLEN];
#endif /* CONFIG_DEBUG */

	/* getopt_long options */
	static struct option long_options[] = {
		{"version", 	no_argument,		NULL,  0 },
		{"help",		no_argument,		NULL,  0 },
		{"interface",	required_argument,	NULL, 'i'},
		{"port",		required_argument,	NULL, 'p'},
#ifdef CONFIG_DAEMON
		{"pidfile",		required_argument,	NULL, 'P'},
		{"kill",		no_argument,		NULL, 'k'},
#endif /* CONFIG_DAEMON */
		{NULL,			0,					NULL,  0 }
	};

#ifdef CONFIG_DAEMON
	/* Open syslog connection */
	openlog(PACKAGE_NAME, LOG_PID | LOG_NDELAY | LOG_PERROR, LOG_DAEMON);
#endif /* CONFIG_DAEMON */

	/* Check for options */
#ifndef CONFIG_DAEMON
#define OPTSTRING	"i:p:0"
#else
#define OPTSTRING	"i:p:Pk0"
#endif /* CONFIG_DAEMON */

	while((option = getopt_long(argc, argv, OPTSTRING,
					long_options, &option_index)) != EOF) {
		switch(option) {
			case 'i':
				if((interface = malloc(strlen(optarg) + 1)) == NULL) {
					eerror("Failed to allocate interface");
					exit(EXIT_FAILURE);
				}
				strcpy(interface, optarg);
				break;
			case 'p':
				if((service = malloc(strlen(optarg) + 1)) == NULL) {
					eerror("Failed to allocate service");
					exit(EXIT_FAILURE);
				}
				strcpy(service, optarg);
				break;
#ifdef CONFIG_DAEMON
			case 'P':
				if((global.pidfile = malloc(strlen(optarg) + 1)) == NULL) {
					eerror("Failed to allocate pidfile");
					exit(EXIT_FAILURE);
				}
				strcpy(global.pidfile, optarg);
				break;
			case 'k':
				killdaemon = 1;
				break;
#endif /* CONFIG_DAEMON */
			case 0:
				if(option_index == 0) {
					fprintf(stdout, "%s\n", PACKAGE_STRING);
					exit(EXIT_SUCCESS);
				}
			case '?':
				fprintf(stderr,
						"Usage: " PACKAGE_NAME " [OPTIONS]\n\n"
						"-i, --interface INTF    Use the specified interface ("
						"default is " DEFAULT_INTERFACE ").\n"
						"-p, --port PORT         Use the specified port ("
						"default is " DEFAULT_PORT ").\n"
#ifdef CONFIG_DAEMON
						"-P, --pidfile PIDFILE   Use the specified pidfile ("
						"default is " DEFAULT_PIDFILE ").\n"
						"-k, --kill              Kill the running daemon.\n"
#endif /* CONFIG_DAEMON */
						"    --version           Display version.\n"
						"    --help              Display this help screen.\n\n"
						PACKAGE_NAME " home page: <" PACKAGE_URL ">\n"
						"Report " PACKAGE_NAME
						" bugs to <" PACKAGE_BUGREPORT ">\n");
				exit(EXIT_FAILURE);
				break;
		}
	}

	/* Set default option if needed */
	if(interface == NULL) {
		if((interface = malloc(strlen(DEFAULT_INTERFACE) + 1)) == NULL) {
			eerror("Failed to allocate interface");
			exit(EXIT_FAILURE);
		}
		strcpy(interface, DEFAULT_INTERFACE);
	}

	if(service == NULL) {
		if((service = malloc(strlen(DEFAULT_PORT) + 1)) == NULL) {
			eerror("Failed to allocate service");
			exit(EXIT_FAILURE);
		}
		strcpy(service, DEFAULT_PORT);
	}

#ifdef CONFIG_DAEMON
	if(global.pidfile == NULL) {
		if((global.pidfile = malloc(strlen(DEFAULT_PIDFILE) + 1)) == NULL) {
			eerror("Failed to allocate pidfile");
			exit(EXIT_FAILURE);
		}
		strcpy(global.pidfile, DEFAULT_PIDFILE);
	}

	/* The --kill option have been used */
	if(killdaemon == 1) {
		if(havepid(global.pidfile) < 0)
			exit(EXIT_SUCCESS);

		if((pid = readpid(global.pidfile)) == 0) {
			critical("Failed to read pid file");
			exit(EXIT_FAILURE);
		}

		if(kill(pid, 0) < 0) {
			ewarning("Failed to check process");
			if(deletepid(global.pidfile) < 0) {
				critical("Failed to delete the pid file");
				exit(EXIT_FAILURE);
			}
			exit(EXIT_SUCCESS);
		}

		if(kill(pid, SIGTERM) < 0) {
			eerror("Failed to send SIGTERM");
			info("Waiting one second before SIGKILL");
			sleep(1);
			if(kill(pid, SIGKILL) < 0) {
				ecritical("Failed to send SIGKILL");
				exit(EXIT_FAILURE);
			}
		}
		exit(EXIT_SUCCESS);
	}

	/* Check if a daemon is not already started */
	if(havepid(global.pidfile) == 0) {
		if((pid = readpid(global.pidfile)) == 0) {
			critical("Failed to read pid file");
			exit(EXIT_FAILURE);
		}

		if(kill(pid, 0) == 0) {
			info("The daemon is already started");
			exit(EXIT_SUCCESS);
		}

		if(deletepid(global.pidfile) < 0) {
			critical("Failed to delete the pid file");
			exit(EXIT_FAILURE);
		}
	}

	/* Become a daemon */
	if(daemonize() < 0) {
		critical("Failed to daemonize");
		exit(EXIT_FAILURE);
	}

	/* Create the pid file */
	if(createpid(global.pidfile) < 0) {
		critical("Failed to create pid file");
		exit(EXIT_FAILURE);
	}
#endif /* CONFIG_DAEMON */

	/* Handle SIGTERM */
	if(signal(SIGTERM, quit) == SIG_ERR) {
		ecritical("Failed to handle SIGTERM");
		exit(EXIT_FAILURE);
	}

	notice("Start %s", PACKAGE_STRING);

	/* Get listener */
	if((global.listener = getintflistener(interface, service, AF_UNSPEC)) < 0) {
		critical("Failed to get listener");
		exit(EXIT_FAILURE);
	}

	free(interface);
	free(service);

	/* The big loop */
	global.running = 1;
	while(global.running == 1) {
		if((client = accept(global.listener, (struct sockaddr *)&clientaddr,
						&clientsize)) < 0) {
			if(global.running == 0)
				break;
			eerror("Failed to accept client");
			continue;
		}

#ifdef CONFIG_DEBUG
		inet_ntop(clientaddr.ss_family,
				(clientaddr.ss_family == AF_INET) ?
				(void*)&(((struct sockaddr_in*)&clientaddr)->sin_addr) :
				(void*)&(((struct sockaddr_in6*)&clientaddr)->sin6_addr),
				clientstr, sizeof(clientstr));
		debug("Got a new connection from: %s", clientstr);
#endif /* CONFIG_DEBUG */

		if((request = (request_t*)recv_packet(client)) == NULL) {
			close(client);
			error("Failed to receive request");
			continue;
		}

		/* Prepare response */
		if((response = (response_t*)prepare_response(SUCCESS, request->data,
						strlen(request->data))) == NULL) {
			free(request);
			close(client);
			eerror("Failed to prepare response");
			continue;
		}

		free(request);

		if(send_packet(client, (packet_t*)response) < 0) {
			close(client);
			error("Failed to send response");
			continue;
		}

		close(client);
	}

	exit(EXIT_SUCCESS);
}
/************************* Proces principal *************************/
int main ()
{
// Variables per a recordar el pid de principal i hores. Tambe minuts, per a tenir-ho controlat.
	int pidP;
	int pidH;
	int mm;

// Variables per a rebre interrupcions de sortida de proces.
	sigset_t mask, oldmask;

// Inicialitzant whilemain per continuar adequadament.
	whilemain = 1;

// Diem quins senyals volem fer cas:
	signal ( SIGCONT, minut		);
	signal ( SIGTERM, killing	);

// Escrivim el nostre pid, si hi ha un problema, ho diem i acabem.
	if ( writepid ( "minutos.pid" ) ) return 1;


// Espera a rebre nova senyal. Suposarem que si s'ha enviat una senyal abans, no i farem cas.
// El usuari ho podra saber perque no ha aparegut per pantalla el "Llegint el pid dels demes".
// I normalment, aixo no pasara, ja que ha d'inicialitzar tots els altres procesos.
pause ();

// Ara toca llegir de principal i hores, si hi ha un problema, ho diem i acabem.
	if ( readpid ( "principal.pid",	&pidP, "minutos" ) ) return 1;
	if ( readpid ( "horas.pid",	&pidH, "minutos" ) ) return 1;


// Inicialitzem les variables abans d'entrar dins el while.
	mm = 0;

// Ajustant l'espera a senyals.
	sigemptyset ( &mask );
	sigaddset ( &mask, SIGTERM );

// Ara fara que nomes rebra la senyal de SIGTERM quan estigui dins de 'sigsuspend'.
	sigprocmask ( SIG_BLOCK, &mask, &oldmask );

// Comenza el programa en si
	while ( whilemain )
	{
// Es un pause, menys per SIGTERM, que nomes el rebra quan la crida sigui feta.
		sigsuspend ( &oldmask );

// Nomes envia un sol senyal per minut.
// Aixo es degut a haver-me trobat errors:
//	- 0:59:59 >> 0:60: 0
//	- 0:59:59 >> 1: 1: 0
// Per evitar aquests dos possibles errors, nomes enviem una senyal.
		if ( ++mm == 60 )
		{
			mm = 0;
			kill ( pidH, SIGCONT );
		} else
			kill ( pidP, SIGUSR2 );
	}
return 0;
}