Commit 5f4dc5c6 authored by Simon Kelley's avatar Simon Kelley

Add --dhcp-hostsdir config option.

parent 2ae195f5
...@@ -54,6 +54,11 @@ version 2.73 ...@@ -54,6 +54,11 @@ version 2.73
address. (IPv6 addresses are scoped, so this is allowed.) address. (IPv6 addresses are scoped, so this is allowed.)
Thanks to Cory Benfield for help with this. Thanks to Cory Benfield for help with this.
Add --dhcp-hostsdir. This allows addition of new host
configurations to a running dnsmasq instance much more
cheaply than having dnsmasq re-read all its existing
configuration each time.
version 2.72 version 2.72
Add ra-advrouter mode, for RFC-3775 mobile IPv6 support. Add ra-advrouter mode, for RFC-3775 mobile IPv6 support.
......
...@@ -977,6 +977,15 @@ is given, then read all the files contained in that directory. The advantage of ...@@ -977,6 +977,15 @@ is given, then read all the files contained in that directory. The advantage of
using this option is the same as for --dhcp-hostsfile: the using this option is the same as for --dhcp-hostsfile: the
dhcp-optsfile will be re-read when dnsmasq receives SIGHUP. Note that dhcp-optsfile will be re-read when dnsmasq receives SIGHUP. Note that
it is possible to encode the information in a it is possible to encode the information in a
.TP
.B --dhcp-hostsdir=<path>
This is exactly equivalent to dhcp-hostfile, except for the following. The path MUST be a
directory, and not an individual file. Changed or new files within
the directory are read automatically, without the need to send SIGHUP.
If a file is deleted for changed after it has been read by dnsmasq, then the
host record it contained will remain until dnsmasq recieves a SIGHUP, or
is restarted; ie host records are only added dynamically.
.TP
.B --dhcp-boot .B --dhcp-boot
flag as DHCP options, using the options names bootfile-name, flag as DHCP options, using the options names bootfile-name,
server-ip-address and tftp-server. This allows these to be included server-ip-address and tftp-server. This allows these to be included
......
...@@ -142,6 +142,9 @@ int main (int argc, char **argv) ...@@ -142,6 +142,9 @@ int main (int argc, char **argv)
set_option_bool(OPT_NOWILD); set_option_bool(OPT_NOWILD);
reset_option_bool(OPT_CLEVERBIND); reset_option_bool(OPT_CLEVERBIND);
} }
if (daemon->inotify_hosts)
die(_("dhcp-hostsdir not supported on this platform"), NULL, EC_BADCONF);
#endif #endif
if (option_bool(OPT_DNSSEC_VALID)) if (option_bool(OPT_DNSSEC_VALID))
...@@ -316,12 +319,15 @@ int main (int argc, char **argv) ...@@ -316,12 +319,15 @@ int main (int argc, char **argv)
#ifdef HAVE_DNSSEC #ifdef HAVE_DNSSEC
blockdata_init(); blockdata_init();
#endif #endif
}
#ifdef HAVE_LINUX_NETWORK #ifdef HAVE_LINUX_NETWORK
if (!option_bool(OPT_NO_POLL)) if ((!option_bool(OPT_NO_POLL) && daemon->port != 0) ||
daemon->dhcp || daemon->doing_dhcp6)
inotify_dnsmasq_init(); inotify_dnsmasq_init();
else
daemon->inotifyfd = -1;
#endif #endif
}
if (option_bool(OPT_DBUS)) if (option_bool(OPT_DBUS))
#ifdef HAVE_DBUS #ifdef HAVE_DBUS
...@@ -870,7 +876,7 @@ int main (int argc, char **argv) ...@@ -870,7 +876,7 @@ int main (int argc, char **argv)
#if defined(HAVE_LINUX_NETWORK) #if defined(HAVE_LINUX_NETWORK)
FD_SET(daemon->netlinkfd, &rset); FD_SET(daemon->netlinkfd, &rset);
bump_maxfd(daemon->netlinkfd, &maxfd); bump_maxfd(daemon->netlinkfd, &maxfd);
if (daemon->port != 0 && !option_bool(OPT_NO_POLL)) if (daemon->inotifyfd != -1)
{ {
FD_SET(daemon->inotifyfd, &rset); FD_SET(daemon->inotifyfd, &rset);
bump_maxfd(daemon->inotifyfd, &maxfd); bump_maxfd(daemon->inotifyfd, &maxfd);
...@@ -943,8 +949,11 @@ int main (int argc, char **argv) ...@@ -943,8 +949,11 @@ int main (int argc, char **argv)
#endif #endif
#ifdef HAVE_LINUX_NETWORK #ifdef HAVE_LINUX_NETWORK
if (daemon->port != 0 && !option_bool(OPT_NO_POLL) && FD_ISSET(daemon->inotifyfd, &rset) && inotify_check()) if (daemon->inotifyfd != -1 && FD_ISSET(daemon->inotifyfd, &rset) && inotify_check(now))
{
if (daemon->port != 0 && !option_bool(OPT_NO_POLL))
poll_resolv(1, 1, now); poll_resolv(1, 1, now);
}
#else #else
/* Check for changes to resolv files once per second max. */ /* Check for changes to resolv files once per second max. */
/* Don't go silent for long periods if the clock goes backwards. */ /* Don't go silent for long periods if the clock goes backwards. */
...@@ -1385,6 +1394,9 @@ void clear_cache_and_reload(time_t now) ...@@ -1385,6 +1394,9 @@ void clear_cache_and_reload(time_t now)
if (option_bool(OPT_ETHERS)) if (option_bool(OPT_ETHERS))
dhcp_read_ethers(); dhcp_read_ethers();
reread_dhcp(); reread_dhcp();
#ifdef HAVE_LINUX_NETWORK
set_dhcp_inotify();
#endif
dhcp_update_configs(daemon->dhcp_conf); dhcp_update_configs(daemon->dhcp_conf);
lease_update_from_configs(); lease_update_from_configs();
lease_update_file(now); lease_update_file(now);
......
...@@ -550,13 +550,17 @@ struct resolvc { ...@@ -550,13 +550,17 @@ struct resolvc {
#endif #endif
}; };
/* adn-hosts parms from command-line (also dhcp-hostsfile and dhcp-optsfile */ /* adn-hosts parms from command-line (also dhcp-hostsfile and dhcp-optsfile and dhcp-hostsdir*/
#define AH_DIR 1 #define AH_DIR 1
#define AH_INACTIVE 2 #define AH_INACTIVE 2
#define AH_WD_DONE 4
struct hostsfile { struct hostsfile {
struct hostsfile *next; struct hostsfile *next;
int flags; int flags;
char *fname; char *fname;
#ifdef HAVE_LINUX_NETWORK
int wd; /* inotify watch descriptor */
#endif
unsigned int index; /* matches to cache entries for logging */ unsigned int index; /* matches to cache entries for logging */
}; };
...@@ -961,7 +965,7 @@ extern struct daemon { ...@@ -961,7 +965,7 @@ extern struct daemon {
int doing_ra, doing_dhcp6; int doing_ra, doing_dhcp6;
struct dhcp_netid_list *dhcp_ignore, *dhcp_ignore_names, *dhcp_gen_names; struct dhcp_netid_list *dhcp_ignore, *dhcp_ignore_names, *dhcp_gen_names;
struct dhcp_netid_list *force_broadcast, *bootp_dynamic; struct dhcp_netid_list *force_broadcast, *bootp_dynamic;
struct hostsfile *dhcp_hosts_file, *dhcp_opts_file; struct hostsfile *dhcp_hosts_file, *dhcp_opts_file, *inotify_hosts;
int dhcp_max, tftp_max; int dhcp_max, tftp_max;
int dhcp_server_port, dhcp_client_port; int dhcp_server_port, dhcp_client_port;
int start_tftp_port, end_tftp_port; int start_tftp_port, end_tftp_port;
...@@ -1197,7 +1201,7 @@ void reset_option_bool(unsigned int opt); ...@@ -1197,7 +1201,7 @@ void reset_option_bool(unsigned int opt);
struct hostsfile *expand_filelist(struct hostsfile *list); struct hostsfile *expand_filelist(struct hostsfile *list);
char *parse_server(char *arg, union mysockaddr *addr, char *parse_server(char *arg, union mysockaddr *addr,
union mysockaddr *source_addr, char *interface, int *flags); union mysockaddr *source_addr, char *interface, int *flags);
int option_read_hostsfile(char *file);
/* forward.c */ /* forward.c */
void reply_query(int fd, int family, time_t now); void reply_query(int fd, int family, time_t now);
void receive_query(struct listener *listen, time_t now); void receive_query(struct listener *listen, time_t now);
...@@ -1486,5 +1490,8 @@ int detect_loop(char *query, int type); ...@@ -1486,5 +1490,8 @@ int detect_loop(char *query, int type);
/* inotify.c */ /* inotify.c */
#ifdef HAVE_LINUX_NETWORK #ifdef HAVE_LINUX_NETWORK
void inotify_dnsmasq_init(); void inotify_dnsmasq_init();
int inotify_check(void); int inotify_check(time_t now);
# ifdef HAVE_DHCP
void set_dhcp_inotify(void);
# endif
#endif #endif
...@@ -19,6 +19,11 @@ ...@@ -19,6 +19,11 @@
#include <sys/inotify.h> #include <sys/inotify.h>
#ifdef HAVE_DHCP
static void check_for_dhcp_inotify(struct inotify_event *in, time_t now);
#endif
/* the strategy is to set a inotify on the directories containing /* the strategy is to set a inotify on the directories containing
resolv files, for any files in the directory which are close-write resolv files, for any files in the directory which are close-write
or moved into the directory. or moved into the directory.
...@@ -40,8 +45,6 @@ void inotify_dnsmasq_init() ...@@ -40,8 +45,6 @@ void inotify_dnsmasq_init()
struct resolvc *res; struct resolvc *res;
inotify_buffer = safe_malloc(INOTIFY_SZ); inotify_buffer = safe_malloc(INOTIFY_SZ);
daemon->inotifyfd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC); daemon->inotifyfd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
if (daemon->inotifyfd == -1) if (daemon->inotifyfd == -1)
...@@ -66,6 +69,7 @@ void inotify_dnsmasq_init() ...@@ -66,6 +69,7 @@ void inotify_dnsmasq_init()
{ {
*d = 0; /* make path just directory */ *d = 0; /* make path just directory */
res->wd = inotify_add_watch(daemon->inotifyfd, path, IN_CLOSE_WRITE | IN_MOVED_TO); res->wd = inotify_add_watch(daemon->inotifyfd, path, IN_CLOSE_WRITE | IN_MOVED_TO);
res->file = d+1; /* pointer to filename */ res->file = d+1; /* pointer to filename */
*d = '/'; *d = '/';
...@@ -78,7 +82,7 @@ void inotify_dnsmasq_init() ...@@ -78,7 +82,7 @@ void inotify_dnsmasq_init()
} }
} }
int inotify_check(void) int inotify_check(time_t now)
{ {
int hit = 0; int hit = 0;
...@@ -101,13 +105,116 @@ int inotify_check(void) ...@@ -101,13 +105,116 @@ int inotify_check(void)
for (res = daemon->resolv_files; res; res = res->next) for (res = daemon->resolv_files; res; res = res->next)
if (res->wd == in->wd && in->len != 0 && strcmp(res->file, in->name) == 0) if (res->wd == in->wd && in->len != 0 && strcmp(res->file, in->name) == 0)
hit = 1; hit = 1;
#ifdef HAVE_DHCP
if (daemon->dhcp || daemon->doing_dhcp6)
check_for_dhcp_inotify(in, now);
#endif
} }
} }
return hit; return hit;
} }
#endif #ifdef HAVE_DHCP
/* initialisation for dhcp-hostdir. Set inotify watch for each directory, and read pre-existing files */
void set_dhcp_inotify(void)
{
struct hostsfile *ah;
for (ah = daemon->inotify_hosts; ah; ah = ah->next)
{
DIR *dir_stream = NULL;
struct dirent *ent;
struct stat buf;
if (stat(ah->fname, &buf) == -1 || !(S_ISDIR(buf.st_mode)))
{
my_syslog(LOG_ERR, _("bad directory in dhcp-hostsdir %s"), ah->fname);
continue;
}
if (!(ah->flags & AH_WD_DONE))
{
ah->wd = inotify_add_watch(daemon->inotifyfd, ah->fname, IN_CLOSE_WRITE | IN_MOVED_TO);
ah->flags |= AH_WD_DONE;
}
/* Read contents of dir _after_ calling add_watch, in the ho[e of avoiding
a race which misses files being added as we start */
if (ah->wd == -1 || !(dir_stream = opendir(ah->fname)))
{
my_syslog(LOG_ERR, _("failed to create inotify for %s"), ah->fname);
continue;
}
while ((ent = readdir(dir_stream)))
{
size_t lendir = strlen(ah->fname);
size_t lenfile = strlen(ent->d_name);
char *path;
/* ignore emacs backups and dotfiles */
if (lenfile == 0 ||
ent->d_name[lenfile - 1] == '~' ||
(ent->d_name[0] == '#' && ent->d_name[lenfile - 1] == '#') ||
ent->d_name[0] == '.')
continue;
if ((path = whine_malloc(lendir + lenfile + 2)))
{
strcpy(path, ah->fname);
strcat(path, "/");
strcat(path, ent->d_name);
/* ignore non-regular files */
if (stat(path, &buf) != -1 && S_ISREG(buf.st_mode))
option_read_hostsfile(path);
free(path);
}
}
}
}
static void check_for_dhcp_inotify(struct inotify_event *in, time_t now)
{
struct hostsfile *ah;
/* ignore emacs backups and dotfiles */
if (in->len == 0 ||
in->name[in->len - 1] == '~' ||
(in->name[0] == '#' && in->name[in->len - 1] == '#') ||
in->name[0] == '.')
return;
for (ah = daemon->inotify_hosts; ah; ah = ah->next)
if (ah->wd == in->wd)
{
size_t lendir = strlen(ah->fname);
char *path;
if ((path = whine_malloc(lendir + in->len + 2)))
{
strcpy(path, ah->fname);
strcat(path, "/");
strcat(path, in->name);
if (option_read_hostsfile(path))
{
/* Propogate the consequences of loading a new dhcp-host */
dhcp_update_configs(daemon->dhcp_conf);
lease_update_from_configs();
lease_update_file(now);
lease_update_dns(1);
}
free(path);
}
return;
}
}
#endif /* DHCP */
#endif /* LINUX_NETWORK */
...@@ -149,7 +149,7 @@ struct myoption { ...@@ -149,7 +149,7 @@ struct myoption {
#define LOPT_LOOP_DETECT 337 #define LOPT_LOOP_DETECT 337
#define LOPT_IGNORE_ADDR 338 #define LOPT_IGNORE_ADDR 338
#define LOPT_MINCTTL 339 #define LOPT_MINCTTL 339
#define LOPT_DHCP_INOTIFY 340
#ifdef HAVE_GETOPT_LONG #ifdef HAVE_GETOPT_LONG
static const struct option opts[] = static const struct option opts[] =
...@@ -248,6 +248,7 @@ static const struct myoption opts[] = ...@@ -248,6 +248,7 @@ static const struct myoption opts[] =
{ "interface-name", 1, 0, LOPT_INTNAME }, { "interface-name", 1, 0, LOPT_INTNAME },
{ "dhcp-hostsfile", 1, 0, LOPT_DHCP_HOST }, { "dhcp-hostsfile", 1, 0, LOPT_DHCP_HOST },
{ "dhcp-optsfile", 1, 0, LOPT_DHCP_OPTS }, { "dhcp-optsfile", 1, 0, LOPT_DHCP_OPTS },
{ "dhcp-hostsdir", 1, 0, LOPT_DHCP_INOTIFY },
{ "dhcp-no-override", 0, 0, LOPT_OVERRIDE }, { "dhcp-no-override", 0, 0, LOPT_OVERRIDE },
{ "tftp-port-range", 1, 0, LOPT_TFTPPORTS }, { "tftp-port-range", 1, 0, LOPT_TFTPPORTS },
{ "stop-dns-rebind", 0, 0, LOPT_REBIND }, { "stop-dns-rebind", 0, 0, LOPT_REBIND },
...@@ -336,6 +337,7 @@ static struct { ...@@ -336,6 +337,7 @@ static struct {
{ 'G', ARG_DUP, "<hostspec>", gettext_noop("Set address or hostname for a specified machine."), NULL }, { 'G', ARG_DUP, "<hostspec>", gettext_noop("Set address or hostname for a specified machine."), NULL },
{ LOPT_DHCP_HOST, ARG_DUP, "<path>", gettext_noop("Read DHCP host specs from file."), NULL }, { LOPT_DHCP_HOST, ARG_DUP, "<path>", gettext_noop("Read DHCP host specs from file."), NULL },
{ LOPT_DHCP_OPTS, ARG_DUP, "<path>", gettext_noop("Read DHCP option specs from file."), NULL }, { LOPT_DHCP_OPTS, ARG_DUP, "<path>", gettext_noop("Read DHCP option specs from file."), NULL },
{ LOPT_DHCP_INOTIFY, ARG_DUP, "<path>", gettext_noop("Read DHCP host specs from a directory."), NULL },
{ LOPT_TAG_IF, ARG_DUP, "tag-expression", gettext_noop("Evaluate conditional tag expression."), NULL }, { LOPT_TAG_IF, ARG_DUP, "tag-expression", gettext_noop("Evaluate conditional tag expression."), NULL },
{ 'h', OPT_NO_HOSTS, NULL, gettext_noop("Do NOT load %s file."), HOSTSFILE }, { 'h', OPT_NO_HOSTS, NULL, gettext_noop("Do NOT load %s file."), HOSTSFILE },
{ 'H', ARG_DUP, "<path>", gettext_noop("Specify a hosts file to be read in addition to %s."), HOSTSFILE }, { 'H', ARG_DUP, "<path>", gettext_noop("Specify a hosts file to be read in addition to %s."), HOSTSFILE },
...@@ -1710,8 +1712,9 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma ...@@ -1710,8 +1712,9 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
break; break;
#endif /* HAVE_DHCP */ #endif /* HAVE_DHCP */
case LOPT_DHCP_HOST: /* --dhcp-hostfile */ case LOPT_DHCP_HOST: /* --dhcp-hostsfile */
case LOPT_DHCP_OPTS: /* --dhcp-optsfile */ case LOPT_DHCP_OPTS: /* --dhcp-optsfile */
case LOPT_DHCP_INOTIFY: /* dhcp-hostsdir */
case 'H': /* --addn-hosts */ case 'H': /* --addn-hosts */
{ {
struct hostsfile *new = opt_malloc(sizeof(struct hostsfile)); struct hostsfile *new = opt_malloc(sizeof(struct hostsfile));
...@@ -1734,6 +1737,12 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma ...@@ -1734,6 +1737,12 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
new->next = daemon->dhcp_opts_file; new->next = daemon->dhcp_opts_file;
daemon->dhcp_opts_file = new; daemon->dhcp_opts_file = new;
} }
else if (option == LOPT_DHCP_INOTIFY)
{
new->next = daemon->inotify_hosts;
daemon->inotify_hosts = new;
}
break; break;
} }
...@@ -4042,6 +4051,13 @@ static void read_file(char *file, FILE *f, int hard_opt) ...@@ -4042,6 +4051,13 @@ static void read_file(char *file, FILE *f, int hard_opt)
fclose(f); fclose(f);
} }
#ifdef HAVE_DHCP
int option_read_hostsfile(char *file)
{
return one_file(file, LOPT_BANK);
}
#endif
static int one_file(char *file, int hard_opt) static int one_file(char *file, int hard_opt)
{ {
FILE *f; FILE *f;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment