[glib] Re-run res_init() when resolv.conf changes



commit 021dd960cf9c02b0ea59cbfa1db603d3f9a467f0
Author: Dan Winship <danw gnome org>
Date:   Wed Aug 19 12:07:53 2009 -0400

    Re-run res_init() when resolv.conf changes
    
    libc caches the contents of resolv.conf, so if it changes (eg, because
    the network state changed), we need re-run res_init().
    
    http://bugzilla.gnome.org/show_bug.cgi?id=584246

 gio/gnetworkingprivate.h |    4 ++
 gio/gresolver.c          |   68 +++++++++++++++++++++++++++++++++++++++++++
 gio/gresolver.h          |    9 +++++-
 gio/gunixresolver.c      |   72 +++++++++++++++++++++++++++++++--------------
 gio/tests/resolver.c     |    3 ++
 5 files changed, 132 insertions(+), 24 deletions(-)
---
diff --git a/gio/gnetworkingprivate.h b/gio/gnetworkingprivate.h
index b6a811a..050b3a2 100644
--- a/gio/gnetworkingprivate.h
+++ b/gio/gnetworkingprivate.h
@@ -56,6 +56,10 @@
 #include <sys/socket.h>
 #include <sys/un.h>
 
+#ifndef _PATH_RESCONF
+#define _PATH_RESCONF "/etc/resolv.conf"
+#endif
+
 #endif
 
 G_BEGIN_DECLS
diff --git a/gio/gresolver.c b/gio/gresolver.c
index f631a78..2b30457 100644
--- a/gio/gresolver.c
+++ b/gio/gresolver.c
@@ -34,6 +34,7 @@
 
 #ifdef G_OS_UNIX
 #include "gunixresolver.h"
+#include <sys/stat.h>
 #endif
 #ifdef G_OS_WIN32
 #include "gwin32resolver.h"
@@ -58,6 +59,21 @@
  * making it easy to connect to a remote host/service.
  */
 
+enum {
+  RELOAD,
+  LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+struct _GResolverPrivate {
+#ifdef G_OS_UNIX
+  time_t resolv_conf_timestamp;
+#else
+  int dummy;
+#endif
+};
+
 /**
  * GResolver:
  *
@@ -71,6 +87,8 @@ g_resolver_class_init (GResolverClass *resolver_class)
 {
   volatile GType type;
 
+  g_type_class_add_private (resolver_class, sizeof (GResolverPrivate));
+
   /* Make sure _g_networking_init() has been called */
   type = g_inet_address_get_type ();
 
@@ -84,11 +102,37 @@ g_resolver_class_init (GResolverClass *resolver_class)
    */
   _g_resolver_addrinfo_hints.ai_socktype = SOCK_STREAM;
   _g_resolver_addrinfo_hints.ai_protocol = IPPROTO_TCP;
+
+  /**
+   * GResolver::reload:
+   * @resolver: a #GResolver
+   *
+   * Emitted when the resolver notices that the system resolver
+   * configuration has changed.
+   **/
+  signals[RELOAD] =
+    g_signal_new (I_("reload"),
+		  G_TYPE_RESOLVER,
+		  G_SIGNAL_RUN_LAST,
+		  G_STRUCT_OFFSET (GResolverClass, reload),
+		  NULL, NULL,
+		  g_cclosure_marshal_VOID__VOID,
+		  G_TYPE_NONE, 0);
 }
 
 static void
 g_resolver_init (GResolver *resolver)
 {
+#ifdef G_OS_UNIX
+  struct stat st;
+#endif
+
+  resolver->priv = G_TYPE_INSTANCE_GET_PRIVATE (resolver, G_TYPE_RESOLVER, GResolverPrivate);
+
+#ifdef G_OS_UNIX
+  if (stat (_PATH_RESCONF, &st) == 0)
+    resolver->priv->resolv_conf_timestamp = st.st_mtime;
+#endif
 }
 
 static GResolver *default_resolver;
@@ -150,6 +194,24 @@ g_resolver_set_default (GResolver *resolver)
 }
 
 
