[libvirt] [PATCHv2 5/9] network: reorganize dnsmasq and radvd config file / startup

Daniel Veillard veillard at redhat.com
Tue Sep 18 07:59:13 UTC 2012


On Tue, Sep 18, 2012 at 03:39:01AM -0400, Laine Stump wrote:
> This patch splits the starting of dnsmasq and radvd into multiple
> files, and adds new networkRefreshXX() and networkRestartXX()
> functions for each. These new functions are currently commented out
> because they won't be used until the next commit, and the compile options
> require all static functions to be used.
> 
> networkRefreshXX() - rewrites any file-based config for dnsmasq/radvd,
> and sends SIGHUP to the process to make it reread its config. If the
> program isn't already running, it's just started.
> 
> networkRestartXX() - kills the given program, waits for it to exit
> (see the comments in the function networkKillDaemon()), then calls
> networkStartXX().
> 
> This commit is here mostly as a checkpoint to verify no change in
> functional behavior after refactoring networkStartXX() functions to
> fit in with these new functions.
> ---
>  src/network/bridge_driver.c | 326 ++++++++++++++++++++++++++++++++++++++------
>  1 file changed, 282 insertions(+), 44 deletions(-)
> 
> diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c
> index 8dbb050..5f7f31e 100644
> --- a/src/network/bridge_driver.c
> +++ b/src/network/bridge_driver.c
> @@ -468,6 +468,68 @@ networkShutdown(void) {
>  }
>  
>  
> +#if 0
> +/* currently unused, so it causes a build error unless we #if it out */
> +/* networkKillDaemon:
> + *
> + * kill the specified pid/name, and wait a bit to make sure it's dead.
> + */
> +static int
> +networkKillDaemon(pid_t pid, const char *daemonName, const char *networkName)
> +{
> +    int ii, ret = -1;
> +    const char *signame = "TERM";
> +
> +    /* send SIGTERM, then wait up to 3 seconds for the process to
> +     * disappear, send SIGKILL, then wait for up to another 2
> +     * seconds. If that fails, log a warning and continue, hoping
> +     * for the best.
> +     */
> +    for (ii = 0; ii < 25; ii++) {
> +        int signum = 0;
> +        if (ii == 0)
> +            signum = SIGTERM;
> +        else if (ii == 15) {
> +            signum = SIGKILL;
> +            signame = "KILL";
> +        }
> +        if (kill(pid, signum) < 0) {
> +            if (errno == ESRCH) {
> +                ret = 0;
> +            } else {
> +                char ebuf[1024];
> +                VIR_WARN("Failed to terminate %s process %d "
> +                         "for network '%s' with SIG%s: %s",
> +                         daemonName, pid, networkName, signame,
> +                         virStrerror(errno, ebuf, sizeof(ebuf)));
> +            }
> +            goto cleanup;
> +        }
> +        /* NB: since networks have no reference count like
> +         * domains, there is no safe way to unlock the network
> +         * object temporarily, and so we can't follow the
> +         * procedure used by the qemu driver of 1) unlock driver
> +         * 2) sleep, 3) add ref to object 4) unlock object, 5)
> +         * re-lock driver, 6) re-lock object. We may need to add
> +         * that functionality eventually, but for now this
> +         * function is rarely used and, at worst, leaving the
> +         * network driver locked during this loop of sleeps will
> +         * have the effect of holding up any other thread trying
> +         * to make modifications to a network for up to 5 seconds;
> +         * since modifications to networks are much less common
> +         * than modifications to domains, this seems a reasonable
> +         * tradeoff in exchange for less code disruption.
> +         */
> +        usleep(20 * 1000);
> +    }
> +    VIR_WARN("Timed out waiting after SIG%s to %s process %d "
> +             "(network '%s')",
> +             signame, daemonName, pid, networkName);
> +cleanup:
> +    return ret;
> +}
> +#endif /* #if 0 */
> +
>  static int
>  networkBuildDnsmasqHostsfile(dnsmasqContext *dctx,
>                               virNetworkIpDefPtr ipdef,
> @@ -777,6 +839,12 @@ networkStartDhcpDaemon(virNetworkObjPtr network)
>      int ret = -1;
>      dnsmasqContext *dctx = NULL;
>  
> +    if (!virNetworkDefGetIpByIndex(network->def, AF_UNSPEC, 0)) {
> +        /* no IPv6 addresses, so we don't need to run radvd */
> +        ret = 0;
> +        goto cleanup;
> +    }
> +
>      if (virFileMakePath(NETWORK_PID_DIR) < 0) {
>          virReportSystemError(errno,
>                               _("cannot create directory %s"),
> @@ -840,50 +908,87 @@ cleanup:
>      return ret;
>  }
>  
> +#if 0
> +/* currently unused, so they cause a build error unless we #if them out */
> +/* networkRefreshDhcpDaemon:
> + *  Update dnsmasq config files, then send a SIGHUP so that it rereads
> + *  them.
> + *
> + *  Returns 0 on success, -1 on failure.
> + */
>  static int
> -networkStartRadvd(virNetworkObjPtr network)
> +networkRefreshDhcpDaemon(virNetworkObjPtr network)
>  {
> -    char *pidfile = NULL;
> -    char *radvdpidbase = NULL;
> -    virBuffer configbuf = VIR_BUFFER_INITIALIZER;;
> -    char *configstr = NULL;
> -    char *configfile = NULL;
> -    virCommandPtr cmd = NULL;
>      int ret = -1, ii;
>      virNetworkIpDefPtr ipdef;
> +    dnsmasqContext *dctx = NULL;
>  
> -    network->radvdPid = -1;
> +    /* if there's no running dnsmasq, just start it */
> +    if (network->dnsmasqPid <= 0 || (kill(network->dnsmasqPid, 0) < 0))
> +        return networkStartDhcpDaemon(network);
>  
> -    if (!virFileIsExecutable(RADVD)) {
> -        virReportSystemError(errno,
> -                             _("Cannot find %s - "
> -                               "Possibly the package isn't installed"),
> -                             RADVD);
> -        goto cleanup;
> +    /* Look for first IPv4 address that has dhcp defined. */
> +    /* We support dhcp config on 1 IPv4 interface only. */
> +    for (ii = 0;
> +         (ipdef = virNetworkDefGetIpByIndex(network->def, AF_INET, ii));
> +         ii++) {
> +        if (ipdef->nranges || ipdef->nhosts)
> +            break;
>      }
> +    /* If no IPv4 addresses had dhcp info, pick the first (if there were any). */
> +    if (!ipdef)
> +        ipdef = virNetworkDefGetIpByIndex(network->def, AF_INET, 0);
>  
> -    if (virFileMakePath(NETWORK_PID_DIR) < 0) {
> -        virReportSystemError(errno,
> -                             _("cannot create directory %s"),
> -                             NETWORK_PID_DIR);
> -        goto cleanup;
> -    }
> -    if (virFileMakePath(RADVD_STATE_DIR) < 0) {
> -        virReportSystemError(errno,
> -                             _("cannot create directory %s"),
> -                             RADVD_STATE_DIR);
> -        goto cleanup;
> +    if (!ipdef) {
> +        /* no <ip> elements, so nothing to do */
> +        return 0;
>      }
>  
> -    /* construct pidfile name */
> -    if (!(radvdpidbase = networkRadvdPidfileBasename(network->def->name))) {
> -        virReportOOMError();
> +    if (!(dctx = dnsmasqContextNew(network->def->name, DNSMASQ_STATE_DIR)))
>          goto cleanup;
> -    }
> -    if (!(pidfile = virPidFileBuildPath(NETWORK_PID_DIR, radvdpidbase))) {
> -        virReportOOMError();
> +
> +    if (networkBuildDnsmasqHostsfile(dctx, ipdef, network->def->dns) < 0)
> +       goto cleanup;
> +
> +    if ((ret = dnsmasqSave(dctx)) < 0)
>          goto cleanup;
> +
> +    ret = kill(network->dnsmasqPid, SIGHUP);
> +cleanup:
> +    dnsmasqContextFree(dctx);
> +    return ret;
> +}
> +
> +/* networkRestartDhcpDaemon:
> + *
> + * kill and restart dnsmasq, in order to update any config that is on
> + * the dnsmasq commandline (and any placed in separate config files).
> + *
> + *  Returns 0 on success, -1 on failure.
> + */
> +static int
> +networkRestartDhcpDaemon(virNetworkObjPtr network)
> +{
> +    /* if there is a running dnsmasq, kill it */
> +    if (network->dnsmasqPid > 0) {
> +        networkKillDaemon(network->dnsmasqPid, "dnsmasq",
> +                          network->def->name);
> +        network->dnsmasqPid = -1;
>      }
> +    /* now start dnsmasq if it should be started */
> +    return networkStartDhcpDaemon(network);
> +}
> +#endif /* #if 0 */
> +
> +static int
> +networkRadvdConfContents(virNetworkObjPtr network, char **configstr)
> +{
> +    virBuffer configbuf = VIR_BUFFER_INITIALIZER;;
> +    int ret = -1, ii;
> +    virNetworkIpDefPtr ipdef;
> +    bool v6present = false;
> +
> +    *configstr = NULL;
>  
>      /* create radvd config file appropriate for this network */
>      virBufferAsprintf(&configbuf, "interface %s\n"
> @@ -893,12 +998,15 @@ networkStartRadvd(virNetworkObjPtr network)
>                        "  AdvOtherConfigFlag off;\n"
>                        "\n",
>                        network->def->bridge);
> +
> +    /* add a section for each IPv6 address in the config */
>      for (ii = 0;
>           (ipdef = virNetworkDefGetIpByIndex(network->def, AF_INET6, ii));
>           ii++) {
>          int prefix;
>          char *netaddr;
>  
> +        v6present = true;
>          prefix = virNetworkIpDefPrefix(ipdef);
>          if (prefix < 0) {
>              virReportError(VIR_ERR_INTERNAL_ERROR,
> @@ -919,30 +1027,118 @@ networkStartRadvd(virNetworkObjPtr network)
>          VIR_FREE(netaddr);
>      }
>  
> -    virBufferAddLit(&configbuf, "};\n");
> +    /* only create the string if we found at least one IPv6 address */
> +    if (v6present) {
> +        virBufferAddLit(&configbuf, "};\n");
>  
> -    if (virBufferError(&configbuf)) {
> -        virReportOOMError();
> -        goto cleanup;
> +        if (virBufferError(&configbuf)) {
> +            virReportOOMError();
> +            goto cleanup;
> +        }
> +        if (!(*configstr = virBufferContentAndReset(&configbuf))) {
> +            virReportOOMError();
> +            goto cleanup;
> +        }
>      }
> -    if (!(configstr = virBufferContentAndReset(&configbuf))) {
> -        virReportOOMError();
> +
> +    ret = 0;
> +cleanup:
> +    virBufferFreeAndReset(&configbuf);
> +    return ret;
> +}
> +
> +/* write file and return it's name (which must be freed by caller) */
> +static int
> +networkRadvdConfWrite(virNetworkObjPtr network, char **configFile)
> +{
> +    int ret = -1;
> +    char *configStr = NULL;
> +    char *myConfigFile = NULL;
> +
> +    if (!configFile)
> +        configFile = &myConfigFile;
> +
> +    *configFile = NULL;
> +
> +    if (networkRadvdConfContents(network, &configStr) < 0)
> +        goto cleanup;
> +
> +    if (!configStr) {
> +        ret = 0;
>          goto cleanup;
>      }
>  
>      /* construct the filename */
> -    if (!(configfile = networkRadvdConfigFileName(network->def->name))) {
> +    if (!(*configFile = networkRadvdConfigFileName(network->def->name))) {
>          virReportOOMError();
>          goto cleanup;
>      }
>      /* write the file */
> -    if (virFileWriteStr(configfile, configstr, 0600) < 0) {
> +    if (virFileWriteStr(*configFile, configStr, 0600) < 0) {
>          virReportSystemError(errno,
>                               _("couldn't write radvd config file '%s'"),
> -                             configfile);
> +                             *configFile);
> +        goto cleanup;
> +    }
> +
> +    ret = 0;
> +cleanup:
> +    VIR_FREE(configStr);
> +    VIR_FREE(myConfigFile);
> +    return ret;
> +}
> +
> +static int
> +networkStartRadvd(virNetworkObjPtr network)
> +{
> +    char *pidfile = NULL;
> +    char *radvdpidbase = NULL;
> +    char *configfile = NULL;
> +    virCommandPtr cmd = NULL;
> +    int ret = -1;
> +
> +    network->radvdPid = -1;
> +
> +    if (!virNetworkDefGetIpByIndex(network->def, AF_INET6, 0)) {
> +        /* no IPv6 addresses, so we don't need to run radvd */
> +        ret = 0;
> +        goto cleanup;
> +    }
> +
> +    if (!virFileIsExecutable(RADVD)) {
> +        virReportSystemError(errno,
> +                             _("Cannot find %s - "
> +                               "Possibly the package isn't installed"),
> +                             RADVD);
>          goto cleanup;
>      }
>  
> +    if (virFileMakePath(NETWORK_PID_DIR) < 0) {
> +        virReportSystemError(errno,
> +                             _("cannot create directory %s"),
> +                             NETWORK_PID_DIR);
> +        goto cleanup;
> +    }
> +    if (virFileMakePath(RADVD_STATE_DIR) < 0) {
> +        virReportSystemError(errno,
> +                             _("cannot create directory %s"),
> +                             RADVD_STATE_DIR);
> +        goto cleanup;
> +    }
> +
> +    /* construct pidfile name */
> +    if (!(radvdpidbase = networkRadvdPidfileBasename(network->def->name))) {
> +        virReportOOMError();
> +        goto cleanup;
> +    }
> +    if (!(pidfile = virPidFileBuildPath(NETWORK_PID_DIR, radvdpidbase))) {
> +        virReportOOMError();
> +        goto cleanup;
> +    }
> +
> +    if (networkRadvdConfWrite(network, &configfile) < 0)
> +        goto cleanup;
> +
>      /* prevent radvd from daemonizing itself with "--debug 1", and use
>       * a dummy pidfile name - virCommand will create the pidfile we
>       * want to use (this is necessary because radvd's internal
> @@ -963,21 +1159,63 @@ networkStartRadvd(virNetworkObjPtr network)
>      if (virCommandRun(cmd, NULL) < 0)
>          goto cleanup;
>  
> -    if (virPidFileRead(NETWORK_PID_DIR, radvdpidbase,
> -                       &network->radvdPid) < 0)
> +    if (virPidFileRead(NETWORK_PID_DIR, radvdpidbase, &network->radvdPid) < 0)
>          goto cleanup;
>  
>      ret = 0;
>  cleanup:
>      virCommandFree(cmd);
>      VIR_FREE(configfile);
> -    VIR_FREE(configstr);
> -    virBufferFreeAndReset(&configbuf);
>      VIR_FREE(radvdpidbase);
>      VIR_FREE(pidfile);
>      return ret;
>  }
>  
> +#if 0
> +/* currently unused, so they cause a build error unless we #if them out */
> +static int
> +networkRefreshRadvd(virNetworkObjPtr network)
> +{
> +    /* if there's no running radvd, just start it */
> +    if (network->radvdPid <= 0 || (kill(network->radvdPid, 0) < 0))
> +        return networkStartRadvd(network);
> +
> +    if (!virNetworkDefGetIpByIndex(network->def, AF_INET6, 0)) {
> +        /* no IPv6 addresses, so we don't need to run radvd */
> +        return 0;
> +    }
> +
> +    if (networkRadvdConfWrite(network, NULL) < 0)
> +        return -1;
> +
> +    return kill(network->radvdPid, SIGHUP);
> +}
> +
> +static int
> +networkRestartRadvd(virNetworkObjPtr network)
> +{
> +    char *radvdpidbase;
> +
> +    /* if there is a running radvd, kill it */
> +    if (network->radvdPid > 0) {
> +        /* essentially ignore errors from the following two functions,
> +         * since there's really no better recovery to be done than to
> +         * just push ahead (and that may be exactly what's needed).
> +         */
> +        if ((networkKillDaemon(network->dnsmasqPid, "radvd",
> +                               network->def->name) >= 0) &&
> +            ((radvdpidbase = networkRadvdPidfileBasename(network->def->name))
> +             != NULL)) {
> +            virPidFileDelete(NETWORK_PID_DIR, radvdpidbase);
> +            VIR_FREE(radvdpidbase);
> +        }
> +        network->radvdPid = -1;
> +    }
> +    /* now start radvd if it should be started */
> +    return networkStartRadvd(network);
> +}
> +#endif /* #if 0 */
> +
>  static int
>  networkAddMasqueradingIptablesRules(struct network_driver *driver,
>                                      virNetworkObjPtr network,

  A bit annoying to add so much #if 0'ed code as we don't check that
code in practice, but okay, ACK,

Daniel

-- 
Daniel Veillard      | libxml Gnome XML XSLT toolkit  http://xmlsoft.org/
daniel at veillard.com  | Rpmfind RPM search engine http://rpmfind.net/
http://veillard.com/ | virtualization library  http://libvirt.org/




More information about the libvir-list mailing list