Subject: re-reading /etc/resolv.conf on change
To: None <tech-userlevel@netbsd.org>
From: Manuel Bouyer <bouyer@antioche.lip6.fr>
List: tech-userlevel
Date: 12/28/2003 17:54:35
--6c2NcOVqGQ03X4Wi
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
Hi,
with the current libc resolver, /etc/resolv.conf is read on first call to a
resolver function, and never re-read again. Any changes to /etc/resolv.conf
require restarting all applications making use of the resolver.
I found this really annoying on my laptop (I have to restart mozilla every time
I get online), so I came with the attached hack. Linux is probably already
rereading /etc/resolv.conf on change, because I didn't have this issue when
I used the linux netscape4 binary.
What I did is unconditionally call res_init() from other resolver functions.
In res_init(), I use kqueue to monitor changes to /etc/resolv.conf
(or to /etc/. if /etc/resolv.conf doens't exists). Every call to a resolver
function will call res_init(), which will check the kevent queue.
This adds a little overhead to the resolver functions (a function call,
and a system call), but I think it's worth it.
Open issues are:
- should I add a #define (_PATH_RESCONF_PARENT ?) instead of hardcoding
/etc/., or maybe use dirname() (which seems overkill, as _PATH_RESCONF is
static) ?
- does the kqueue code looks OK (it's my first kqueue code) ?
- should we have a -DSMALL version of res_init() for install media ?
If noone objects, I'll commit this code in about a week.
--
Manuel Bouyer <bouyer@antioche.eu.org>
NetBSD: 23 ans d'experience feront toujours la difference
--
--6c2NcOVqGQ03X4Wi
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename=diff
Index: net/getaddrinfo.c
===================================================================
RCS file: /home/bouyer/anoncvs/cvs/src/lib/libc/net/getaddrinfo.c,v
retrieving revision 1.66
diff -u -r1.66 getaddrinfo.c
--- net/getaddrinfo.c 17 May 2003 01:36:03 -0000 1.66
+++ net/getaddrinfo.c 28 Dec 2003 16:10:55 -0000
@@ -1724,7 +1724,7 @@
rcode = NOERROR;
ancount = 0;
- if ((_res.options & RES_INIT) == 0 && res_init() == -1) {
+ if (res_init() == -1) {
h_errno = NETDB_INTERNAL;
return (-1);
}
@@ -1833,7 +1833,7 @@
hp = (HEADER *)(void *)target->answer; /*XXX*/
- if ((_res.options & RES_INIT) == 0 && res_init() == -1) {
+ if (res_init() == -1) {
h_errno = NETDB_INTERNAL;
return (-1);
}
@@ -1973,7 +1973,7 @@
_DIAGASSERT(name != NULL);
/* XXX: target may be NULL??? */
- if ((_res.options & RES_INIT) == 0 && res_init() == -1) {
+ if (res_init() == -1) {
h_errno = NETDB_INTERNAL;
return (-1);
}
Index: net/gethnamaddr.c
===================================================================
RCS file: /home/bouyer/anoncvs/cvs/src/lib/libc/net/gethnamaddr.c,v
retrieving revision 1.58
diff -u -r1.58 gethnamaddr.c
--- net/gethnamaddr.c 11 Oct 2003 03:35:42 -0000 1.58
+++ net/gethnamaddr.c 28 Dec 2003 16:11:02 -0000
@@ -515,7 +515,7 @@
_DIAGASSERT(name != NULL);
- if ((_res.options & RES_INIT) == 0 && res_init() == -1) {
+ if (res_init() == -1) {
h_errno = NETDB_INTERNAL;
return (NULL);
}
Index: net/getnetnamadr.c
===================================================================
RCS file: /home/bouyer/anoncvs/cvs/src/lib/libc/net/getnetnamadr.c,v
retrieving revision 1.27
diff -u -r1.27 getnetnamadr.c
--- net/getnetnamadr.c 7 Aug 2003 16:43:09 -0000 1.27
+++ net/getnetnamadr.c 28 Dec 2003 16:11:16 -0000
@@ -365,7 +365,7 @@
{ 0 }
};
- if ((_res.options & RES_INIT) == 0 && res_init() == -1) {
+ if (res_init() == -1) {
h_errno = NETDB_INTERNAL;
return (NULL);
}
@@ -467,7 +467,7 @@
_DIAGASSERT(net != NULL);
- if ((_res.options & RES_INIT) == 0 && res_init() == -1) {
+ if (res_init() == -1) {
h_errno = NETDB_INTERNAL;
return (NULL);
}
Index: net/hesiod.c
===================================================================
RCS file: /home/bouyer/anoncvs/cvs/src/lib/libc/net/hesiod.c,v
retrieving revision 1.20
diff -u -r1.20 hesiod.c
--- net/hesiod.c 11 Nov 2002 17:56:11 -0000 1.20
+++ net/hesiod.c 28 Dec 2003 16:11:25 -0000
@@ -430,7 +430,7 @@
_DIAGASSERT(name != NULL);
/* Make sure the resolver is initialized. */
- if ((_res.options & RES_INIT) == 0 && res_init() == -1)
+ if (res_init() == -1)
return NULL;
/* Construct the query. */
Index: net/res_debug.c
===================================================================
RCS file: /home/bouyer/anoncvs/cvs/src/lib/libc/net/res_debug.c,v
retrieving revision 1.35
diff -u -r1.35 res_debug.c
--- net/res_debug.c 7 Aug 2003 16:43:13 -0000 1.35
+++ net/res_debug.c 28 Dec 2003 16:12:01 -0000
@@ -286,7 +286,7 @@
_DIAGASSERT(msg != NULL);
_DIAGASSERT(file != NULL);
- if ((_res.options & RES_INIT) == 0 && res_init() == -1)
+ if (res_init() == -1)
return;
#define TruncTest(x) if (x > endMark) goto trunc
@@ -522,7 +522,7 @@
_DIAGASSERT(msg != NULL);
_DIAGASSERT(file != NULL);
- if ((_res.options & RES_INIT) == 0 && res_init() == -1) {
+ if (res_init() == -1) {
h_errno = NETDB_INTERNAL;
return (NULL);
}
Index: net/res_init.c
===================================================================
RCS file: /home/bouyer/anoncvs/cvs/src/lib/libc/net/res_init.c,v
retrieving revision 1.45
diff -u -r1.45 res_init.c
--- net/res_init.c 9 Sep 2003 22:16:58 -0000 1.45
+++ net/res_init.c 28 Dec 2003 16:07:48 -0000
@@ -62,6 +62,7 @@
#if defined(_LIBC)
#include "namespace.h"
#endif
+#include <sys/event.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/time.h>
@@ -71,6 +72,8 @@
#include <assert.h>
#include <ctype.h>
+#include <err.h>
+#include <fcntl.h>
#include <netdb.h>
#include <resolv.h>
#include <stdio.h>
@@ -129,9 +132,15 @@
int
res_init()
{
- register FILE *fp;
- register char *cp, **pp;
- register int n;
+ static FILE *fp = NULL;
+ static int fd = -1;
+ static int kq = -1;
+ static struct timespec tm;
+ struct kevent eventlist[64];
+ int nevents;
+ int has_resconv = 0;
+ char *cp, **pp;
+ int n;
char buf[MAXDNAME];
int nserv = 0; /* number of nameserver records read from file */
int haveenv = 0;
@@ -142,6 +151,52 @@
int dots;
#endif
+ if (_res.options & RES_INIT) {
+ if (fd >= 0 && kq >= 0) {
+ nevents = kevent(kq, NULL, 0, eventlist, 64, &tm);
+ if (nevents == 0) {
+ /* nothing changed */
+ return (0);
+ }
+ /* need to re-initialize the resolver */
+ if (fp)
+ fclose(fp); /* will also clear the kevent */
+ else
+ close(fd);
+ fp = NULL;
+ fd = -1;
+ }
+ }
+ fd = open(_PATH_RESCONF, O_RDONLY, 0);
+ if (fd >= 0) {
+ has_resconv = 1;
+ } else {
+ /* no /etc/resolv.conf, we want to monitor /etc */
+ fd = open("/etc/.", O_RDONLY, 0);
+ if (fd < 0)
+ warn("res_init: /etc/.");
+ }
+ if (kq < 0) {
+ kq = kqueue();
+ if (kq < 0) {
+ warn("res_init kqueue");
+ } else {
+ memset(&tm, 0, sizeof(tm));
+ }
+ }
+ if (fd >= 0 && kq >= 0) {
+ eventlist[0].ident = fd;
+ eventlist[0].filter = EVFILT_VNODE;
+ eventlist[0].flags = EV_ADD | EV_ENABLE | EV_CLEAR;
+ eventlist[0].fflags =
+ NOTE_DELETE | NOTE_WRITE | NOTE_RENAME | NOTE_REVOKE;
+ eventlist[0].data = 0;
+ eventlist[0].udata = 0;
+ if (kevent(kq, eventlist, 1, NULL, 0, &tm) < 0) {
+ warn("res_init kevent");
+ }
+ }
+
#ifdef __RES_IN_BSS
/*
* These three fields used to be statically initialized. This made
@@ -228,7 +283,7 @@
(line[sizeof(name) - 1] == ' ' || \
line[sizeof(name) - 1] == '\t'))
- if ((fp = fopen(_PATH_RESCONF, "r")) != NULL) {
+ if (has_resconv && (fp = fdopen(fd, "r")) != NULL) {
/* read the config file */
while (fgets(buf, sizeof(buf), fp) != NULL) {
/* skip comments */
@@ -447,7 +502,12 @@
if (nserv > 1)
_res.nscount = nserv;
_res.nsort = nsort;
- (void) fclose(fp);
+ } else {
+ /* either fd == -1, or fdopen() failed */
+ if (fd >= 0) {
+ close(fd);
+ fd = -1;
+ }
}
if (_res.defdname[0] == 0) {
int rv;
Index: net/res_mkquery.c
===================================================================
RCS file: /home/bouyer/anoncvs/cvs/src/lib/libc/net/res_mkquery.c,v
retrieving revision 1.25
diff -u -r1.25 res_mkquery.c
--- net/res_mkquery.c 9 Sep 2003 22:16:58 -0000 1.25
+++ net/res_mkquery.c 28 Dec 2003 16:12:18 -0000
@@ -112,7 +112,7 @@
}
}
- if ((_res.options & RES_INIT) == 0 && res_init() == -1) {
+ if (res_init() == -1) {
h_errno = NETDB_INTERNAL;
return (-1);
}
Index: net/res_query.c
===================================================================
RCS file: /home/bouyer/anoncvs/cvs/src/lib/libc/net/res_query.c,v
retrieving revision 1.37
diff -u -r1.37 res_query.c
--- net/res_query.c 7 Aug 2003 16:43:14 -0000 1.37
+++ net/res_query.c 28 Dec 2003 16:12:35 -0000
@@ -116,7 +116,7 @@
_DIAGASSERT(name != NULL);
_DIAGASSERT(answer != NULL);
- if ((_res.options & RES_INIT) == 0 && res_init() == -1) {
+ if (res_init() == -1) {
h_errno = NETDB_INTERNAL;
return (-1);
}
@@ -199,7 +199,7 @@
_DIAGASSERT(name != NULL);
_DIAGASSERT(answer != NULL);
- if ((_res.options & RES_INIT) == 0 && res_init() == -1) {
+ if (res_init() == -1) {
h_errno = NETDB_INTERNAL;
return (-1);
}
@@ -343,7 +343,7 @@
/* domain may be NULL */
_DIAGASSERT(answer != NULL);
- if ((_res.options & RES_INIT) == 0 && res_init() == -1) {
+ if (res_init() == -1) {
h_errno = NETDB_INTERNAL;
return (-1);
}
Index: net/res_send.c
===================================================================
RCS file: /home/bouyer/anoncvs/cvs/src/lib/libc/net/res_send.c,v
retrieving revision 1.37
diff -u -r1.37 res_send.c
--- net/res_send.c 7 Aug 2003 16:43:14 -0000 1.37
+++ net/res_send.c 28 Dec 2003 16:12:46 -0000
@@ -389,7 +389,7 @@
_DIAGASSERT(buf != NULL);
_DIAGASSERT(ans != NULL);
- if ((_res.options & RES_INIT) == 0 && res_init() == -1) {
+ if (res_init() == -1) {
/* errno should have been set by res_init() in this case. */
return (-1);
}
Index: net/sethostent.c
===================================================================
RCS file: /home/bouyer/anoncvs/cvs/src/lib/libc/net/sethostent.c,v
retrieving revision 1.12
diff -u -r1.12 sethostent.c
--- net/sethostent.c 7 Aug 2003 16:43:15 -0000 1.12
+++ net/sethostent.c 28 Dec 2003 16:12:57 -0000
@@ -58,7 +58,7 @@
int stayopen;
{
- if ((_res.options & RES_INIT) == 0 && res_init() == -1)
+ if (res_init() == -1)
return;
if (stayopen)
_res.options |= RES_STAYOPEN | RES_USEVC;
--6c2NcOVqGQ03X4Wi--