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 :| -- Libvir-list mailing list Libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list