summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Kelley <simon@thekelleys.org.uk>2021-08-12 16:48:54 +0100
committerSimon Kelley <simon@thekelleys.org.uk>2021-08-12 16:48:54 +0100
commit2f2d59b35c3338ffa20361d409f07ef340987d1b (patch)
tree6d310fe41484b42054d931eda9f7ea64bfda526d
parenta1729deed3a7d456c5568c0a81450938099e66ae (diff)
downloaddnsmasq-2f2d59b35c3338ffa20361d409f07ef340987d1b.tar.gz
Define order of reading files when --addn-hosts given a directory.
Also applies to --dhcp-hostsfile and --dhcp-optsfile though it is less useful there.
-rw-r--r--CHANGELOG6
-rw-r--r--man/dnsmasq.810
-rw-r--r--src/option.c54
3 files changed, 51 insertions, 19 deletions
diff --git a/CHANGELOG b/CHANGELOG
index d477812..717c29d 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -86,6 +86,12 @@ version 2.86
conntrack is configured. Thanks to Yick Xie for spotting
the lack of this.
+ When --dhcp-hostsfile --dhcp-optsfile and --addn-hosts are
+ given a directory as argument, define the order in which
+ files within that directory are read (alphabetical order
+ of filename). Thanks to Ed Wildgoose for the initial patch
+ and motivation for this.
+
version 2.85
Fix problem with DNS retries in 2.83/2.84.
diff --git a/man/dnsmasq.8 b/man/dnsmasq.8
index 75b5599..4ad125b 100644
--- a/man/dnsmasq.8
+++ b/man/dnsmasq.8
@@ -55,7 +55,8 @@ Don't read the hostnames in /etc/hosts.
.B \-H, --addn-hosts=<file>
Additional hosts file. Read the specified file as well as /etc/hosts. If \fB--no-hosts\fP is given, read
only the specified file. This option may be repeated for more than one
-additional hosts file. If a directory is given, then read all the files contained in that directory.
+additional hosts file. If a directory is given, then read all the files contained in that directory
+in alphabetical order.
.TP
.B --hostsdir=<path>
Read all the hosts files contained in the directory. New or changed files
@@ -1165,7 +1166,7 @@ has both wired and wireless interfaces.
.TP
.B --dhcp-hostsfile=<path>
Read DHCP host information from the specified file. If a directory
-is given, then read all the files contained in that directory. The file contains
+is given, then read all the files contained in that directory in alphabetical order. The file contains
information about one host per line. The format of a line is the same
as text to the right of '=' in \fB--dhcp-host\fP. The advantage of storing DHCP host information
in this file is that it can be changed without re-starting dnsmasq:
@@ -1173,7 +1174,7 @@ the file will be re-read when dnsmasq receives SIGHUP.
.TP
.B --dhcp-optsfile=<path>
Read DHCP option information from the specified file. If a directory
-is given, then read all the files contained in that directory. The advantage of
+is given, then read all the files contained in that directory in alphabetical order. The advantage of
using this option is the same as for \fB--dhcp-hostsfile\fP: the
\fB--dhcp-optsfile\fP will be re-read when dnsmasq receives SIGHUP. Note that
it is possible to encode the information in a
@@ -1188,7 +1189,8 @@ 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 or changed after it has been read by dnsmasq, then the
host record it contained will remain until dnsmasq receives a SIGHUP, or
-is restarted; ie host records are only added dynamically.
+is restarted; ie host records are only added dynamically. The order in which the
+files in a directory are read is not defined.
.TP
.B --dhcp-optsdir=<path>
This is equivalent to \fB--dhcp-optsfile\fP, with the differences noted for \fB--dhcp-hostsdir\fP.
diff --git a/src/option.c b/src/option.c
index 14fa537..3a87097 100644
--- a/src/option.c
+++ b/src/option.c
@@ -5027,15 +5027,33 @@ static int one_file(char *file, int hard_opt)
return 1;
}
+static int file_filter(const struct dirent *ent)
+{
+ size_t lenfile = strlen(ent->d_name);
+
+ /* 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] == '.')
+ return 0;
+
+ return 1;
+}
/* expand any name which is a directory */
struct hostsfile *expand_filelist(struct hostsfile *list)
{
unsigned int i;
- struct hostsfile *ah;
+ int entcnt, n;
+ struct hostsfile *ah, *last, *next, **up;
+ struct dirent **namelist;
/* find largest used index */
for (i = SRC_AH, ah = list; ah; ah = ah->next)
{
+ last = ah;
+
if (i <= ah->index)
i = ah->index + 1;
@@ -5051,46 +5069,48 @@ struct hostsfile *expand_filelist(struct hostsfile *list)
struct stat buf;
if (stat(ah->fname, &buf) != -1 && S_ISDIR(buf.st_mode))
{
- DIR *dir_stream;
struct dirent *ent;
/* don't read this as a file */
ah->flags |= AH_INACTIVE;
- if (!(dir_stream = opendir(ah->fname)))
+ entcnt = scandir(ah->fname, &namelist, file_filter, alphasort);
+ if (entcnt < 0)
my_syslog(LOG_ERR, _("cannot access directory %s: %s"),
ah->fname, strerror(errno));
else
{
- while ((ent = readdir(dir_stream)))
+ for (n = 0; n < entcnt; n++)
{
+ ent = namelist[n];
size_t lendir = strlen(ah->fname);
size_t lenfile = strlen(ent->d_name);
struct hostsfile *ah1;
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;
-
/* see if we have an existing record.
dir is ah->fname
file is ent->d_name
path to match is ah1->fname */
- for (ah1 = list; ah1; ah1 = ah1->next)
+ for (up = &list, ah1 = list; ah1; ah1 = next)
{
+ next = ah1->next;
+
if (lendir < strlen(ah1->fname) &&
strstr(ah1->fname, ah->fname) == ah1->fname &&
ah1->fname[lendir] == '/' &&
strcmp(ah1->fname + lendir + 1, ent->d_name) == 0)
{
ah1->flags &= ~AH_INACTIVE;
+ /* If found, remove from list to re-insert at the end.
+ Unless it's already at the end. */
+ if (last != ah1)
+ *up = next;
break;
}
+
+ up = &ah1->next;
}
/* make new record */
@@ -5111,17 +5131,21 @@ struct hostsfile *expand_filelist(struct hostsfile *list)
ah1->fname = path;
ah1->index = i++;
ah1->flags = AH_DIR;
- ah1->next = list;
- list = ah1;
}
+
+ /* Edge case, may be the last in the list anyway */
+ if (last != ah1)
+ last->next = ah1;
+ ah1->next = NULL;
+ last = ah1;
/* inactivate record if not regular file */
if ((ah1->flags & AH_DIR) && stat(ah1->fname, &buf) != -1 && !S_ISREG(buf.st_mode))
ah1->flags |= AH_INACTIVE;
}
- closedir(dir_stream);
}
+ free(namelist);
}
}