# dnsmasq.h
dnsmasqAddDhcpHost;
dnsmasqAddHost;
+dnsmasqCapsGet;
+dnsmasqCapsGetBinaryPath;
+dnsmasqCapsGetVersion;
+dnsmasqCapsNewFromBuffer;
+dnsmasqCapsNewFromFile;
+dnsmasqCapsNewFromBinary;
+dnsmasqCapsRefresh;
dnsmasqContextFree;
dnsmasqContextNew;
dnsmasqDelete;
char *networkConfigDir;
char *networkAutostartDir;
char *logDir;
+ dnsmasqCapsPtr dnsmasqCaps;
};
char *radvdpidbase;
ignore_value(virPidFileReadIfAlive(NETWORK_PID_DIR, obj->def->name,
- &obj->dnsmasqPid, DNSMASQ));
+ &obj->dnsmasqPid,
+ dnsmasqCapsGetBinaryPath(driver->dnsmasqCaps)));
if (!(radvdpidbase = networkRadvdPidfileBasename(obj->def->name))) {
virReportOOMError();
goto out_of_memory;
}
+ /* if this fails now, it will be retried later with dnsmasqCapsRefresh() */
+ driverState->dnsmasqCaps = dnsmasqCapsNewFromBinary(DNSMASQ);
if (virNetworkLoadAllConfigs(&driverState->networks,
driverState->networkConfigDir,
if (driverState->iptables)
iptablesContextFree(driverState->iptables);
+ virObjectUnref(driverState->dnsmasqCaps);
+
networkDriverUnlock(driverState);
virMutexDestroy(&driverState->lock);
virNetworkIpDefPtr ipdef,
const char *pidfile,
virCommandPtr cmd,
- dnsmasqContext *dctx)
+ dnsmasqContext *dctx,
+ dnsmasqCapsPtr caps ATTRIBUTE_UNUSED)
{
int r, ret = -1;
int nbleases = 0;
int
networkBuildDhcpDaemonCommandLine(virNetworkObjPtr network, virCommandPtr *cmdout,
- char *pidfile, dnsmasqContext *dctx)
+ char *pidfile, dnsmasqContext *dctx,
+ dnsmasqCapsPtr caps)
{
virCommandPtr cmd = NULL;
int ret = -1, ii;
if (!virNetworkDefGetIpByIndex(network->def, AF_UNSPEC, 0))
return 0;
- cmd = virCommandNew(DNSMASQ);
- if (networkBuildDnsmasqArgv(network, ipdef, pidfile, cmd, dctx) < 0) {
+ cmd = virCommandNew(dnsmasqCapsGetBinaryPath(caps));
+ if (networkBuildDnsmasqArgv(network, ipdef, pidfile, cmd, dctx, caps) < 0) {
goto cleanup;
}
}
static int
-networkStartDhcpDaemon(virNetworkObjPtr network)
+networkStartDhcpDaemon(struct network_driver *driver,
+ virNetworkObjPtr network)
{
virCommandPtr cmd = NULL;
char *pidfile = NULL;
if (dctx == NULL)
goto cleanup;
- ret = networkBuildDhcpDaemonCommandLine(network, &cmd, pidfile, dctx);
+ dnsmasqCapsRefresh(&driver->dnsmasqCaps, false);
+
+ ret = networkBuildDhcpDaemonCommandLine(network, &cmd, pidfile,
+ dctx, driver->dnsmasqCaps);
if (ret < 0)
goto cleanup;
* Returns 0 on success, -1 on failure.
*/
static int
-networkRefreshDhcpDaemon(virNetworkObjPtr network)
+networkRefreshDhcpDaemon(struct network_driver *driver,
+ virNetworkObjPtr network)
{
int ret = -1, ii;
virNetworkIpDefPtr ipdef;
/* if there's no running dnsmasq, just start it */
if (network->dnsmasqPid <= 0 || (kill(network->dnsmasqPid, 0) < 0))
- return networkStartDhcpDaemon(network);
+ return networkStartDhcpDaemon(driver, network);
/* Look for first IPv4 address that has dhcp defined. */
/* We support dhcp config on 1 IPv4 interface only. */
* Returns 0 on success, -1 on failure.
*/
static int
-networkRestartDhcpDaemon(virNetworkObjPtr network)
+networkRestartDhcpDaemon(struct network_driver *driver,
+ virNetworkObjPtr network)
{
/* if there is a running dnsmasq, kill it */
if (network->dnsmasqPid > 0) {
network->dnsmasqPid = -1;
}
/* now start dnsmasq if it should be started */
- return networkStartDhcpDaemon(network);
+ return networkStartDhcpDaemon(driver, network);
}
static int
}
static int
-networkRefreshRadvd(virNetworkObjPtr network)
+networkRefreshRadvd(struct network_driver *driver ATTRIBUTE_UNUSED,
+ virNetworkObjPtr network)
{
/* if there's no running radvd, just start it */
if (network->radvdPid <= 0 || (kill(network->radvdPid, 0) < 0))
#if 0
/* currently unused, so it causes a build error unless we #if it out */
static int
-networkRestartRadvd(virNetworkObjPtr network)
+networkRestartRadvd(struct network_driver *driver,
+ virNetworkObjPtr network)
{
char *radvdpidbase;
* dnsmasq and/or radvd, or restart them if they've
* disappeared.
*/
- networkRefreshDhcpDaemon(network);
- networkRefreshRadvd(network);
+ networkRefreshDhcpDaemon(driver, network);
+ networkRefreshRadvd(driver, network);
}
virNetworkObjUnlock(network);
}
/* start dnsmasq if there are any IP addresses (v4 or v6) */
- if ((v4present || v6present) && networkStartDhcpDaemon(network) < 0)
+ if ((v4present || v6present) &&
+ networkStartDhcpDaemon(driver, network) < 0)
goto err3;
/* start radvd if there are any ipv6 addresses */
/* these sections all change things on the dnsmasq commandline,
* so we need to kill and restart dnsmasq.
*/
- if (networkRestartDhcpDaemon(network) < 0)
+ if (networkRestartDhcpDaemon(driver, network) < 0)
goto cleanup;
} else if (section == VIR_NETWORK_SECTION_IP_DHCP_HOST) {
}
if ((newDhcpActive != oldDhcpActive &&
- networkRestartDhcpDaemon(network) < 0) ||
- networkRefreshDhcpDaemon(network) < 0) {
+ networkRestartDhcpDaemon(driver, network) < 0) ||
+ networkRefreshDhcpDaemon(driver, network) < 0) {
goto cleanup;
}
* can just update the config files and send SIGHUP to
* dnsmasq.
*/
- if (networkRefreshDhcpDaemon(network) < 0)
+ if (networkRefreshDhcpDaemon(driver, network) < 0)
goto cleanup;
}
/* only a change in IP addresses will affect radvd, and all of radvd's
* config is stored in the conf file which will be re-read with a SIGHUP.
*/
- if (networkRefreshRadvd(network) < 0)
+ if (networkRefreshRadvd(driver, network) < 0)
goto cleanup;
}
/*
* network_driver.h: core driver methods for managing networks
*
- * Copyright (C) 2006, 2007, 2011 Red Hat, Inc.
+ * Copyright (C) 2006-2012 Red Hat, Inc.
* Copyright (C) 2006 Daniel P. Berrange
*
* This library is free software; you can redistribute it and/or
int networkBuildDhcpDaemonCommandLine(virNetworkObjPtr network,
virCommandPtr *cmdout, char *pidfile,
- dnsmasqContext *dctx)
+ dnsmasqContext *dctx,
+ dnsmasqCapsPtr caps)
;
# else
/* Define no-op replacements that don't drag in any link dependencies. */
# define networkNotifyActualDevice(iface) (iface=iface, 0)
# define networkReleaseActualDevice(iface) (iface=iface, 0)
# define networkGetNetworkAddress(netname, netaddr) (-2)
-# define networkBuildDhcpDaemonCommandLine(network, cmdout, pidfile, dctx) 0
+# define networkBuildDhcpDaemonCommandLine(network, cmdout, pidfile, dctx, caps) 0
# endif
typedef char *(*networkDnsmasqLeaseFileNameFunc)(const char *netname);
#include "internal.h"
#include "datatypes.h"
+#include "bitmap.h"
#include "dnsmasq.h"
#include "util.h"
+#include "command.h"
#include "memory.h"
#include "virterror_internal.h"
#include "logging.h"
return 0;
}
+
+/*
+ * dnsmasqCapabilities functions - provide useful information about the
+ * version of dnsmasq on this machine.
+ *
+ */
+struct _dnsmasqCaps {
+ virObject object;
+ char *binaryPath;
+ bool noRefresh;
+ time_t mtime;
+ virBitmapPtr flags;
+ unsigned long version;
+};
+
+static virClassPtr dnsmasqCapsClass;
+
+static void
+dnsmasqCapsDispose(void *obj)
+{
+ dnsmasqCapsPtr caps = obj;
+
+ virBitmapFree(caps->flags);
+ VIR_FREE(caps->binaryPath);
+}
+
+static int dnsmasqCapsOnceInit(void)
+{
+ if (!(dnsmasqCapsClass = virClassNew("dnsmasqCaps",
+ sizeof(dnsmasqCaps),
+ dnsmasqCapsDispose))) {
+ return -1;
+ }
+
+ return 0;
+}
+
+VIR_ONCE_GLOBAL_INIT(dnsmasqCaps)
+
+static void
+dnsmasqCapsSet(dnsmasqCapsPtr caps,
+ dnsmasqCapsFlags flag)
+{
+ ignore_value(virBitmapSetBit(caps->flags, flag));
+}
+
+
+#define DNSMASQ_VERSION_STR "Dnsmasq version "
+
+static int
+dnsmasqCapsSetFromBuffer(dnsmasqCapsPtr caps, const char *buf)
+{
+ const char *p;
+
+ caps->noRefresh = true;
+
+ p = STRSKIP(buf, DNSMASQ_VERSION_STR);
+ if (!p)
+ goto fail;
+ virSkipSpaces(&p);
+ if (virParseVersionString(p, &caps->version, true) < 0)
+ goto fail;
+
+ if (strstr(buf, "--bind-dynamic"))
+ dnsmasqCapsSet(caps, DNSMASQ_CAPS_BIND_DYNAMIC);
+
+ VIR_INFO("dnsmasq version is %d.%d, --bind-dynamic is %s",
+ (int)caps->version / 1000000, (int)(caps->version % 1000000) / 1000,
+ dnsmasqCapsGet(caps, DNSMASQ_CAPS_BIND_DYNAMIC)
+ ? "present" : "NOT present");
+ return 0;
+
+fail:
+ p = strchrnul(buf, '\n');
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("cannot parse %s version number in '%.*s'"),
+ caps->binaryPath, (int) (p - buf), buf);
+ return -1;
+
+}
+
+static int
+dnsmasqCapsSetFromFile(dnsmasqCapsPtr caps, const char *path)
+{
+ int ret = -1;
+ char *buf = NULL;
+
+ if (virFileReadAll(path, 1024 * 1024, &buf) < 0)
+ goto cleanup;
+
+ ret = dnsmasqCapsSetFromBuffer(caps, buf);
+
+cleanup:
+ VIR_FREE(buf);
+ return ret;
+}
+
+static int
+dnsmasqCapsRefreshInternal(dnsmasqCapsPtr caps, bool force)
+{
+ int ret = -1;
+ struct stat sb;
+ virCommandPtr cmd = NULL;
+ char *help = NULL, *version = NULL, *complete = NULL;
+
+ if (!caps || caps->noRefresh)
+ return 0;
+
+ if (stat(caps->binaryPath, &sb) < 0) {
+ virReportSystemError(errno, _("Cannot check dnsmasq binary %s"),
+ caps->binaryPath);
+ return -1;
+ }
+ if (!force && caps->mtime == sb.st_mtime) {
+ return 0;
+ }
+ caps->mtime = sb.st_mtime;
+
+ /* Make sure the binary we are about to try exec'ing exists.
+ * Technically we could catch the exec() failure, but that's
+ * in a sub-process so it's hard to feed back a useful error.
+ */
+ if (!virFileIsExecutable(caps->binaryPath)) {
+ virReportSystemError(errno, _("dnsmasq binary %s is not executable"),
+ caps->binaryPath);
+ goto cleanup;
+ }
+
+ cmd = virCommandNewArgList(caps->binaryPath, "--version", NULL);
+ virCommandSetOutputBuffer(cmd, &version);
+ virCommandSetErrorBuffer(cmd, &version);
+ virCommandAddEnvPassCommon(cmd);
+ virCommandClearCaps(cmd);
+ if (virCommandRun(cmd, NULL) < 0) {
+ virReportSystemError(errno, _("failed to run '%s --version': %s"),
+ caps->binaryPath, version);
+ goto cleanup;
+ }
+ virCommandFree(cmd);
+
+ cmd = virCommandNewArgList(caps->binaryPath, "--help", NULL);
+ virCommandSetOutputBuffer(cmd, &help);
+ virCommandSetErrorBuffer(cmd, &help);
+ virCommandAddEnvPassCommon(cmd);
+ virCommandClearCaps(cmd);
+ if (virCommandRun(cmd, NULL) < 0) {
+ virReportSystemError(errno, _("failed to run '%s --help': %s"),
+ caps->binaryPath, help);
+ goto cleanup;
+ }
+
+ if (virAsprintf(&complete, "%s\n%s", version, help) < 0) {
+ virReportOOMError();
+ goto cleanup;
+ }
+
+ ret = dnsmasqCapsSetFromBuffer(caps, complete);
+
+cleanup:
+ virCommandFree(cmd);
+ VIR_FREE(help);
+ VIR_FREE(version);
+ VIR_FREE(complete);
+ return ret;
+}
+
+static dnsmasqCapsPtr
+dnsmasqCapsNewEmpty(const char *binaryPath)
+{
+ dnsmasqCapsPtr caps;
+
+ if (dnsmasqCapsInitialize() < 0)
+ return NULL;
+ if (!(caps = virObjectNew(dnsmasqCapsClass)))
+ return NULL;
+ if (!(caps->flags = virBitmapNew(DNSMASQ_CAPS_LAST)))
+ goto error;
+ if (!(caps->binaryPath = strdup(binaryPath ? binaryPath : DNSMASQ)))
+ goto error;
+ return caps;
+
+error:
+ virReportOOMError();
+ virObjectUnref(caps);
+ return NULL;
+}
+
+dnsmasqCapsPtr
+dnsmasqCapsNewFromBuffer(const char *buf, const char *binaryPath)
+{
+ dnsmasqCapsPtr caps = dnsmasqCapsNewEmpty(binaryPath);
+
+ if (!caps)
+ return NULL;
+
+ if (dnsmasqCapsSetFromBuffer(caps, buf) < 0) {
+ virObjectUnref(caps);
+ return NULL;
+ }
+ return caps;
+}
+
+dnsmasqCapsPtr
+dnsmasqCapsNewFromFile(const char *dataPath, const char *binaryPath)
+{
+ dnsmasqCapsPtr caps = dnsmasqCapsNewEmpty(binaryPath);
+
+ if (!caps)
+ return NULL;
+
+ if (dnsmasqCapsSetFromFile(caps, dataPath) < 0) {
+ virObjectUnref(caps);
+ return NULL;
+ }
+ return caps;
+}
+
+dnsmasqCapsPtr
+dnsmasqCapsNewFromBinary(const char *binaryPath)
+{
+ dnsmasqCapsPtr caps = dnsmasqCapsNewEmpty(binaryPath);
+
+ if (!caps)
+ return NULL;
+
+ if (dnsmasqCapsRefreshInternal(caps, true) < 0) {
+ virObjectUnref(caps);
+ return NULL;
+ }
+ return caps;
+}
+
+/** dnsmasqCapsRefresh:
+ *
+ * Refresh an existing caps object if the binary has changed. If
+ * there isn't yet a caps object (if it's NULL), create a new one.
+ *
+ * Returns 0 on success, -1 on failure
+ */
+int
+dnsmasqCapsRefresh(dnsmasqCapsPtr *caps, const char *binaryPath)
+{
+ if (!*caps) {
+ *caps = dnsmasqCapsNewFromBinary(binaryPath);
+ return *caps ? 0 : -1;
+ }
+ return dnsmasqCapsRefreshInternal(*caps, false);
+}
+
+const char *
+dnsmasqCapsGetBinaryPath(dnsmasqCapsPtr caps)
+{
+ return caps ? caps->binaryPath : DNSMASQ;
+}
+
+unsigned long
+dnsmasqCapsGetVersion(dnsmasqCapsPtr caps)
+{
+ if (caps)
+ return caps->version;
+ else
+ return 0;
+}
+
+bool
+dnsmasqCapsGet(dnsmasqCapsPtr caps, dnsmasqCapsFlags flag)
+{
+ bool b;
+
+ if (!caps || virBitmapGetBit(caps->flags, flag, &b) < 0)
+ return false;
+ else
+ return b;
+}
/*
- * Copyright (C) 2007-2010 Red Hat, Inc.
+ * Copyright (C) 2007-2012 Red Hat, Inc.
* Copyright (C) 2010 Satoru SATOH <satoru.satoh@gmail.com>
*
* This library is free software; you can redistribute it and/or
#ifndef __DNSMASQ_H__
# define __DNSMASQ_H__
+# include "virobject.h"
# include "virsocketaddr.h"
typedef struct
dnsmasqAddnHostsfile *addnhostsfile;
} dnsmasqContext;
+typedef enum {
+ DNSMASQ_CAPS_BIND_DYNAMIC = 0, /* support for --bind-dynamic */
+
+ DNSMASQ_CAPS_LAST, /* this must always be the last item */
+} dnsmasqCapsFlags;
+
+typedef struct _dnsmasqCaps dnsmasqCaps;
+typedef dnsmasqCaps *dnsmasqCapsPtr;
+
+
dnsmasqContext * dnsmasqContextNew(const char *network_name,
const char *config_dir);
void dnsmasqContextFree(dnsmasqContext *ctx);
int dnsmasqDelete(const dnsmasqContext *ctx);
int dnsmasqReload(pid_t pid);
+dnsmasqCapsPtr dnsmasqCapsNewFromBuffer(const char *buf,
+ const char *binaryPath);
+dnsmasqCapsPtr dnsmasqCapsNewFromFile(const char *dataPath,
+ const char *binaryPath);
+dnsmasqCapsPtr dnsmasqCapsNewFromBinary(const char *binaryPath);
+int dnsmasqCapsRefresh(dnsmasqCapsPtr *caps, const char *binaryPath);
+bool dnsmasqCapsGet(dnsmasqCapsPtr caps, dnsmasqCapsFlags flag);
+const char *dnsmasqCapsGetBinaryPath(dnsmasqCapsPtr caps);
+unsigned long dnsmasqCapsGetVersion(dnsmasqCapsPtr caps);
#endif /* __DNSMASQ_H__ */
return 0;
}
-static int testCompareXMLToArgvFiles(const char *inxml, const char *outargv) {
+static int
+testCompareXMLToArgvFiles(const char *inxml, const char *outargv, dnsmasqCapsPtr caps)
+{
char *inXmlData = NULL;
char *outArgvData = NULL;
char *actual = NULL;
if (dctx == NULL)
goto fail;
- if (networkBuildDhcpDaemonCommandLine(obj, &cmd, pidfile, dctx) < 0)
+ if (networkBuildDhcpDaemonCommandLine(obj, &cmd, pidfile, dctx, caps) < 0)
goto fail;
if (!(actual = virCommandToString(cmd)))
return ret;
}
+typedef struct {
+ const char *name;
+ dnsmasqCapsPtr caps;
+} testInfo;
+
static int
testCompareXMLToArgvHelper(const void *data)
{
int result = -1;
+ const testInfo *info = data;
char *inxml = NULL;
char *outxml = NULL;
if (virAsprintf(&inxml, "%s/networkxml2argvdata/%s.xml",
- abs_srcdir, (const char*)data) < 0 ||
+ abs_srcdir, info->name) < 0 ||
virAsprintf(&outxml, "%s/networkxml2argvdata/%s.argv",
- abs_srcdir, (const char*)data) < 0) {
+ abs_srcdir, info->name) < 0) {
goto cleanup;
}
- result = testCompareXMLToArgvFiles(inxml, outxml);
+ result = testCompareXMLToArgvFiles(inxml, outxml, info->caps);
cleanup:
VIR_FREE(inxml);
mymain(void)
{
int ret = 0;
+ dnsmasqCapsPtr restricted
+ = dnsmasqCapsNewFromBuffer("Dnsmasq version 2.48", DNSMASQ);
+ dnsmasqCapsPtr full
+ = dnsmasqCapsNewFromBuffer("Dnsmasq version 2.63\n--bind-dynamic", DNSMASQ);
networkDnsmasqLeaseFileName = testDnsmasqLeaseFileName;
-#define DO_TEST(name) \
- if (virtTestRun("Network XML-2-Argv " name, \
- 1, testCompareXMLToArgvHelper, (name)) < 0) \
- ret = -1
-
- DO_TEST("isolated-network");
- DO_TEST("routed-network");
- DO_TEST("nat-network");
- DO_TEST("netboot-network");
- DO_TEST("netboot-proxy-network");
- DO_TEST("nat-network-dns-txt-record");
- DO_TEST("nat-network-dns-srv-record");
- DO_TEST("nat-network-dns-srv-record-minimal");
- DO_TEST("nat-network-dns-hosts");
+#define DO_TEST(xname, xcaps) \
+ do { \
+ static testInfo info; \
+ \
+ info.name = xname; \
+ info.caps = xcaps; \
+ if (virtTestRun("Network XML-2-Argv " xname, \
+ 1, testCompareXMLToArgvHelper, &info) < 0) { \
+ ret = -1; \
+ } \
+ } while (0)
+
+ DO_TEST("isolated-network", restricted);
+ DO_TEST("netboot-network", restricted);
+ DO_TEST("netboot-proxy-network", restricted);
+ DO_TEST("nat-network-dns-srv-record-minimal", restricted);
+ DO_TEST("routed-network", full);
+ DO_TEST("nat-network", full);
+ DO_TEST("nat-network-dns-txt-record", full);
+ DO_TEST("nat-network-dns-srv-record", full);
+ DO_TEST("nat-network-dns-hosts", full);
return ret==0 ? EXIT_SUCCESS : EXIT_FAILURE;
}