-/* dnsmasq is Copyright (c) 2000-2007 Simon Kelley
+/* dnsmasq is Copyright (c) 2000-2011 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "dnsmasq.h"
#ifdef NO_FORK
"no-MMU "
#endif
-#ifndef HAVE_ISC_READER
-"no-"
-#endif
-"ISC-leasefile "
#ifndef HAVE_DBUS
"no-"
#endif
"no-"
#endif
"I18N "
+#ifndef HAVE_DHCP
+"no-"
+#endif
+"DHCP "
+#if defined(HAVE_DHCP) && !defined(HAVE_SCRIPT)
+"no-scripts "
+#endif
#ifndef HAVE_TFTP
"no-"
#endif
"TFTP";
+
+
static volatile pid_t pid = 0;
static volatile int pipewrite;
static void check_dns_listeners(fd_set *set, time_t now);
static void sig_handler(int sig);
static void async_event(int pipe, time_t now);
-static void poll_resolv(void);
+static void fatal_event(struct event_desc *ev);
int main (int argc, char **argv)
{
int bind_fallback = 0;
- int bad_capabilities = 0;
- time_t now, last = 0;
+ time_t now;
struct sigaction sigact;
struct iname *if_tmp;
- int piperead, pipefd[2];
- struct passwd *ent_pw;
+ int piperead, pipefd[2], err_pipe[2];
+ struct passwd *ent_pw = NULL;
+#if defined(HAVE_DHCP) && defined(HAVE_SCRIPT)
+ uid_t script_uid = 0;
+ gid_t script_gid = 0;
+#endif
+ struct group *gp = NULL;
long i, max_fd = sysconf(_SC_OPEN_MAX);
+ char *baduser = NULL;
+ int log_err;
+#if defined(HAVE_LINUX_NETWORK)
+ cap_user_header_t hdr = NULL;
+ cap_user_data_t data = NULL;
+#endif
#ifdef LOCALEDIR
setlocale(LC_ALL, "");
daemon->packet_buff_sz = daemon->edns_pktsz > DNSMASQ_PACKETSZ ?
daemon->edns_pktsz : DNSMASQ_PACKETSZ;
daemon->packet = safe_malloc(daemon->packet_buff_sz);
-
+
+#ifdef HAVE_DHCP
if (!daemon->lease_file)
{
if (daemon->dhcp)
daemon->lease_file = LEASEFILE;
}
-#ifndef HAVE_ISC_READER
- else if (!daemon->dhcp)
- die(_("ISC dhcpd integration not available: set HAVE_ISC_READER in src/config.h"), NULL, EC_BADCONF);
#endif
/* Close any file descriptors we inherited apart from std{in|out|err} */
#elif !(defined(IP_RECVDSTADDR) && \
defined(IP_RECVIF) && \
defined(IP_SENDSRCADDR))
- if (!(daemon->options & OPT_NOWILD))
+ if (!option_bool(OPT_NOWILD))
{
bind_fallback = 1;
- daemon->options |= OPT_NOWILD;
+ set_option_bool(OPT_NOWILD);
}
#endif
#ifndef HAVE_TFTP
- if (daemon->options & OPT_TFTP)
+ if (daemon->tftp_unlimited || daemon->tftp_interfaces)
die(_("TFTP server not available: set HAVE_TFTP in src/config.h"), NULL, EC_BADCONF);
#endif
die(_("asychronous logging is not available under Solaris"), NULL, EC_BADCONF);
#endif
+ rand_init();
+
now = dnsmasq_time();
+#ifdef HAVE_DHCP
if (daemon->dhcp)
{
-#if !defined(HAVE_LINUX_NETWORK) && !defined(IP_RECVIF)
- int c;
- struct iname *tmp;
- for (c = 0, tmp = daemon->if_names; tmp; tmp = tmp->next)
- if (!tmp->isloop)
- c++;
- if (c != 1)
- die(_("must set exactly one interface on broken systems without IP_RECVIF"), NULL, EC_BADCONF);
-#endif
/* Note that order matters here, we must call lease_init before
creating any file descriptors which shouldn't be leaked
to the lease-script init process. */
lease_init(now);
dhcp_init();
}
+#endif
if (!enumerate_interfaces())
die(_("failed to find list of interfaces: %s"), NULL, EC_MISC);
- if (daemon->options & OPT_NOWILD)
+ if (option_bool(OPT_NOWILD))
{
daemon->listeners = create_bound_listeners();
die(_("no interface with address %s"), daemon->namebuff, EC_BADNET);
}
}
- else if ((daemon->port != 0 || (daemon->options & OPT_TFTP)) &&
- !(daemon->listeners = create_wildcard_listeners()))
- die(_("failed to create listening socket: %s"), NULL, EC_BADNET);
+ else
+ daemon->listeners = create_wildcard_listeners();
if (daemon->port != 0)
cache_init();
-
- if (daemon->options & OPT_DBUS)
+
+ if (option_bool(OPT_DBUS))
#ifdef HAVE_DBUS
{
char *err;
if (daemon->port != 0)
pre_allocate_sfds();
+
+#if defined(HAVE_DHCP) && defined(HAVE_SCRIPT)
+ /* Note getpwnam returns static storage */
+ if (daemon->dhcp && daemon->lease_change_command && daemon->scriptuser)
+ {
+ if ((ent_pw = getpwnam(daemon->scriptuser)))
+ {
+ script_uid = ent_pw->pw_uid;
+ script_gid = ent_pw->pw_gid;
+ }
+ else
+ baduser = daemon->scriptuser;
+ }
+#endif
+ if (daemon->username && !(ent_pw = getpwnam(daemon->username)))
+ baduser = daemon->username;
+ else if (daemon->groupname && !(gp = getgrnam(daemon->groupname)))
+ baduser = daemon->groupname;
+
+ if (baduser)
+ die(_("unknown user or group: %s"), baduser, EC_BADCONF);
+
+ /* implement group defaults, "dip" if available, or group associated with uid */
+ if (!daemon->group_set && !gp)
+ {
+ if (!(gp = getgrnam(CHGRP)) && ent_pw)
+ gp = getgrgid(ent_pw->pw_gid);
+
+ /* for error message */
+ if (gp)
+ daemon->groupname = gp->gr_name;
+ }
+
+#if defined(HAVE_LINUX_NETWORK)
+ /* determine capability API version here, while we can still
+ call safe_malloc */
+ if (ent_pw && ent_pw->pw_uid != 0)
+ {
+ int capsize = 1; /* for header version 1 */
+ hdr = safe_malloc(sizeof(*hdr));
+
+ /* find version supported by kernel */
+ memset(hdr, 0, sizeof(*hdr));
+ capget(hdr, NULL);
+
+ if (hdr->version != LINUX_CAPABILITY_VERSION_1)
+ {
+ /* if unknown version, use largest supported version (3) */
+ if (hdr->version != LINUX_CAPABILITY_VERSION_2)
+ hdr->version = LINUX_CAPABILITY_VERSION_3;
+ capsize = 2;
+ }
+
+ data = safe_malloc(sizeof(*data) * capsize);
+ memset(data, 0, sizeof(*data) * capsize);
+ }
+#endif
+
/* Use a pipe to carry signals and other events back to the event loop
- in a race-free manner */
- if (pipe(pipefd) == -1 || !fix_fd(pipefd[0]) || !fix_fd(pipefd[1]))
- die(_("cannot create pipe: %s"), NULL, EC_MISC);
+ in a race-free manner and another to carry errors to daemon-invoking process */
+ safe_pipe(pipefd, 1);
piperead = pipefd[0];
pipewrite = pipefd[1];
/* prime the pipe to load stuff first time. */
send_event(pipewrite, EVENT_RELOAD, 0);
+
+ err_pipe[1] = -1;
- if (!(daemon->options & OPT_DEBUG))
+ if (!option_bool(OPT_DEBUG))
{
- FILE *pidfile;
- int nullfd;
-
/* The following code "daemonizes" the process.
See Stevens section 12.4 */
+
+ if (chdir("/") != 0)
+ die(_("cannot chdir to filesystem root: %s"), NULL, EC_MISC);
#ifndef NO_FORK
- if (!(daemon->options & OPT_NO_FORK))
+ if (!option_bool(OPT_NO_FORK))
{
pid_t pid;
- if ((pid = fork()) == -1 )
- die(_("cannot fork into background: %s"), NULL, EC_MISC);
+ /* pipe to carry errors back to original process.
+ When startup is complete we close this and the process terminates. */
+ safe_pipe(err_pipe, 0);
+ if ((pid = fork()) == -1)
+ /* fd == -1 since we've not forked, never returns. */
+ send_event(-1, EVENT_FORK_ERR, errno);
+
if (pid != 0)
- _exit(EC_GOOD);
+ {
+ struct event_desc ev;
+
+ /* close our copy of write-end */
+ close(err_pipe[1]);
+
+ /* check for errors after the fork */
+ if (read_write(err_pipe[0], (unsigned char *)&ev, sizeof(ev), 1))
+ fatal_event(&ev);
+
+ _exit(EC_GOOD);
+ }
- setsid();
- pid = fork();
+ close(err_pipe[0]);
- if (pid != 0 && pid != -1)
+ /* NO calls to die() from here on. */
+
+ setsid();
+
+ if ((pid = fork()) == -1)
+ send_event(err_pipe[1], EVENT_FORK_ERR, errno);
+
+ if (pid != 0)
_exit(0);
}
#endif
-
- if (chdir("/") != 0)
- die(_("cannot chdir to filesystem root: %s"), NULL, EC_MISC);
-
+
/* write pidfile _after_ forking ! */
- if (daemon->runfile && (pidfile = fopen(daemon->runfile, "w")))
- {
- fprintf(pidfile, "%d\n", (int) getpid());
- fclose(pidfile);
+ if (daemon->runfile)
+ {
+ FILE *pidfile;
+
+ /* only complain if started as root */
+ if ((pidfile = fopen(daemon->runfile, "w")))
+ {
+ fprintf(pidfile, "%d\n", (int) getpid());
+ fclose(pidfile);
+ }
+ else if (getuid() == 0)
+ {
+ send_event(err_pipe[1], EVENT_PIDFILE, errno);
+ _exit(0);
+ }
}
-
- /* open stdout etc to /dev/null */
- nullfd = open("/dev/null", O_RDWR);
- dup2(nullfd, STDOUT_FILENO);
- dup2(nullfd, STDERR_FILENO);
- dup2(nullfd, STDIN_FILENO);
- close(nullfd);
}
- /* if we are to run scripts, we need to fork a helper before dropping root. */
-#ifndef NO_FORK
- daemon->helperfd = create_helper(pipewrite, max_fd);
-#endif
+ log_err = log_start(ent_pw, err_pipe[1]);
+
+ if (!option_bool(OPT_DEBUG))
+ {
+ /* open stdout etc to /dev/null */
+ int nullfd = open("/dev/null", O_RDWR);
+ dup2(nullfd, STDOUT_FILENO);
+ dup2(nullfd, STDERR_FILENO);
+ dup2(nullfd, STDIN_FILENO);
+ close(nullfd);
+ }
- ent_pw = daemon->username ? getpwnam(daemon->username) : NULL;
-
- /* before here, we should only call die(), after here, only call syslog() */
- log_start(ent_pw);
+ /* if we are to run scripts, we need to fork a helper before dropping root. */
+ daemon->helperfd = -1;
+#if defined(HAVE_DHCP) && defined(HAVE_SCRIPT)
+ if (daemon->dhcp && daemon->lease_change_command)
+ daemon->helperfd = create_helper(pipewrite, err_pipe[1], script_uid, script_gid, max_fd);
+#endif
- if (!(daemon->options & OPT_DEBUG))
+ if (!option_bool(OPT_DEBUG) && getuid() == 0)
{
- /* UID changing, etc */
- if (daemon->groupname || ent_pw)
+ int bad_capabilities = 0;
+ gid_t dummy;
+
+ /* remove all supplimentary groups */
+ if (gp &&
+ (setgroups(0, &dummy) == -1 ||
+ setgid(gp->gr_gid) == -1))
{
- gid_t dummy;
- struct group *gp;
-
- /* change group for /etc/ppp/resolv.conf otherwise get the group for "nobody" */
- if ((daemon->groupname && (gp = getgrnam(daemon->groupname))) ||
- (ent_pw && (gp = getgrgid(ent_pw->pw_gid))))
- {
- /* remove all supplimentary groups */
- setgroups(0, &dummy);
- setgid(gp->gr_gid);
- }
+ send_event(err_pipe[1], EVENT_GROUP_ERR, errno);
+ _exit(0);
}
-
+
if (ent_pw && ent_pw->pw_uid != 0)
{
#if defined(HAVE_LINUX_NETWORK)
/* On linux, we keep CAP_NETADMIN (for ARP-injection) and
CAP_NET_RAW (for icmp) if we're doing dhcp */
- cap_user_header_t hdr = safe_malloc(sizeof(*hdr));
- cap_user_data_t data = safe_malloc(sizeof(*data));
- hdr->version = _LINUX_CAPABILITY_VERSION;
- hdr->pid = 0; /* this process */
data->effective = data->permitted = data->inheritable =
- (1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW) |
- (1 << CAP_SETGID) | (1 << CAP_SETUID);
+ (1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW) | (1 << CAP_SETUID);
/* Tell kernel to not clear capabilities when dropping root */
if (capset(hdr, data) == -1 || prctl(PR_SET_KEEPCAPS, 1) == -1)
bad_capabilities = errno;
-
-#elif defined(HAVE_SOLARIS_PRIVS)
+
+#elif defined(HAVE_SOLARIS_NETWORK)
/* http://developers.sun.com/solaris/articles/program_privileges.html */
priv_set_t *priv_set;
if (priv_set)
priv_freeset(priv_set);
-#elif defined(HAVE_SOLARIS_NETWORK)
-
- bad_capabilities = ENOTSUP;
#endif
- if (bad_capabilities == 0)
+ if (bad_capabilities != 0)
{
- /* finally drop root */
- setuid(ent_pw->pw_uid);
-
+ send_event(err_pipe[1], EVENT_CAP_ERR, bad_capabilities);
+ _exit(0);
+ }
+
+ /* finally drop root */
+ if (setuid(ent_pw->pw_uid) == -1)
+ {
+ send_event(err_pipe[1], EVENT_USER_ERR, errno);
+ _exit(0);
+ }
+
#ifdef HAVE_LINUX_NETWORK
- data->effective = data->permitted =
- (1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW);
- data->inheritable = 0;
-
- /* lose the setuid and setgid capbilities */
- capset(hdr, data);
-#endif
+ data->effective = data->permitted =
+ (1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW);
+ data->inheritable = 0;
+
+ /* lose the setuid and setgid capbilities */
+ if (capset(hdr, data) == -1)
+ {
+ send_event(err_pipe[1], EVENT_CAP_ERR, errno);
+ _exit(0);
}
+#endif
+
}
}
#ifdef HAVE_LINUX_NETWORK
- if (daemon->options & OPT_DEBUG)
+ if (option_bool(OPT_DEBUG))
prctl(PR_SET_DUMPABLE, 1);
#endif
my_syslog(LOG_INFO, _("compile time options: %s"), compile_opts);
#ifdef HAVE_DBUS
- if (daemon->options & OPT_DBUS)
+ if (option_bool(OPT_DBUS))
{
if (daemon->dbus)
my_syslog(LOG_INFO, _("DBus support enabled: connected to system bus"));
}
#endif
+ if (log_err != 0)
+ my_syslog(LOG_WARNING, _("warning: failed to change owner of %s: %s"),
+ daemon->log_file, strerror(log_err));
+
if (bind_fallback)
my_syslog(LOG_WARNING, _("setting --bind-interfaces option because of OS limitations"));
- if (!(daemon->options & OPT_NOWILD))
+ if (!option_bool(OPT_NOWILD))
for (if_tmp = daemon->if_names; if_tmp; if_tmp = if_tmp->next)
if (if_tmp->name && !if_tmp->used)
my_syslog(LOG_WARNING, _("warning: interface %s does not currently exist"), if_tmp->name);
- if (daemon->port != 0 && (daemon->options & OPT_NO_RESOLV))
+ if (daemon->port != 0 && option_bool(OPT_NO_RESOLV))
{
if (daemon->resolv_files && !daemon->resolv_files->is_default)
my_syslog(LOG_WARNING, _("warning: ignoring resolv-file flag because no-resolv is set"));
if (daemon->max_logs != 0)
my_syslog(LOG_INFO, _("asynchronous logging enabled, queue limit is %d messages"), daemon->max_logs);
+#ifdef HAVE_DHCP
if (daemon->dhcp)
{
struct dhcp_context *dhcp_tmp;
{
prettyprint_time(daemon->dhcp_buff2, dhcp_tmp->lease_time);
strcpy(daemon->dhcp_buff, inet_ntoa(dhcp_tmp->start));
- my_syslog(LOG_INFO,
+ my_syslog(MS_DHCP | LOG_INFO,
(dhcp_tmp->flags & CONTEXT_STATIC) ?
_("DHCP, static leases only on %.0s%s, lease time %s") :
+ (dhcp_tmp->flags & CONTEXT_PROXY) ?
+ _("DHCP, proxy on subnet %.0s%s%.0s") :
_("DHCP, IP range %s -- %s, lease time %s"),
daemon->dhcp_buff, inet_ntoa(dhcp_tmp->end), daemon->dhcp_buff2);
}
}
+#endif
#ifdef HAVE_TFTP
- if (daemon->options & OPT_TFTP)
+ if (daemon->tftp_unlimited || daemon->tftp_interfaces)
{
#ifdef FD_SETSIZE
if (FD_SETSIZE < (unsigned)max_fd)
max_fd = FD_SETSIZE;
#endif
- my_syslog(LOG_INFO, "TFTP %s%s %s",
+ my_syslog(MS_TFTP | LOG_INFO, "TFTP %s%s %s",
daemon->tftp_prefix ? _("root is ") : _("enabled"),
daemon->tftp_prefix ? daemon->tftp_prefix: "",
- daemon->options & OPT_TFTP_SECURE ? _("secure mode") : "");
+ option_bool(OPT_TFTP_SECURE) ? _("secure mode") : "");
/* This is a guess, it assumes that for small limits,
disjoint files might be served, but for large limits,
if (daemon->tftp_max > max_fd)
{
daemon->tftp_max = max_fd;
- my_syslog(LOG_WARNING,
+ my_syslog(MS_TFTP | LOG_WARNING,
_("restricting maximum simultaneous TFTP transfers to %d"),
daemon->tftp_max);
}
}
#endif
- if (!(daemon->options & OPT_DEBUG) && (getuid() == 0 || geteuid() == 0))
- {
- if (bad_capabilities)
- my_syslog(LOG_WARNING, _("warning: setting capabilities failed: %s"), strerror(bad_capabilities));
-
- my_syslog(LOG_WARNING, _("running as root"));
- }
+ /* finished start-up - release original process */
+ if (err_pipe[1] != -1)
+ close(err_pipe[1]);
if (daemon->port != 0)
check_servers();
/* Whilst polling for the dbus, or doing a tftp transfer, wake every quarter second */
if (daemon->tftp_trans ||
- ((daemon->options & OPT_DBUS) && !daemon->dbus))
+ (option_bool(OPT_DBUS) && !daemon->dbus))
{
t.tv_sec = 0;
t.tv_usec = 250000;
set_dbus_listeners(&maxfd, &rset, &wset, &eset);
#endif
+#ifdef HAVE_DHCP
if (daemon->dhcp)
{
FD_SET(daemon->dhcpfd, &rset);
bump_maxfd(daemon->dhcpfd, &maxfd);
+ if (daemon->pxefd != -1)
+ {
+ FD_SET(daemon->pxefd, &rset);
+ bump_maxfd(daemon->pxefd, &maxfd);
+ }
}
+#endif
#ifdef HAVE_LINUX_NETWORK
FD_SET(daemon->netlinkfd, &rset);
FD_SET(piperead, &rset);
bump_maxfd(piperead, &maxfd);
-#ifndef NO_FORK
+#ifdef HAVE_DHCP
+# ifdef HAVE_SCRIPT
while (helper_buf_empty() && do_script_run(now));
if (!helper_buf_empty())
FD_SET(daemon->helperfd, &wset);
bump_maxfd(daemon->helperfd, &maxfd);
}
-#else
+# else
/* need this for other side-effects */
while (do_script_run(now));
+# endif
#endif
-
+
/* must do this just before select(), when we know no
more calls to my_syslog() can occur */
set_log_writer(&wset, &maxfd);
check_log_writer(&wset);
+#ifdef HAVE_LINUX_NETWORK
+ if (FD_ISSET(daemon->netlinkfd, &rset))
+ netlink_multicast();
+#endif
+
/* Check for changes to resolv files once per second max. */
/* Don't go silent for long periods if the clock goes backwards. */
- if (last == 0 || difftime(now, last) > 1.0 || difftime(now, last) < -1.0)
+ if (daemon->last_resolv == 0 ||
+ difftime(now, daemon->last_resolv) > 1.0 ||
+ difftime(now, daemon->last_resolv) < -1.0)
{
- last = now;
-
-#ifdef HAVE_ISC_READER
- if (daemon->lease_file && !daemon->dhcp)
- load_dhcp(now);
-#endif
+ /* poll_resolv doesn't need to reload first time through, since
+ that's queued anyway. */
- if (daemon->port != 0 && !(daemon->options & OPT_NO_POLL))
- poll_resolv();
+ poll_resolv(0, daemon->last_resolv != 0, now);
+ daemon->last_resolv = now;
}
if (FD_ISSET(piperead, &rset))
async_event(piperead, now);
-#ifdef HAVE_LINUX_NETWORK
- if (FD_ISSET(daemon->netlinkfd, &rset))
- netlink_multicast();
-#endif
-
#ifdef HAVE_DBUS
/* if we didn't create a DBus connection, retry now. */
- if ((daemon->options & OPT_DBUS) && !daemon->dbus)
+ if (option_bool(OPT_DBUS) && !daemon->dbus)
{
char *err;
if ((err = dbus_init()))
check_tftp_listeners(&rset, now);
#endif
- if (daemon->dhcp && FD_ISSET(daemon->dhcpfd, &rset))
- dhcp_packet(now);
+#ifdef HAVE_DHCP
+ if (daemon->dhcp)
+ {
+ if (FD_ISSET(daemon->dhcpfd, &rset))
+ dhcp_packet(now, 0);
+ if (daemon->pxefd != -1 && FD_ISSET(daemon->pxefd, &rset))
+ dhcp_packet(now, 1);
+ }
-#ifndef NO_FORK
+# ifdef HAVE_SCRIPT
if (daemon->helperfd != -1 && FD_ISSET(daemon->helperfd, &wset))
helper_write();
+# endif
#endif
}
ev.event = event;
ev.data = data;
- /* pipe is non-blocking and struct event_desc is smaller than
- PIPE_BUF, so this either fails or writes everything */
- while (write(fd, &ev, sizeof(ev)) == -1 && errno == EINTR);
+
+ /* error pipe, debug mode. */
+ if (fd == -1)
+ fatal_event(&ev);
+ else
+ /* pipe is non-blocking and struct event_desc is smaller than
+ PIPE_BUF, so this either fails or writes everything */
+ while (write(fd, &ev, sizeof(ev)) == -1 && errno == EINTR);
}
+static void fatal_event(struct event_desc *ev)
+{
+ errno = ev->data;
+
+ switch (ev->event)
+ {
+ case EVENT_DIE:
+ exit(0);
+
+ case EVENT_FORK_ERR:
+ die(_("cannot fork into background: %s"), NULL, EC_MISC);
+
+ case EVENT_PIPE_ERR:
+ die(_("failed to create helper: %s"), NULL, EC_MISC);
+
+ case EVENT_CAP_ERR:
+ die(_("setting capabilities failed: %s"), NULL, EC_MISC);
+
+ case EVENT_USER_ERR:
+ case EVENT_HUSER_ERR:
+ die(_("failed to change user-id to %s: %s"),
+ ev->event == EVENT_USER_ERR ? daemon->username : daemon->scriptuser,
+ EC_MISC);
+
+ case EVENT_GROUP_ERR:
+ die(_("failed to change group-id to %s: %s"), daemon->groupname, EC_MISC);
+
+ case EVENT_PIDFILE:
+ die(_("failed to open pidfile %s: %s"), daemon->runfile, EC_FILE);
+
+ case EVENT_LOG_ERR:
+ die(_("cannot open %s: %s"), daemon->log_file ? daemon->log_file : "log", EC_FILE);
+ }
+}
+
static void async_event(int pipe, time_t now)
{
pid_t p;
{
case EVENT_RELOAD:
clear_cache_and_reload(now);
- if (daemon->port != 0 && daemon->resolv_files && (daemon->options & OPT_NO_POLL))
+ if (daemon->port != 0 && daemon->resolv_files && option_bool(OPT_NO_POLL))
{
reload_servers(daemon->resolv_files->name);
check_servers();
}
+#ifdef HAVE_DHCP
rerun_scripts();
+#endif
break;
case EVENT_DUMP:
break;
case EVENT_ALARM:
+#ifdef HAVE_DHCP
if (daemon->dhcp)
{
lease_prune(NULL, now);
lease_update_file(now);
}
+#endif
break;
case EVENT_CHILD:
break;
case EVENT_EXEC_ERR:
- my_syslog(LOG_ERR, _("failed to execute %s: %s"), daemon->lease_change_command, strerror(ev.data));
+ my_syslog(LOG_ERR, _("failed to execute %s: %s"),
+ daemon->lease_change_command, strerror(ev.data));
break;
- case EVENT_PIPE_ERR:
- my_syslog(LOG_ERR, _("failed to create helper: %s"), strerror(ev.data));
+ /* necessary for fatal errors in helper */
+ case EVENT_HUSER_ERR:
+ case EVENT_DIE:
+ fatal_event(&ev);
break;
case EVENT_REOPEN:
if (daemon->tcp_pids[i] != 0)
kill(daemon->tcp_pids[i], SIGALRM);
-#ifndef NO_FORK
+#if defined(HAVE_DHCP) && defined(HAVE_SCRIPT)
/* handle pending lease transitions */
if (daemon->helperfd != -1)
{
if (daemon->lease_stream)
fclose(daemon->lease_stream);
+
+ if (daemon->runfile)
+ unlink(daemon->runfile);
my_syslog(LOG_INFO, _("exiting on receipt of SIGTERM"));
flush_log();
}
}
-static void poll_resolv()
+void poll_resolv(int force, int do_reload, time_t now)
{
struct resolvc *res, *latest;
struct stat statbuf;
/* There may be more than one possible file.
Go through and find the one which changed _last_.
Warn of any which can't be read. */
+
+ if (daemon->port == 0 || option_bool(OPT_NO_POLL))
+ return;
+
for (latest = NULL, res = daemon->resolv_files; res; res = res->next)
if (stat(res->name, &statbuf) == -1)
{
+ if (force)
+ {
+ res->mtime = 0;
+ continue;
+ }
+
if (!res->logged)
my_syslog(LOG_WARNING, _("failed to access %s: %s"), res->name, strerror(errno));
res->logged = 1;
+
+ if (res->mtime != 0)
+ {
+ /* existing file evaporated, force selection of the latest
+ file even if its mtime hasn't changed since we last looked */
+ poll_resolv(1, do_reload, now);
+ return;
+ }
}
else
{
res->logged = 0;
- if (statbuf.st_mtime != res->mtime)
- {
- res->mtime = statbuf.st_mtime;
+ if (force || (statbuf.st_mtime != res->mtime))
+ {
+ res->mtime = statbuf.st_mtime;
if (difftime(statbuf.st_mtime, last_change) > 0.0)
{
last_change = statbuf.st_mtime;
my_syslog(LOG_INFO, _("reading %s"), latest->name);
warned = 0;
check_servers();
- if (daemon->options & OPT_RELOAD)
- cache_reload(daemon->options, daemon->namebuff, daemon->domain_suffix, daemon->addn_hosts);
+ if (option_bool(OPT_RELOAD) && do_reload)
+ clear_cache_and_reload(now);
}
else
{
void clear_cache_and_reload(time_t now)
{
if (daemon->port != 0)
- cache_reload(daemon->options, daemon->namebuff, daemon->domain_suffix, daemon->addn_hosts);
+ cache_reload();
+#ifdef HAVE_DHCP
if (daemon->dhcp)
{
- if (daemon->options & OPT_ETHERS)
+ if (option_bool(OPT_ETHERS))
dhcp_read_ethers();
reread_dhcp();
dhcp_update_configs(daemon->dhcp_conf);
lease_update_file(now);
lease_update_dns();
}
+#endif
}
static int set_dns_listeners(time_t now, fd_set *set, int *maxfdp)
FD_SET(serverfdp->fd, set);
bump_maxfd(serverfdp->fd, maxfdp);
}
-
+
+ if (daemon->port != 0 && !daemon->osport)
+ for (i = 0; i < RANDOM_SOCKS; i++)
+ if (daemon->randomsocks[i].refcount != 0)
+ {
+ FD_SET(daemon->randomsocks[i].fd, set);
+ bump_maxfd(daemon->randomsocks[i].fd, maxfdp);
+ }
+
for (listener = daemon->listeners; listener; listener = listener->next)
{
/* only listen for queries if we have resources */
static void check_dns_listeners(fd_set *set, time_t now)
{
struct serverfd *serverfdp;
- struct listener *listener;
-
+ struct listener *listener;
+ int i;
+
for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next)
if (FD_ISSET(serverfdp->fd, set))
- reply_query(serverfdp, now);
+ reply_query(serverfdp->fd, serverfdp->source_addr.sa.sa_family, now);
+
+ if (daemon->port != 0 && !daemon->osport)
+ for (i = 0; i < RANDOM_SOCKS; i++)
+ if (daemon->randomsocks[i].refcount != 0 &&
+ FD_ISSET(daemon->randomsocks[i].fd, set))
+ reply_query(daemon->randomsocks[i].fd, daemon->randomsocks[i].family, now);
for (listener = daemon->listeners; listener; listener = listener->next)
{
if (listener->fd != -1 && FD_ISSET(listener->fd, set))
receive_query(listener, now);
-
+
#ifdef HAVE_TFTP
if (listener->tftpfd != -1 && FD_ISSET(listener->tftpfd, set))
tftp_request(listener, now);
if (confd == -1)
continue;
- if (daemon->options & OPT_NOWILD)
+ if (option_bool(OPT_NOWILD))
iface = listener->iface;
else
{
close(confd);
}
#ifndef NO_FORK
- else if (!(daemon->options & OPT_DEBUG) && (p = fork()) != 0)
+ else if (!option_bool(OPT_DEBUG) && (p = fork()) != 0)
{
if (p != -1)
{
dst_addr_4.s_addr = 0;
- /* Arrange for SIGALARM after CHILD_LIFETIME seconds to
- terminate the process. */
- if (!(daemon->options & OPT_DEBUG))
+#ifndef NO_FORK
+ /* Arrange for SIGALARM after CHILD_LIFETIME seconds to
+ terminate the process. */
+ if (!option_bool(OPT_DEBUG))
alarm(CHILD_LIFETIME);
-
+#endif
+
/* start with no upstream connections. */
for (s = daemon->servers; s; s = s->next)
s->tcpfd = -1;
close(s->tcpfd);
}
#ifndef NO_FORK
- if (!(daemon->options & OPT_DEBUG))
+ if (!option_bool(OPT_DEBUG))
{
flush_log();
_exit(0);
}
}
-
+#ifdef HAVE_DHCP
int make_icmp_sock(void)
{
int fd;
return gotreply;
}
+#endif