[libvirt] PATCH: Allow virtual networks to survive daemon restarts

Daniel P. Berrange berrange at redhat.com
Wed Jan 14 12:44:50 UTC 2009


On Wed, Jan 14, 2009 at 08:55:46AM +0000, Mark McLoughlin wrote:
> On Tue, 2009-01-13 at 20:49 +0000, Daniel P. Berrange wrote:
> > Currently when we shutdown  the virtual networks are all shutdown too.
> > This is less than useful if we're letting guest VMs hang around post
> > shutdown of libvirtd, because it means we're tearing their network
> > connection out from under them. This patch fixes that allowing networks
> > to survive restarts, and be re-detected next time around.
> > 
> > When starting a virtual network we write the live config into
> > 
> >   /var/lib/libvirt/network/$NAME.xml
> > 
> > This is because the bridge device name is potentially auto-generated
> > and we need to keep track of that
> > 
> > We change dnsmasq args to include an explicit pidfile location
> > 
> >   /var/run/libvirt/network/$NAME.pid
> > 
> > and also tell it to put itself into the background - ie daemonize. This
> > is because we want dnsmasq to survive the daemon.
> > 
> > Now, when libvirtd starts up it
> > 
> >  - Looks for the live config, and if found loads it. 
> >  - Calls a new method brHasBridge() to see if its desired bridge
> >    actually exists (and thus whether the network is running).
> >    If it exists,the network is marked active
> >  - If DHCP is configured, then reads the dnsmasq PIDfile, and sends
> >    kill($PID, 0) to check if the process is actually alive
> > 
> > In addition I cleanup the network code to remove the configFile and
> > autostartLink fields in virNetworkObjPtr, so it matches virDomaiObjPtr
> > usage.
> > 
> > With all this applied you can now restart the daemon, and virbr0 is
> > left happily running.
> 
> It all sounds and looks good to me. Some comments below, but nothing
> major.

Here's an updated patch with all your comments incorporated.

 libvirt.spec.in          |   31 ++++-
 src/Makefile.am          |   24 +++-
 src/bridge.c             |   37 ++++++
 src/bridge.h             |    2 
 src/libvirt_bridge.syms  |    1 
 src/libvirt_private.syms |    2 
 src/network_conf.c       |  130 +++++++++++++----------
 src/network_conf.h       |   18 ++-
 src/network_driver.c     |  260 +++++++++++++++++++++++++++++++++++------------
 9 files changed, 370 insertions(+), 135 deletions(-)


Daniel

diff --git a/libvirt.spec.in b/libvirt.spec.in
--- a/libvirt.spec.in
+++ b/libvirt.spec.in
@@ -11,6 +11,7 @@
 %define with_python    0%{!?_without_python:1}
 %define with_libvirtd  0%{!?_without_libvirtd:1}
 %define with_uml       0%{!?_without_uml:1}
+%define with_network   0%{!?_without_network:1}
 
 # Xen is available only on i386 x86_64 ia64
 %ifnarch i386 i686 x86_64 ia64