+static void
+g_resolver_maybe_reload (GResolver *resolver)
+{
+#ifdef G_OS_UNIX
+  struct stat st;
+
+  if (stat (_PATH_RESCONF, &st) == 0)
+    {
+      if (st.st_mtime != resolver->priv->resolv_conf_timestamp)
+        {
+          resolver->priv->resolv_conf_timestamp = st.st_mtime;
+          res_init ();
+          g_signal_emit (resolver, signals[RELOAD], 0);
+        }
+    }
+#endif
+}
+
 /**
  * g_resolver_lookup_by_name:
  * @resolver: a #GResolver
@@ -205,6 +267,7 @@ g_resolver_lookup_by_name (GResolver     *resolver,
   if (g_hostname_is_non_ascii (hostname))
     hostname = ascii_hostname = g_hostname_to_ascii (hostname);
 
+  g_resolver_maybe_reload (resolver);
   addrs = G_RESOLVER_GET_CLASS (resolver)->
     lookup_by_name (resolver, hostname, cancellable, error);
 
@@ -259,6 +322,7 @@ g_resolver_lookup_by_name_async (GResolver           *resolver,
   if (g_hostname_is_non_ascii (hostname))
     hostname = ascii_hostname = g_hostname_to_ascii (hostname);
 
+  g_resolver_maybe_reload (resolver);
   G_RESOLVER_GET_CLASS (resolver)->
     lookup_by_name_async (resolver, hostname, cancellable, callback, user_data);
 
@@ -363,6 +427,7 @@ g_resolver_lookup_by_address (GResolver     *resolver,
   g_return_val_if_fail (G_IS_RESOLVER (resolver), NULL);
   g_return_val_if_fail (G_IS_INET_ADDRESS (address), NULL);
 
+  g_resolver_maybe_reload (resolver);
   return G_RESOLVER_GET_CLASS (resolver)->
     lookup_by_address (resolver, address, cancellable, error);
 }
@@ -391,6 +456,7 @@ g_resolver_lookup_by_address_async (GResolver           *resolver,
   g_return_if_fail (G_IS_RESOLVER (resolver));
   g_return_if_fail (G_IS_INET_ADDRESS (address));
 
+  g_resolver_maybe_reload (resolver);
   G_RESOLVER_GET_CLASS (resolver)->
     lookup_by_address_async (resolver, address, cancellable, callback, user_data);
 }
@@ -504,6 +570,7 @@ g_resolver_lookup_service (GResolver     *resolver,
 
   rrname = g_resolver_get_service_rrname (service, protocol, domain);
 
+  g_resolver_maybe_reload (resolver);
   targets = G_RESOLVER_GET_CLASS (resolver)->
     lookup_service (resolver, rrname, cancellable, error);
 
@@ -547,6 +614,7 @@ g_resolver_lookup_service_async (GResolver           *resolver,
 
   rrname = g_resolver_get_service_rrname (service, protocol, domain);
 
+  g_resolver_maybe_reload (resolver);
   G_RESOLVER_GET_CLASS (resolver)->
     lookup_service_async (resolver, rrname, cancellable, callback, user_data);
 
diff --git a/gio/gresolver.h b/gio/gresolver.h
index 6bdbc90..1187a3f 100644
--- a/gio/gresolver.h
+++ b/gio/gresolver.h
@@ -36,15 +36,22 @@ G_BEGIN_DECLS
 #define G_IS_RESOLVER_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_RESOLVER))
 #define G_RESOLVER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_RESOLVER, GResolverClass))
 
+typedef struct _GResolverPrivate GResolverPrivate;
+typedef struct _GResolverClass   GResolverClass;
+
 struct _GResolver {
   GObject parent_instance;
 
+  GResolverPrivate *priv;
 };
 
-typedef struct _GResolverClass GResolverClass;
 struct _GResolverClass {
   GObjectClass parent_class;
 
+  /* Signals */
+  void    ( *reload)                   (GResolver            *resolver);
+
+  /* Virtual methods */
   GList * ( *lookup_by_name)           (GResolver            *resolver,
 					const gchar          *hostname,
 					GCancellable         *cancellable,
diff --git a/gio/gunixresolver.c b/gio/gunixresolver.c
index d25274f..2155c66 100644
--- a/gio/gunixresolver.c
+++ b/gio/gunixresolver.c
@@ -41,21 +41,12 @@ G_DEFINE_TYPE (GUnixResolver, g_unix_resolver, G_TYPE_THREADED_RESOLVER)
 static gboolean g_unix_resolver_watch (GIOChannel   *iochannel,
                                        GIOCondition  condition,
                                        gpointer      user_data);
+static void g_unix_resolver_reload (GResolver *resolver);
 
 static void
 g_unix_resolver_init (GUnixResolver *gur)
 {
-  gint fd;
-  GIOChannel *io;
-
-  /* FIXME: how many workers? */
-  gur->asyncns = _g_asyncns_new (2);
-
-  fd = _g_asyncns_fd (gur->asyncns);
-  io = g_io_channel_unix_new (fd);
-  gur->watch = g_io_add_watch (io, G_IO_IN | G_IO_HUP | G_IO_ERR,
-                               g_unix_resolver_watch, gur);
-  g_io_channel_unref (io);
+  g_unix_resolver_reload (G_RESOLVER (gur));
 }
 
 static void
@@ -70,6 +61,33 @@ g_unix_resolver_finalize (GObject *object)
   G_OBJECT_CLASS (g_unix_resolver_parent_class)->finalize (object);
 }
 
