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--