@@ -207,6 +208,10 @@ of recent versions of Linux (and other O
 %define _without_uml --without-uml
 %endif
 
+%if ! %{with_network}
+%define _without_network --without-network
+%endif
+
 %configure %{?_without_xen} \
            %{?_without_qemu} \
            %{?_without_openvz} \
@@ -217,6 +222,7 @@ of recent versions of Linux (and other O
            %{?_without_python} \
            %{?_without_libvirtd} \
            %{?_without_uml} \
+           %{?_without_network} \
            --with-init-script=redhat \
            --with-qemud-pid-file=%{_localstatedir}/run/libvirt_qemud.pid \
            --with-remote-file=%{_localstatedir}/run/libvirtd.pid
@@ -232,11 +238,6 @@ rm -f $RPM_BUILD_ROOT%{_libdir}/*.la
 rm -f $RPM_BUILD_ROOT%{_libdir}/*.a
 rm -f $RPM_BUILD_ROOT%{_libdir}/python*/site-packages/*.la
 rm -f $RPM_BUILD_ROOT%{_libdir}/python*/site-packages/*.a
-install -d -m 0755 $RPM_BUILD_ROOT%{_localstatedir}/run/libvirt/
-# Default dir for disk images defined in SELinux policy
-install -d -m 0755 $RPM_BUILD_ROOT%{_localstatedir}/lib/libvirt/images/
-# Default dir for kernel+initrd images defnied in SELinux policy
-install -d -m 0755 $RPM_BUILD_ROOT%{_localstatedir}/lib/libvirt/boot/
 
 %if %{with_qemu}
 # We don't want to install /etc/libvirt/qemu/networks in the main %files list
@@ -338,11 +339,31 @@ fi
 %endif
 
 %dir %{_localstatedir}/run/libvirt/
+
 %dir %{_localstatedir}/lib/libvirt/
 %dir %attr(0700, root, root) %{_localstatedir}/lib/libvirt/images/
 %dir %attr(0700, root, root) %{_localstatedir}/lib/libvirt/boot/
 
 %if %{with_qemu}
+%dir %{_localstatedir}/run/libvirt/qemu/
+%dir %attr(0700, root, root) %{_localstatedir}/lib/libvirt/qemu/
+%endif
+%if %{with_lxc}
+%dir %{_localstatedir}/run/libvirt/lxc/
+%dir %attr(0700, root, root) %{_localstatedir}/lib/libvirt/lxc/
+%endif
+%if %{with_uml}
+%dir %{_localstatedir}/run/libvirt/uml/
+%dir %attr(0700, root, root) %{_localstatedir}/lib/libvirt/uml/
+%endif
+%if %{with_network}
+%dir %{_localstatedir}/run/libvirt/network/
+%dir %attr(0700, root, root) %{_localstatedir}/lib/libvirt/network/
+%dir %attr(0700, root, root) %{_localstatedir}/lib/libvirt/iptables/filter/
+%dir %attr(0700, root, root) %{_localstatedir}/lib/libvirt/iptables/nat/
+%endif
+
+%if %{with_qemu}
 %{_datadir}/augeas/lenses/libvirtd_qemu.aug
 %{_datadir}/augeas/lenses/tests/test_libvirtd_qemu.aug
 %endif
diff --git a/src/Makefile.am b/src/Makefile.am
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -589,9 +589,29 @@ endif
 endif
 EXTRA_DIST += $(LXC_CONTROLLER_SOURCES)
 
-# Create the /var/cache/libvirt directory when installing.
 install-exec-local:
-	$(MKDIR_P) $(DESTDIR)$(localstatedir)/cache/libvirt
+	$(MKDIR_P) "$(DESTDIR)$(localstatedir)/cache/libvirt"
+	$(MKDIR_P) "$(DESTDIR)$(localstatedir)/lib/libvirt/images"
+	$(MKDIR_P) "$(DESTDIR)$(localstatedir)/lib/libvirt/boot"
+if WITH_QEMU
+	$(MKDIR_P) "$(DESTDIR)$(localstatedir)/lib/libvirt/qemu"
+	$(MKDIR_P) "$(DESTDIR)$(localstatedir)/run/libvirt/qemu"
+endif
+if WITH_LXC
+	$(MKDIR_P) "$(DESTDIR)$(localstatedir)/lib/libvirt/lxc"
+	$(MKDIR_P) "$(DESTDIR)$(localstatedir)/run/libvirt/lxc"
+endif
+if WITH_UML
+	$(MKDIR_P) "$(DESTDIR)$(localstatedir)/lib/libvirt/uml"
+	$(MKDIR_P) "$(DESTDIR)$(localstatedir)/run/libvirt/uml"
+endif
+if WITH_NETWORK
+	$(MKDIR_P) "$(DESTDIR)$(localstatedir)/lib/libvirt/iptables/filter"
+	$(MKDIR_P) "$(DESTDIR)$(localstatedir)/lib/libvirt/iptables/nat"
+	$(MKDIR_P) "$(DESTDIR)$(localstatedir)/lib/libvirt/network"
+	$(MKDIR_P) "$(DESTDIR)$(localstatedir)/run/libvirt/network"
+endif
+
 
 CLEANFILES = *.gcov .libs/*.gcda .libs/*.gcno *.gcno *.gcda
 DISTCLEANFILES = $(BUILT_SOURCES)
diff --git a/src/bridge.c b/src/bridge.c
--- a/src/bridge.c
+++ b/src/bridge.c
@@ -163,6 +163,43 @@ int brAddBridge (brControl *ctl ATTRIBUT
 }
 #endif
 
+#ifdef SIOCBRDELBR
+int
+brHasBridge(brControl *ctl,
+            const char *name)
+{
+    struct ifreq ifr;
+    int len;
+
+    if (!ctl || !name) {
+        errno = EINVAL;
+        return -1;
+    }
+
+    if ((len = strlen(name)) >= BR_IFNAME_MAXLEN) {
+        errno = EINVAL;
+        return -1;
+    }
+
+    memset(&ifr, 0, sizeof(struct ifreq));
+
+    strncpy(ifr.ifr_name, name, len);
+    ifr.ifr_name[len] = '\0';
+
+    if (ioctl(ctl->fd, SIOCGIFFLAGS, &ifr))
+        return -1;
+
+    return 0;
+}
+#else
+int
+brHasBridge(brControl *ctl,
+            const char *name)
+{
+    return EINVAL;
+}
+#endif
+
 /**
  * brDeleteBridge:
  * @ctl: bridge control pointer
diff --git a/src/bridge.h b/src/bridge.h
--- a/src/bridge.h
+++ b/src/bridge.h
@@ -50,6 +50,8 @@ int     brAddBridge             (brContr
                                  char **name);
 int     brDeleteBridge          (brControl *ctl,
                                  const char *name);
+int     brHasBridge             (brControl *ctl,
+                                 const char *name);
 
 int     brAddInterface          (brControl *ctl,
                                  const char *bridge,
diff --git a/src/libvirt_bridge.syms b/src/libvirt_bridge.syms
--- a/src/libvirt_bridge.syms
+++ b/src/libvirt_bridge.syms
@@ -9,6 +9,7 @@ brAddBridge;
 brAddInterface;
 brAddTap;
 brDeleteBridge;
+brHasBridge;
 brInit;
 brSetEnableSTP;
 brSetForwardDelay;
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -190,6 +190,7 @@ virFree;
 
 # network_conf.h
 virNetworkAssignDef;
+virNetworkConfigFile;
 virNetworkDefFormat;
 virNetworkDefFree;
 virNetworkDefParseFile;
@@ -202,6 +203,7 @@ virNetworkLoadAllConfigs;
 virNetworkObjListFree;
 virNetworkDefParseNode;
 virNetworkRemoveInactive;
+virNetworkSaveConfigXML;
 virNetworkSaveConfig;
 virNetworkObjLock;
 virNetworkObjUnlock;
diff --git a/src/network_conf.c b/src/network_conf.c
--- a/src/network_conf.c
+++ b/src/network_conf.c
@@ -125,9 +125,6 @@ void virNetworkObjFree(virNetworkObjPtr 
     virNetworkDefFree(net->def);
     virNetworkDefFree(net->newDef);
 
-    VIR_FREE(net->configFile);
-    VIR_FREE(net->autostartLink);
-
     virMutexDestroy(&net->lock);
 
     VIR_FREE(net);
@@ -641,31 +638,17 @@ char *virNetworkDefFormat(virConnectPtr 
     return NULL;
 }
 
-int virNetworkSaveConfig(virConnectPtr conn,
-                         const char *configDir,
-                         const char *autostartDir,
-                         virNetworkObjPtr net)
+int virNetworkSaveXML(virConnectPtr conn,
+                      const char *configDir,
+                      virNetworkDefPtr def,
+                      const char *xml)
 {
-    char *xml;
+    char *configFile = NULL;
     int fd = -1, ret = -1;
     size_t towrite;
     int err;
 
-    if (!net->configFile &&
-        virAsprintf(&net->configFile, "%s/%s.xml",
-                    configDir, net->def->name) < 0) {
-        virNetworkReportError(conn, VIR_ERR_NO_MEMORY, NULL);
-        goto cleanup;
-    }
-    if (!net->autostartLink &&
-        virAsprintf(&net->autostartLink, "%s/%s.xml",
-                    autostartDir, net->def->name) < 0) {
-        virNetworkReportError(conn, VIR_ERR_NO_MEMORY, NULL);
-        goto cleanup;
-    }
-
-    if (!(xml = virNetworkDefFormat(conn,
-                                    net->newDef ? net->newDef : net->def)))
+    if ((configFile = virNetworkConfigFile(conn, configDir, def->name)) == NULL)
         goto cleanup;
 
     if ((err = virFileMakePath(configDir))) {
@@ -675,19 +658,12 @@ int virNetworkSaveConfig(virConnectPtr c
         goto cleanup;
     }
 
-    if ((err = virFileMakePath(autostartDir))) {
-        virReportSystemError(conn, err,
-                             _("cannot create autostart directory '%s'"),
-                             autostartDir);
-        goto cleanup;
-    }
-
-    if ((fd = open(net->configFile,
+    if ((fd = open(configFile,
                    O_WRONLY | O_CREAT | O_TRUNC,
                    S_IRUSR | S_IWUSR )) < 0) {
         virReportSystemError(conn, errno,
                              _("cannot create config file '%s'"),
-                             net->configFile);
+                             configFile);
         goto cleanup;
     }
 
@@ -695,48 +671,63 @@ int virNetworkSaveConfig(virConnectPtr c
     if (safewrite(fd, xml, towrite) < 0) {
         virReportSystemError(conn, errno,
                              _("cannot write config file '%s'"),
-                             net->configFile);
+                             configFile);
         goto cleanup;
     }
 
     if (close(fd) < 0) {
         virReportSystemError(conn, errno,
                              _("cannot save config file '%s'"),
-                             net->configFile);
+                             configFile);
         goto cleanup;
     }
 
     ret = 0;
 
  cleanup:
-    VIR_FREE(xml);
     if (fd != -1)
         close(fd);
 
+    VIR_FREE(configFile);
+
     return ret;
 }
 
+int virNetworkSaveConfig(virConnectPtr conn,
+                         const char *configDir,
+                         virNetworkDefPtr def)
+{
+    int ret = -1;
+    char *xml;
+
+    if (!(xml = virNetworkDefFormat(conn, def)))
+        goto cleanup;
+
+    if (virNetworkSaveXML(conn, configDir, def, xml))
+        goto cleanup;
+
+    ret = 0;
+cleanup:
+    VIR_FREE(xml);
+    return ret;
+}
+
+
 virNetworkObjPtr virNetworkLoadConfig(virConnectPtr conn,
                                       virNetworkObjListPtr nets,
                                       const char *configDir,
                                       const char *autostartDir,
-                                      const char *file)
+                                      const char *name)
 {
     char *configFile = NULL, *autostartLink = NULL;
     virNetworkDefPtr def = NULL;
     virNetworkObjPtr net;
     int autostart;
 
-    if (virAsprintf(&configFile, "%s/%s",
-                    configDir, file) < 0) {
-        virNetworkReportError(conn, VIR_ERR_NO_MEMORY, NULL);
+    if ((configFile = virNetworkConfigFile(conn, configDir, name)) == NULL)
         goto error;
-    }
-    if (virAsprintf(&autostartLink, "%s/%s",
-                    autostartDir, file) < 0) {
-        virNetworkReportError(conn, VIR_ERR_NO_MEMORY, NULL);
+    if ((autostartLink = virNetworkConfigFile(conn, autostartDir, name)) == NULL)
         goto error;
-    }
 
     if ((autostart = virFileLinkPointsTo(autostartLink, configFile)) < 0)
         goto error;
@@ -744,7 +735,7 @@ virNetworkObjPtr virNetworkLoadConfig(vi
     if (!(def = virNetworkDefParseFile(conn, configFile)))
         goto error;
 
-    if (!virFileMatchesNameSuffix(file, def->name, ".xml")) {
+    if (!STREQ(name, def->name)) {
         virNetworkReportError(conn, VIR_ERR_INTERNAL_ERROR,
                               _("Network config filename '%s'"
                                 " does not match network name '%s'"),
@@ -755,10 +746,11 @@ virNetworkObjPtr virNetworkLoadConfig(vi
     if (!(net = virNetworkAssignDef(conn, nets, def)))
         goto error;
 
-    net->configFile = configFile;
-    net->autostartLink = autostartLink;
     net->autostart = autostart;
 
+    VIR_FREE(configFile);
+    VIR_FREE(autostartLink);
+
     return net;
 
 error:
@@ -791,7 +783,7 @@ int virNetworkLoadAllConfigs(virConnectP
         if (entry->d_name[0] == '.')
             continue;
 
-        if (!virFileHasSuffix(entry->d_name, ".xml"))
+        if (!virFileStripSuffix(entry->d_name, ".xml"))
             continue;
 
         /* NB: ignoring errors, so one malformed config doesn't
@@ -811,27 +803,51 @@ int virNetworkLoadAllConfigs(virConnectP
 }
 
 int virNetworkDeleteConfig(virConnectPtr conn,
+                           const char *configDir,
+                           const char *autostartDir,
                            virNetworkObjPtr net)
 {
-    if (!net->configFile || !net->autostartLink) {
-        virNetworkReportError(conn, VIR_ERR_INTERNAL_ERROR,
-                              _("no config file for %s"), net->def->name);
-        return -1;
-    }
+    char *configFile = NULL;
+    char *autostartLink = NULL;
+
+    if ((configFile = virNetworkConfigFile(conn, configDir, net->def->name)) == NULL)
+        goto error;
+    if ((autostartLink = virNetworkConfigFile(conn, autostartDir, net->def->name)) == NULL)
+        goto error;
 
     /* Not fatal if this doesn't work */
-    unlink(net->autostartLink);
+    unlink(autostartLink);
 
-    if (unlink(net->configFile) < 0) {
+    if (unlink(configFile) < 0) {
         virReportSystemError(conn, errno,
                              _("cannot remove config file '%s'"),
-                             net->configFile);
-        return -1;
+                             configFile);
+        goto error;
     }
 
     return 0;
+
+error:
+    VIR_FREE(configFile);
+    VIR_FREE(autostartLink);
+    return -1;
 }
 
+char *virNetworkConfigFile(virConnectPtr conn,
+                           const char *dir,
+                           const char *name)
+{
+    char *ret = NULL;
+
+    if (virAsprintf(&ret, "%s/%s.xml", dir, name) < 0) {
+        virNetworkReportError(conn, VIR_ERR_NO_MEMORY, NULL);
+        return NULL;
+    }
+
+    return ret;
+}
+
+
 void virNetworkObjLock(virNetworkObjPtr obj)
 {
     virMutexLock(&obj->lock);
diff --git a/src/network_conf.h b/src/network_conf.h
--- a/src/network_conf.h
+++ b/src/network_conf.h
@@ -90,9 +90,6 @@ struct _virNetworkObj {
     unsigned int autostart : 1;
     unsigned int persistent : 1;
 
-    char *configFile;    /* Persistent config file path */
-    char *autostartLink; /* Symlink path for autostart */
-
     virNetworkDefPtr def; /* The current definition */
     virNetworkDefPtr newDef; /* New definition to activate at shutdown */
 };
@@ -139,10 +136,14 @@ char *virNetworkDefFormat(virConnectPtr 
                           const virNetworkDefPtr def);
 
 
+int virNetworkSaveXML(virConnectPtr conn,
+                      const char *configDir,
+                      virNetworkDefPtr def,
+                      const char *xml);
+
 int virNetworkSaveConfig(virConnectPtr conn,
                          const char *configDir,
-                         const char *autostartDir,
-                         virNetworkObjPtr net);
+                         virNetworkDefPtr def);
 
 virNetworkObjPtr virNetworkLoadConfig(virConnectPtr conn,
                                       virNetworkObjListPtr nets,
@@ -156,8 +157,15 @@ int virNetworkLoadAllConfigs(virConnectP
                              const char *autostartDir);
 
 int virNetworkDeleteConfig(virConnectPtr conn,
+                           const char *configDir,
+                           const char *autostartDir,
                            virNetworkObjPtr net);
 
+char *virNetworkConfigFile(virConnectPtr conn,
+                           const char *dir,
+                           const char *name);
+
+
 void virNetworkObjLock(virNetworkObjPtr obj);
 void virNetworkObjUnlock(virNetworkObjPtr obj);
 
diff --git a/src/network_driver.c b/src/network_driver.c
--- a/src/network_driver.c
+++ b/src/network_driver.c
@@ -57,6 +57,8 @@
 #include "iptables.h"
 #include "bridge.h"
 
+#define NETWORK_PID_DIR LOCAL_STATE_DIR "/run/libvirt/network"
+#define NETWORK_STATE_DIR LOCAL_STATE_DIR "/lib/libvirt/network"
 
 #define VIR_FROM_THIS VIR_FROM_NETWORK
 
@@ -106,6 +108,64 @@ static struct network_driver *driverStat
 
 
 static void
+networkFindActiveConfigs(struct network_driver *driver) {
+    unsigned int i;
+
+    for (i = 0 ; i < driver->networks.count ; i++) {
+        virNetworkObjPtr obj = driver->networks.objs[i];
+        virNetworkDefPtr tmp;
+        char *config;
+
+        virNetworkObjLock(obj);
+
+        if ((config = virNetworkConfigFile(NULL,
+                                           NETWORK_STATE_DIR,
+                                           obj->def->name)) == NULL) {
+            virNetworkObjUnlock(obj);
+            continue;
+        }
+
+        if (access(config, R_OK) < 0) {
+            VIR_FREE(config);
+            virNetworkObjUnlock(obj);
+            continue;
+        }
+
+        /* Try and load the live config */
+        tmp = virNetworkDefParseFile(NULL, config);
+        VIR_FREE(config);
+        if (tmp) {
+            obj->newDef = obj->def;
+            obj->def = tmp;
+        }
+
+        /* If bridge exists, then mark it active */
+        if (obj->def->bridge &&
+            brHasBridge(driver->brctl, obj->def->bridge) == 0) {
+            obj->active = 1;
+
+            /* Finally try and read dnsmasq pid if any DHCP ranges are set */
+            if (obj->def->nranges &&
+                virFileReadPid(NETWORK_PID_DIR, obj->def->name,
+                               &obj->dnsmasqPid) == 0) {
+
+                /* Check its still alive */
+                if (kill(obj->dnsmasqPid, 0) != 0)
+                    obj->dnsmasqPid = -1;
+
+                /* XXX ideally we'd check this was actually
+                 * the dnsmasq process, not a stale pid file
+                 * with someone else's process. But how ?
+                 */
+            }
+        }
+
+        virNetworkObjUnlock(obj);
+    }
+}
+
+
+static void
 networkAutostartConfigs(struct network_driver *driver) {
     unsigned int i;
 
@@ -132,6 +192,7 @@ static int
 networkStartup(void) {
     uid_t uid = geteuid();
     char *base = NULL;
+    int err;
 
     if (VIR_ALLOC(driverState) < 0)
         goto error;
@@ -181,12 +242,26 @@ networkStartup(void) {
 
     VIR_FREE(base);
 
+    if ((err = brInit(&driverState->brctl))) {
+        virReportSystemError(NULL, err, "%s",
+                             _("cannot initialize bridge support"));
+        goto error;
+    }
+
+    if (!(driverState->iptables = iptablesContextNew())) {
+        networkReportError(NULL, NULL, NULL, VIR_ERR_NO_MEMORY,
+                           "%s", _("failed to allocate space for IP tables support"));
+        goto error;
+    }
+
+
     if (virNetworkLoadAllConfigs(NULL,
                                  &driverState->networks,
                                  driverState->networkConfigDir,
                                  driverState->networkAutostartDir) < 0)
         goto error;
 
+    networkFindActiveConfigs(driverState);
     networkAutostartConfigs(driverState);
 
     networkDriverUnlock(driverState);
@@ -269,23 +344,11 @@ networkActive(void) {
  */
 static int
 networkShutdown(void) {
-    unsigned int i;
-
     if (!driverState)
         return -1;
 
     networkDriverLock(driverState);
 
-    /* shutdown active networks */
-    for (i = 0 ; i < driverState->networks.count ; i++) {
-        virNetworkObjPtr net = driverState->networks.objs[i];
-        virNetworkObjLock(net);
-        if (virNetworkIsActive(net))
-            networkShutdownNetworkDaemon(NULL, driverState,
-                                         driverState->networks.objs[i]);
-        virNetworkObjUnlock(net);
-    }
-
     /* free inactive networks */
     virNetworkObjListFree(&driverState->networks);
 
@@ -309,23 +372,42 @@ networkShutdown(void) {
 
 static int
 networkBuildDnsmasqArgv(virConnectPtr conn,
-                      virNetworkObjPtr network,
-                      const char ***argv) {
+                        virNetworkObjPtr network,
+                        const char *pidfile,
+                        const char ***argv) {
     int i, len, r;
-    char buf[PATH_MAX];
+    char *pidfileArg;
+    char buf[1024];
+
+    /*
+     * NB, be careful about syntax for dnsmasq options in long format
+     *
+     * If the flag has a mandatory argument, it can be given using
+     * either syntax:
+     *
+     *     --foo bar
+     *     --foo=bar
+     *
+     * If the flag has a optional argument, it *must* be given using
+     * the syntax:
+     *
+     *     --foo=bar
+     *
+     * It is hard to determine whether a flag is optional or not,
+     * without reading the dnsmasq source :-( The manpages is not
+     * very explicit on this
+     */
 
     len =
         1 + /* dnsmasq */
-        1 + /* --keep-in-foreground */
         1 + /* --strict-order */
         1 + /* --bind-interfaces */
         (network->def->domain?2:0) + /* --domain name */
-        2 + /* --pid-file "" */
+        2 + /* --pid-file /var/run/libvirt/network/$NAME.pid */
         2 + /* --conf-file "" */
         /*2 + *//* --interface virbr0 */
         2 + /* --except-interface lo */
         2 + /* --listen-address 10.0.0.1 */
-        1 + /* --dhcp-leasefile=path */
         (2 * network->def->nranges) + /* --dhcp-range 10.0.0.2,10.0.0.254 */
         /*  --dhcp-host 01:23:45:67:89:0a,hostname,10.0.0.3 */
         (2 * network->def->nhosts) +
@@ -339,11 +421,13 @@ networkBuildDnsmasqArgv(virConnectPtr co
             goto no_memory;          \
     } while (0)
 
+#define APPEND_ARG_LIT(v, n, s) \
+        (v)[(n)] = s
+
     i = 0;
 
     APPEND_ARG(*argv, i++, DNSMASQ);
 
-    APPEND_ARG(*argv, i++, "--keep-in-foreground");
     /*
      * Needed to ensure dnsmasq uses same algorithm for processing
      * multiple namedriver entries in /etc/resolv.conf as GLibC.
@@ -356,10 +440,11 @@ networkBuildDnsmasqArgv(virConnectPtr co
        APPEND_ARG(*argv, i++, network->def->domain);
     }
 
-    APPEND_ARG(*argv, i++, "--pid-file");
-    APPEND_ARG(*argv, i++, "");
+    if (virAsprintf(&pidfileArg, "--pid-file=%s", pidfile) < 0)
+        goto no_memory;
+    APPEND_ARG_LIT(*argv, i++, pidfileArg);
 
-    APPEND_ARG(*argv, i++, "--conf-file");
+    APPEND_ARG(*argv, i++, "--conf-file=");
     APPEND_ARG(*argv, i++, "");
 
     /*
@@ -377,15 +462,6 @@ networkBuildDnsmasqArgv(virConnectPtr co
     APPEND_ARG(*argv, i++, "--except-interface");
     APPEND_ARG(*argv, i++, "lo");
 
-    /*
-     * NB, dnsmasq command line arg bug means we need to
-     * use a single arg '--dhcp-leasefile=path' rather than
-     * two separate args in '--dhcp-leasefile path' style
-     */
-    snprintf(buf, sizeof(buf), "--dhcp-leasefile=%s/lib/libvirt/dhcp-%s.leases",
-             LOCAL_STATE_DIR, network->def->name);
-    APPEND_ARG(*argv, i++, buf);
-
     for (r = 0 ; r < network->def->nranges ; r++) {
         snprintf(buf, sizeof(buf), "%s,%s",
                  network->def->ranges[r].start,
@@ -434,7 +510,10 @@ dhcpStartDhcpDaemon(virConnectPtr conn,
                     virNetworkObjPtr network)
 {
     const char **argv;
-    int ret, i;
+    char *pidfile;
+    int ret = -1, i, err;
+
+    network->dnsmasqPid = -1;
 
     if (network->def->ipAddress == NULL) {
         networkReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
@@ -442,13 +521,49 @@ dhcpStartDhcpDaemon(virConnectPtr conn,
         return -1;
     }
 
+    if ((err = virFileMakePath(NETWORK_PID_DIR)) < 0) {
+        virReportSystemError(conn, err,
+                             _("cannot create directory %s"),
+                             NETWORK_PID_DIR);
+        return -1;
+    }
+    if ((err = virFileMakePath(NETWORK_STATE_DIR)) < 0) {
+        virReportSystemError(conn, err,
+                             _("cannot create directory %s"),
+                             NETWORK_STATE_DIR);
+        return -1;
+    }
+
+    if (!(pidfile = virFilePid(NETWORK_PID_DIR, network->def->name))) {
+        virReportOOMError(conn);
+        return -1;
+    }
+
     argv = NULL;
-    if (networkBuildDnsmasqArgv(conn, network, &argv) < 0)
+    if (networkBuildDnsmasqArgv(conn, network, pidfile, &argv) < 0) {
+        VIR_FREE(pidfile);
         return -1;
+    }
 
-    ret = virExec(conn, argv, NULL, NULL,
-                  &network->dnsmasqPid, -1, NULL, NULL, VIR_EXEC_NONBLOCK);
+    if (virRun(conn, argv, NULL) < 0)
+        goto cleanup;
 
+    /*
+     * There really is no race here - when dnsmasq daemonizes,
+     * its leader process stays around until its child has
+     * actually written its pidfile. So by time virRun exits
+     * it has waitpid'd and guaranteed the proess has started
+     * and written a pid
+     */
+
+    if (virFileReadPid(NETWORK_PID_DIR, network->def->name,
+                       &network->dnsmasqPid) < 0)
+        goto cleanup;
+
+    ret = 0;
+
+cleanup:
+    VIR_FREE(pidfile);
     for (i = 0; argv[i]; i++)
         VIR_FREE(argv[i]);
     VIR_FREE(argv);
@@ -554,13 +669,6 @@ networkAddIptablesRules(virConnectPtr co
                       virNetworkObjPtr network) {
     int err;
 
-    if (!driver->iptables && !(driver->iptables = iptablesContextNew())) {
-        networkReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY,
-                     "%s", _("failed to allocate space for IP tables support"));
-        return 0;
-    }
-
-
     /* allow DHCP requests through to dnsmasq */
     if ((err = iptablesAddTcpInput(driver->iptables, network->def->bridge, 67))) {
         virReportSystemError(conn, err,
@@ -716,12 +824,6 @@ static int networkStartNetworkDaemon(vir
         return -1;
     }
 
-    if (!driver->brctl && (err = brInit(&driver->brctl))) {
-        virReportSystemError(conn, err, "%s",
-                             _("cannot initialize bridge support"));
-        return -1;
-    }
-
     if ((err = brAddBridge(driver->brctl, &network->def->bridge))) {
         virReportSystemError(conn, err,
                              _("cannot create bridge '%s'"),
@@ -729,7 +831,6 @@ static int networkStartNetworkDaemon(vir
         return -1;
     }
 
-
     if (brSetForwardDelay(driver->brctl, network->def->bridge, network->def->delay) < 0)
         goto err_delbr;
 
@@ -774,10 +875,22 @@ static int networkStartNetworkDaemon(vir
         dhcpStartDhcpDaemon(conn, network) < 0)
         goto err_delbr2;
 
+
+    /* Persist the live configuration now we have bridge info  */
+    if (virNetworkSaveConfig(conn, NETWORK_STATE_DIR, network->def) < 0) {
+        goto err_kill;
+    }
+
     network->active = 1;
 
     return 0;
 
+ err_kill:
+    if (network->dnsmasqPid > 0) {
+        kill(network->dnsmasqPid, SIGTERM);
+        network->dnsmasqPid = -1;
+    }
+
  err_delbr2:
     networkRemoveIptablesRules(driver, network);
 
@@ -798,16 +911,24 @@ static int networkStartNetworkDaemon(vir
 }
 
 
-static int networkShutdownNetworkDaemon(virConnectPtr conn ATTRIBUTE_UNUSED,
-                                      struct network_driver *driver,
-                                      virNetworkObjPtr network) {
+static int networkShutdownNetworkDaemon(virConnectPtr conn,
+                                        struct network_driver *driver,
+                                        virNetworkObjPtr network) {
     int err;
+    char *stateFile;
 
     networkLog(NETWORK_INFO, _("Shutting down network '%s'\n"), network->def->name);
 
     if (!virNetworkIsActive(network))
         return 0;
 
+    stateFile = virNetworkConfigFile(conn, NETWORK_STATE_DIR, network->def->name);
+    if (!stateFile)
+        return -1;
+
+    unlink(stateFile);
+    VIR_FREE(stateFile);
+
     if (network->dnsmasqPid > 0)
         kill(network->dnsmasqPid, SIGTERM);
 
@@ -824,13 +945,10 @@ static int networkShutdownNetworkDaemon(
                  network->def->bridge, strerror(err));
     }
 
+    /* See if its still alive and really really kill it */
     if (network->dnsmasqPid > 0 &&
-        waitpid(network->dnsmasqPid, NULL, WNOHANG) != network->dnsmasqPid) {
+        (kill(network->dnsmasqPid, 0) == 0))
         kill(network->dnsmasqPid, SIGKILL);
-        if (waitpid(network->dnsmasqPid, NULL, 0) != network->dnsmasqPid)
-            networkLog(NETWORK_WARN,
-                     "%s", _("Got unexpected pid for dnsmasq\n"));
-    }
 
     network->dnsmasqPid = -1;
     network->active = 0;
@@ -1048,8 +1166,7 @@ static virNetworkPtr networkDefine(virCo
 
     if (virNetworkSaveConfig(conn,
                              driver->networkConfigDir,
-                             driver->networkAutostartDir,
-                             network) < 0) {
+                             network->newDef ? network->newDef : network->def) < 0) {
         virNetworkRemoveInactive(&driver->networks,
                                  network);
         network = NULL;
@@ -1086,7 +1203,10 @@ static int networkUndefine(virNetworkPtr
         goto cleanup;
     }
 
-    if (virNetworkDeleteConfig(net->conn, network) < 0)
+    if (virNetworkDeleteConfig(net->conn,
+                               driver->networkConfigDir,
+                               driver->networkAutostartDir,
+                               network) < 0)
         goto cleanup;
 
     virNetworkRemoveInactive(&driver->networks,
@@ -1140,7 +1260,7 @@ static int networkDestroy(virNetworkPtr 
     }
 
     ret = networkShutdownNetworkDaemon(net->conn, driver, network);
-    if (!network->configFile) {
+    if (!network->persistent) {
         virNetworkRemoveInactive(&driver->networks,
                                  network);
         network = NULL;
@@ -1233,9 +1353,10 @@ cleanup:
 }
 
 static int networkSetAutostart(virNetworkPtr net,
-                             int autostart) {
+                               int autostart) {
     struct network_driver *driver = net->conn->networkPrivateData;
     virNetworkObjPtr network;
+    char *configFile = NULL, *autostartLink = NULL;
     int ret = -1;
 
     networkDriverLock(driver);
@@ -1251,6 +1372,11 @@ static int networkSetAutostart(virNetwor
     autostart = (autostart != 0);
 
     if (network->autostart != autostart) {
+        if ((configFile = virNetworkConfigFile(net->conn, driver->networkConfigDir, network->def->name)) == NULL)
+            goto cleanup;
+        if ((autostartLink = virNetworkConfigFile(net->conn, driver->networkAutostartDir, network->def->name)) == NULL)
+            goto cleanup;
+
         if (autostart) {
             int err;
 
@@ -1261,17 +1387,17 @@ static int networkSetAutostart(virNetwor
                 goto cleanup;
             }
 
-            if (symlink(network->configFile, network->autostartLink) < 0) {
+            if (symlink(configFile, autostartLink) < 0) {
                 virReportSystemError(net->conn, errno,
                                      _("Failed to create symlink '%s' to '%s'"),
-                                     network->autostartLink, network->configFile);
+                                     autostartLink, configFile);
                 goto cleanup;
             }
         } else {
-            if (unlink(network->autostartLink) < 0 && errno != ENOENT && errno != ENOTDIR) {
+            if (unlink(autostartLink) < 0 && errno != ENOENT && errno != ENOTDIR) {
                 virReportSystemError(net->conn, errno,
                                      _("Failed to delete symlink '%s'"),
-                                     network->autostartLink);
+                                     autostartLink);
                 goto cleanup;
             }
         }
@@ -1281,6 +1407,8 @@ static int networkSetAutostart(virNetwor
     ret = 0;
 
 cleanup:
+    VIR_FREE(configFile);
+    VIR_FREE(autostartLink);
     if (network)
         virNetworkObjUnlock(network);
     return ret;

-- 
|: Red Hat, Engineering, London   -o-   http://people.redhat.com/berrange/ :|
|: http://libvirt.org  -o-  http://virt-manager.org  -o-  http://ovirt.org :|
|: http://autobuild.org       -o-         http://search.cpan.org/~danberr/ :|
|: GnuPG: 7D3B9505  -o-  F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|




More information about the libvir-list mailing list