+static void
+g_unix_resolver_reload (GResolver *resolver)
+{
+  GUnixResolver *gur = G_UNIX_RESOLVER (resolver);
+  gint fd;
+  GIOChannel *io;
+
+  if (gur->asyncns)
+    {
+      if (_g_asyncns_getnqueries (gur->asyncns) == 0)
+        {
+          g_source_remove (gur->watch);
+          _g_asyncns_free (gur->asyncns);
+        }
+      /* else, we will free it later from g_unix_resolver_watch */
+    }
+
+  /* FIXME: how many workers? */
+  gur->asyncns = _g_asyncns_new (2);
+
+  fd = _g_asyncns_fd (gur->asyncns);
+  io = g_io_channel_unix_new (fd);
+  gur->watch = g_io_add_watch (io, G_IO_IN | G_IO_HUP | G_IO_ERR,
+                               g_unix_resolver_watch, gur->asyncns);
+  g_io_channel_unref (io);
+}
+
 /* The various request possibilities:
  *
  * 1. Synchronous: handed off to the base class (GThreadedResolver);
@@ -107,6 +125,7 @@ typedef void (*GUnixResolverFunc) (GUnixResolverRequest *);
 
 struct _GUnixResolverRequest {
   GUnixResolver *gur;
+  _g_asyncns_t *asyncns;
 
   _g_asyncns_query_t *qy;
   union {
@@ -148,6 +167,7 @@ g_unix_resolver_request_new (GUnixResolver         *gur,
 
   req = g_slice_new0 (GUnixResolverRequest);
   req->gur = g_object_ref (gur);
+  req->asyncns = gur->asyncns;
   req->qy = qy;
   req->process_func = process_func;
   req->free_func = free_func;
@@ -170,12 +190,17 @@ static void
 g_unix_resolver_request_free (GUnixResolverRequest *req)
 {
   req->free_func (req);
-  g_object_unref (req->gur);
 
   /* We don't have to free req->cancellable and req->async_result,
    * since they must already have been freed if we're here.
    */
 
+  /* Check if this was the last request remaining on an old asyncns. */
+  if (req->asyncns != req->gur->asyncns &&
+      _g_asyncns_getnqueries (req->asyncns) == 0)
+    _g_asyncns_free (req->asyncns);
+
+  g_object_unref (req->gur);
   g_slice_free (GUnixResolverRequest, req);
 }
 
