resolver: automatically reload /etc/resolv.conf

On each resolver query, use stat(2) to see if the modification time
of /etc/resolv.conf has changed.  If so, reload the file and reinitialize
the resolver library.  However, only call stat(2) if at least two seconds
have passed since the last call to stat(2), since calling it on every
query could kill performance.

This new behavior is enabled by default.  Add a "reload-period" option
to disable it or change the period of the test.

Document this behavior and option in resolv.conf(5).

Polish the man page just enough to appease igor.

https://lists.freebsd.org/pipermail/freebsd-arch/2015-October/017342.html

Reviewed by:	kp, wblock
Discussed with:	jilles, imp, alfred
MFC after:	1 month
Relnotes:	yes
Sponsored by:	Dell Inc.
Differential Revision:	https://reviews.freebsd.org/D3867
This commit is contained in:
Eric van Gyzen 2015-10-14 14:26:44 +00:00
parent 2bfd7091a5
commit 60b27ebb25
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=289315
4 changed files with 70 additions and 6 deletions

View file

@ -176,7 +176,8 @@ struct __res_state {
int res_h_errno; /*%< last one set for this context */
int _vcsock; /*%< PRIVATE: for res_send VC i/o */
u_int _flags; /*%< PRIVATE: see below */
u_int _pad; /*%< make _u 64 bit aligned */
u_short reload_period; /*%< seconds between stat(resolv.conf)*/
u_short _pad; /*%< make _u 64 bit aligned */
union {
/* On an 32-bit arch this means 512b total. */
char pad[72 - 4*sizeof (int) - 3*sizeof (void *)];
@ -188,6 +189,8 @@ struct __res_state {
} _ext;
} _u;
u_char *_rnd; /*%< PRIVATE: random state */
struct timespec conf_mtim; /*%< mod time of loaded resolv.conf */
time_t conf_stat; /*%< time of last stat(resolv.conf) */
};
typedef struct __res_state *res_state;

View file

@ -78,6 +78,7 @@ __FBSDID("$FreeBSD$");
#include <sys/types.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <netinet/in.h>
@ -227,6 +228,7 @@ __res_vinit(res_state statp, int preinit) {
statp->pfcode = 0;
statp->_vcsock = -1;
statp->_flags = 0;
statp->reload_period = 2;
statp->qhook = NULL;
statp->rhook = NULL;
statp->_u._ext.nscount = 0;
@ -321,6 +323,22 @@ __res_vinit(res_state statp, int preinit) {
nserv = 0;
if ((fp = fopen(_PATH_RESCONF, "re")) != NULL) {
struct stat sb;
struct timespec now;
if (_fstat(fileno(fp), &sb) == 0) {
statp->conf_mtim = sb.st_mtim;
if (clock_gettime(CLOCK_MONOTONIC_FAST, &now) == 0) {
statp->conf_stat = now.tv_sec;
} else {
statp->conf_stat = 0;
}
} else {
statp->conf_mtim.tv_sec = 0;
statp->conf_mtim.tv_nsec = 0;
statp->conf_stat = 0;
}
/* read the config file */
while (fgets(buf, sizeof(buf), fp) != NULL) {
/* skip comments */
@ -666,6 +684,10 @@ res_setoptions(res_state statp, const char *options, const char *source)
} else if (!strncmp(cp, "no-check-names",
sizeof("no-check-names") - 1)) {
statp->options |= RES_NOCHECKNAME;
} else if (!strncmp(cp, "reload-period:",
sizeof("reload-period:") - 1)) {
statp->reload_period = (u_short)
atoi(cp + sizeof("reload-period:") - 1);
}
#ifdef RES_USE_EDNS0
else if (!strncmp(cp, "edns0", sizeof("edns0") - 1)) {

View file

@ -26,6 +26,8 @@
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/nameser.h>
#include <resolv.h>
@ -59,13 +61,38 @@ res_keycreate(void)
res_thr_keycreated = thr_keycreate(&res_key, free_res) == 0;
}
static res_state
res_check_reload(res_state statp)
{
struct timespec now;
struct stat sb;
if ((statp->options & RES_INIT) == 0 || statp->reload_period == 0) {
return (statp);
}
if (clock_gettime(CLOCK_MONOTONIC_FAST, &now) != 0 ||
(now.tv_sec - statp->conf_stat) < statp->reload_period) {
return (statp);
}
statp->conf_stat = now.tv_sec;
if (stat(_PATH_RESCONF, &sb) == 0 &&
(sb.st_mtim.tv_sec != statp->conf_mtim.tv_sec ||
sb.st_mtim.tv_nsec != statp->conf_mtim.tv_nsec)) {
statp->options &= ~RES_INIT;
}
return (statp);
}
res_state
__res_state(void)
{
res_state statp;
if (thr_main() != 0)
return (&_res);
return res_check_reload(&_res);
if (thr_once(&res_init_once, res_keycreate) != 0 ||
!res_thr_keycreated)
@ -73,7 +100,7 @@ __res_state(void)
statp = thr_getspecific(res_key);
if (statp != NULL)
return (statp);
return res_check_reload(statp);
statp = calloc(1, sizeof(*statp));
if (statp == NULL)
return (&_res);

View file

@ -28,7 +28,7 @@
.\" @(#)resolver.5 8.1 (Berkeley) 6/5/93
.\" $FreeBSD$
.\"
.Dd December 25, 2013
.Dd October 12, 2015
.Dt RESOLVER 5
.Os
.Sh NAME
@ -175,6 +175,19 @@ the resolver from obeying the standard
and
.Sy search
rules with the given name.
.It Sy reload-period: Ns Ar n
The resolver checks the modification time of
.Pa /etc/resolv.conf
every
.Ar n
seconds.
If
.Pa /etc/resolv.conf
has changed, it is automatically reloaded.
The default for
.Ar n
is two seconds.
Setting it to zero disables the file check.
.El
.Pp
Options may also be specified as a space or tab separated list using the
@ -191,8 +204,7 @@ If more than one instance of these keywords is present,
the last instance will override.
.Pp
The keyword and value must appear on a single line, and the keyword
(e.g.\&
.Sy nameserver )
.Pq for example, Sy nameserver
must start the line.
The value follows the keyword, separated by white space.
.Sh FILES