diff options
author | vangyzen <vangyzen@FreeBSD.org> | 2015-12-19 02:41:31 +0000 |
---|---|---|
committer | vangyzen <vangyzen@FreeBSD.org> | 2015-12-19 02:41:31 +0000 |
commit | b6f246520d532d80d0838a3b005db8ef8e9e9a47 (patch) | |
tree | 83f00a984fb131904f8621162a28cdfffaa7e50e /lib/libc | |
parent | ec40565958ba8acbc1f4b137261df32c9fc37568 (diff) | |
download | FreeBSD-src-b6f246520d532d80d0838a3b005db8ef8e9e9a47.zip FreeBSD-src-b6f246520d532d80d0838a3b005db8ef8e9e9a47.tar.gz |
MFC r289315, r292216
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.
Relnotes: yes
Sponsored by: Dell Inc.
Diffstat (limited to 'lib/libc')
-rw-r--r-- | lib/libc/resolv/res_init.c | 22 | ||||
-rw-r--r-- | lib/libc/resolv/res_private.h | 5 | ||||
-rw-r--r-- | lib/libc/resolv/res_state.c | 39 |
3 files changed, 62 insertions, 4 deletions
diff --git a/lib/libc/resolv/res_init.c b/lib/libc/resolv/res_init.c index f636baa..41e9f5a 100644 --- a/lib/libc/resolv/res_init.c +++ b/lib/libc/resolv/res_init.c @@ -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> @@ -238,6 +239,7 @@ __res_vinit(res_state statp, int preinit) { statp->_u._ext.ext->nsaddrs[0].sin = statp->nsaddr; strcpy(statp->_u._ext.ext->nsuffix, "ip6.arpa"); strcpy(statp->_u._ext.ext->nsuffix2, "ip6.int"); + statp->_u._ext.ext->reload_period = 2; } else { /* * Historically res_init() rarely, if at all, failed. @@ -323,6 +325,18 @@ __res_vinit(res_state statp, int preinit) { nserv = 0; if ((fp = fopen(_PATH_RESCONF, "re")) != NULL) { + struct stat sb; + struct timespec now; + + if (statp->_u._ext.ext != NULL) { + if (_fstat(fileno(fp), &sb) == 0) { + statp->_u._ext.ext->conf_mtim = sb.st_mtim; + if (clock_gettime(CLOCK_MONOTONIC_FAST, &now) == 0) { + statp->_u._ext.ext->conf_stat = now.tv_sec; + } + } + } + /* read the config file */ while (fgets(buf, sizeof(buf), fp) != NULL) { /* skip comments */ @@ -583,9 +597,7 @@ res_setoptions(res_state statp, const char *options, const char *source) { const char *cp = options; int i; -#ifndef _LIBC struct __res_state_ext *ext = statp->_u._ext.ext; -#endif #ifdef DEBUG if (statp->options & RES_DEBUG) @@ -668,6 +680,12 @@ 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)) { + if (ext != NULL) { + ext->reload_period = (u_short) + atoi(cp + sizeof("reload-period:") - 1); + } } #ifdef RES_USE_EDNS0 else if (!strncmp(cp, "edns0", sizeof("edns0") - 1)) { diff --git a/lib/libc/resolv/res_private.h b/lib/libc/resolv/res_private.h index 4e98157..a986e95 100644 --- a/lib/libc/resolv/res_private.h +++ b/lib/libc/resolv/res_private.h @@ -1,3 +1,5 @@ +/* $FreeBSD$ */ + #ifndef res_private_h #define res_private_h @@ -12,6 +14,9 @@ struct __res_state_ext { } sort_list[MAXRESOLVSORT]; char nsuffix[64]; char nsuffix2[64]; + struct timespec conf_mtim; /* mod time of loaded resolv.conf */ + time_t conf_stat; /* time of last stat(resolv.conf) */ + u_short reload_period; /* seconds between stat(resolv.conf) */ }; extern int diff --git a/lib/libc/resolv/res_state.c b/lib/libc/resolv/res_state.c index 59c9430..6d31d92 100644 --- a/lib/libc/resolv/res_state.c +++ b/lib/libc/resolv/res_state.c @@ -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> @@ -35,6 +37,8 @@ #include "reentrant.h" #include "un-namespace.h" +#include "res_private.h" + #undef _res struct __res_state _res; @@ -59,13 +63,44 @@ 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; + struct __res_state_ext *ext; + + if ((statp->options & RES_INIT) == 0) { + return (statp); + } + + ext = statp->_u._ext.ext; + if (ext == NULL || ext->reload_period == 0) { + return (statp); + } + + if (clock_gettime(CLOCK_MONOTONIC_FAST, &now) != 0 || + (now.tv_sec - ext->conf_stat) < ext->reload_period) { + return (statp); + } + + ext->conf_stat = now.tv_sec; + if (stat(_PATH_RESCONF, &sb) == 0 && + (sb.st_mtim.tv_sec != ext->conf_mtim.tv_sec || + sb.st_mtim.tv_nsec != ext->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 +108,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); |