@@ -204,7 +229,7 @@ request_cancelled (GCancellable *cancellable,
   GUnixResolverRequest *req = user_data;
   GError *error = NULL;
 
-  _g_asyncns_cancel (req->gur->asyncns, req->qy);
+  _g_asyncns_cancel (req->asyncns, req->qy);
   req->qy = NULL;
 
   g_cancellable_set_error_if_cancelled (cancellable, &error);
@@ -219,22 +244,22 @@ g_unix_resolver_watch (GIOChannel   *iochannel,
                        GIOCondition  condition,
                        gpointer      user_data)
 {
-  GUnixResolver *gur = user_data;
+  _g_asyncns_t *asyncns = user_data;
   _g_asyncns_query_t *qy;
   GUnixResolverRequest *req;
 
   if (condition & (G_IO_HUP | G_IO_ERR))
     {
-      /* Shouldn't happen. Should we create a new asyncns? FIXME */
-      g_warning ("asyncns died");
-      gur->watch = 0;
+      /* Will happen if we reload, and then eventually kill the old
+       * _g_asyncns_t when it's done processing requests.
+       */
       return FALSE;
     }
 
-  while (_g_asyncns_wait (gur->asyncns, FALSE) == 0 &&
-         (qy = _g_asyncns_getnext (gur->asyncns)) != NULL)
+  while (_g_asyncns_wait (asyncns, FALSE) == 0 &&
+         (qy = _g_asyncns_getnext (asyncns)) != NULL)
     {
-      req = _g_asyncns_getuserdata (gur->asyncns, qy);
+      req = _g_asyncns_getuserdata (asyncns, qy);
       req->process_func (req);
       g_unix_resolver_request_complete (req);
     }
@@ -271,7 +296,7 @@ lookup_by_name_process (GUnixResolverRequest *req)
   gint retval;
   GError *error = NULL;
 
-  retval = _g_asyncns_getaddrinfo_done (req->gur->asyncns, req->qy, &res);
+  retval = _g_asyncns_getaddrinfo_done (req->asyncns, req->qy, &res);
   req->u.name.addresses =
     _g_resolver_addresses_from_addrinfo (req->u.name.hostname,
                                          res, retval, &error);
@@ -341,7 +366,7 @@ lookup_by_address_process (GUnixResolverRequest *req)
   gint retval;
   GError *error = NULL;
 
-  retval = _g_asyncns_getnameinfo_done (req->gur->asyncns, req->qy,
+  retval = _g_asyncns_getnameinfo_done (req->asyncns, req->qy,
                                         host, sizeof (host), NULL, 0);
   req->u.address.hostname =
     _g_resolver_name_from_nameinfo (req->u.address.address,
@@ -415,7 +440,7 @@ lookup_service_process (GUnixResolverRequest *req)
   gint len, herr;
   GError *error = NULL;
 
-  len = _g_asyncns_res_done (req->gur->asyncns, req->qy, &answer);
+  len = _g_asyncns_res_done (req->asyncns, req->qy, &answer);
   if (len < 0)
     herr = h_errno;
   else
@@ -487,6 +512,7 @@ g_unix_resolver_class_init (GUnixResolverClass *unix_class)
   GResolverClass *resolver_class = G_RESOLVER_CLASS (unix_class);
   GObjectClass *object_class = G_OBJECT_CLASS (unix_class);
 
+  resolver_class->reload                   = g_unix_resolver_reload;
   resolver_class->lookup_by_name_async     = lookup_by_name_async;
   resolver_class->lookup_by_name_finish    = lookup_by_name_finish;
   resolver_class->lookup_by_address_async  = lookup_by_address_async;
diff --git a/gio/tests/resolver.c b/gio/tests/resolver.c
index 055e152..a6b4d94 100644
--- a/gio/tests/resolver.c
+++ b/gio/tests/resolver.c
@@ -284,6 +284,9 @@ start_async_lookups (char **argv, int argc)
                                            lookup_by_name_callback,
                                            argv[i]);
 	}
+
+      /* Stress-test the reloading code */
+      g_signal_emit_by_name (resolver, "reload");
     }
 }
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]