Signed-off-by: Michal Novotny <minovotn(a)redhat.com>
---
.gnulib | 2 +-
docs/formatnetwork.html.in | 9 +-
src/libvirt_private.syms | 1 +
src/network/bridge_driver.c | 3 +
src/util/dnsmasq.c | 285 ++++++++++++++++++++++++++++++++++++++++---
src/util/dnsmasq.h | 22 +++-
6 files changed, 299 insertions(+), 23 deletions(-)
diff --git a/.gnulib b/.gnulib
index 3864a29..7d06b32 160000
--- a/.gnulib
+++ b/.gnulib
@@ -1 +1 @@
-Subproject commit 3864a29763baff1d01895a57f73729fd53cc5fbf
+Subproject commit 7d06b32684363a39fae65c616b84bc7589768106
diff --git a/docs/formatnetwork.html.in b/docs/formatnetwork.html.in
index 008897d..95d9c1a 100644
--- a/docs/formatnetwork.html.in
+++ b/docs/formatnetwork.html.in
@@ -183,8 +183,6 @@
or commas. value is a single string that can contain multiple values which are
comma-separated which is allowed for the TXT records and it is represented a
single value. <span class="since">Since 0.9.1</span>
- </dd>
-
</dd><dt><code>dhcp</code></dt><dd>Also within
the <code>ip</code> element there is an
optional <code>dhcp</code> element. The presence of this element
enables DHCP services on the virtual network. It will further
@@ -220,6 +218,13 @@
for all address ranges and statically assigned addresses.<span
class="since">Since 0.7.1 (<code>server</code> since
0.7.3).</span>
</dd>
+ <dt><code>host</code></dt>
+ <dd>The <code>host</code> element is the definition of DNS hosts
to be passed
+ to the DNS service. The IP address is identified by the
<code>ip</code> attribute
+ and the names for the IP addresses are identified in the
<code>hostname</code>
+ subelements of the <code>host</code> element.
+ <span class="since">Since 0.9.1</span>
+ </dd>
</dl>
<h2><a name="examples">Example
configuration</a></h2>
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 1b22be6..cc0c4c7 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -186,6 +186,7 @@ virUnrefStream;
# dnsmasq.h
dnsmasqAddDhcpHost;
+dnsmasqAddHost;
dnsmasqContextFree;
dnsmasqContextNew;
dnsmasqDelete;
diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c
index 653be35..26196d0 100644
--- a/src/network/bridge_driver.c
+++ b/src/network/bridge_driver.c
@@ -609,6 +609,9 @@ networkBuildDnsmasqArgv(virNetworkObjPtr network,
if (dctx->hostsfile->nhosts)
virCommandAddArgPair(cmd, "--dhcp-hostsfile",
dctx->hostsfile->path);
+ if (dctx->addnhostsfile->nhosts)
+ virCommandAddArgPair(cmd, "--addn-hosts",
+ dctx->addnhostsfile->path);
dnsmasqContextFree(dctx);
}
diff --git a/src/util/dnsmasq.c b/src/util/dnsmasq.c
index 2ba9355..04a912a 100644
--- a/src/util/dnsmasq.c
+++ b/src/util/dnsmasq.c
@@ -48,6 +48,7 @@
#define VIR_FROM_THIS VIR_FROM_NETWORK
#define DNSMASQ_HOSTSFILE_SUFFIX "hostsfile"
+#define DNSMASQ_ADDNHOSTSFILE_SUFFIX "addnhosts"
static void
dhcphostFree(dnsmasqDhcpHost *host)
@@ -56,6 +57,235 @@ dhcphostFree(dnsmasqDhcpHost *host)
}
static void
+addnhostFree(dnsmasqAddnHost *host)
+{
+ int i;
+
+ for (i = 0; i < host->nhostnames; i++)
+ VIR_FREE(host->hostnames[i]);
+ VIR_FREE(host->hostnames);
+ VIR_FREE(host->ip);
+}
+
+static void
+addnhostsFree(dnsmasqAddnHostsfile *addnhostsfile)
+{
+ unsigned int i;
+
+ if (addnhostsfile->hosts) {
+ for (i = 0; i < addnhostsfile->nhosts; i++)
+ addnhostFree(&addnhostsfile->hosts[i]);
+
+ VIR_FREE(addnhostsfile->hosts);
+
+ addnhostsfile->nhosts = 0;
+ }
+
+ VIR_FREE(addnhostsfile->path);
+
+ VIR_FREE(addnhostsfile);
+}
+
+static int
+addnhostsAdd(dnsmasqAddnHostsfile *addnhostsfile,
+ virSocketAddr *ip,
+ const char *name)
+{
+ char *ipstr = NULL;
+ int idx = -1;
+ int i;
+
+ if (!(ipstr = virSocketFormatAddr(ip)))
+ return -1;
+
+ for (i = 0; i < addnhostsfile->nhosts; i++) {
+ if (STREQ((const char *)addnhostsfile->hosts[i].ip, (const char *)ipstr)) {
+ idx = i;
+ break;
+ }
+ }
+
+ if (idx < 0) {
+ if (VIR_REALLOC_N(addnhostsfile->hosts, addnhostsfile->nhosts + 1) < 0)
+ goto alloc_error;
+
+ idx = addnhostsfile->nhosts;
+ if (VIR_ALLOC(addnhostsfile->hosts[idx].hostnames) < 0)
+ goto alloc_error;
+
+ if (virAsprintf(&addnhostsfile->hosts[idx].ip, "%s", ipstr) <
0)
+ goto alloc_error;
+
+ addnhostsfile->hosts[idx].nhostnames = 0;
+ addnhostsfile->nhosts++;
+ }
+
+ if (VIR_REALLOC_N(addnhostsfile->hosts[idx].hostnames,
addnhostsfile->hosts[idx].nhostnames + 1) < 0)
+ goto alloc_error;
+
+ if
(virAsprintf(&addnhostsfile->hosts[idx].hostnames[addnhostsfile->hosts[idx].nhostnames],
"%s", name) < 0)
+ goto alloc_error;
+
+ VIR_FREE(ipstr);
+
+ addnhostsfile->hosts[idx].nhostnames++;
+
+ return 0;
+
+ alloc_error:
+ virReportOOMError();
+ VIR_FREE(ipstr);
+ return -1;
+}
+
+static dnsmasqAddnHostsfile *
+addnhostsNew(const char *name,
+ const char *config_dir)
+{
+ int err;
+ dnsmasqAddnHostsfile *addnhostsfile;
+
+ if (VIR_ALLOC(addnhostsfile) < 0) {
+ virReportOOMError();
+ return NULL;
+ }
+
+ addnhostsfile->hosts = NULL;
+ addnhostsfile->nhosts = 0;
+
+ if (virAsprintf(&addnhostsfile->path, "%s/%s.%s", config_dir, name,
+ DNSMASQ_ADDNHOSTSFILE_SUFFIX) < 0) {
+ virReportOOMError();
+ goto error;
+ }
+
+ if ((err = virFileMakePath(config_dir))) {
+ virReportSystemError(err, _("cannot create config directory
'%s'"),
+ config_dir);
+ goto error;
+ }
+
+ return addnhostsfile;
+
+ error:
+ addnhostsFree(addnhostsfile);
+ return NULL;
+}
+
+static int
+addnhostsWrite(const char *path,
+ dnsmasqAddnHost *hosts,
+ unsigned int nhosts)
+{
+ char *tmp;
+ FILE *f;
+ bool istmp = true;
+ unsigned int i, ii;
+ int rc = 0;
+
+ if (nhosts == 0)
+ return rc;
+
+ if (virAsprintf(&tmp, "%s.new", path) < 0)
+ return -ENOMEM;
+
+ if (!(f = fopen(tmp, "w"))) {
+ istmp = false;
+ if (!(f = fopen(path, "w"))) {
+ rc = errno;
+ goto cleanup;
+ }
+ }
+
+ for (i = 0; i < nhosts; i++) {
+ if (fputs(hosts[i].ip, f) == EOF || fputc('\t', f) == EOF) {
+ rc = errno;
+ VIR_FORCE_FCLOSE(f);
+
+ if (istmp)
+ unlink(tmp);
+
+ goto cleanup;
+ }
+
+ for (ii = 0; ii < hosts[i].nhostnames; ii++) {
+ if (fputs(hosts[i].hostnames[ii], f) == EOF || fputc('\t', f) == EOF)
{
+ rc = errno;
+ VIR_FORCE_FCLOSE(f);
+
+ if (istmp)
+ unlink(tmp);
+
+ goto cleanup;
+ }
+ }
+
+ if (fputc('\n', f) == EOF) {
+ rc = errno;
+ VIR_FORCE_FCLOSE(f);
+
+ if (istmp)
+ unlink(tmp);
+
+ goto cleanup;
+ }
+ }
+
+ if (VIR_FCLOSE(f) == EOF) {
+ rc = errno;
+ goto cleanup;
+ }
+
+ if (istmp) {
+ if (rename(tmp, path) < 0) {
+ rc = errno;
+ unlink(tmp);
+ goto cleanup;
+ }
+
+ if (unlink(tmp) < 0) {
+ rc = errno;
+ goto cleanup;
+ }
+ }
+
+ cleanup:
+ VIR_FREE(tmp);
+
+ return rc;
+}
+
+static int
+addnhostsSave(dnsmasqAddnHostsfile *addnhostsfile)
+{
+ int err = addnhostsWrite(addnhostsfile->path, addnhostsfile->hosts,
+ addnhostsfile->nhosts);
+
+ if (err < 0) {
+ virReportSystemError(err, _("cannot write config file '%s'"),
+ addnhostsfile->path);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+genericFileDelete(char *path)
+{
+ if (!virFileExists(path))
+ return 0;
+
+ if (unlink(path) < 0) {
+ virReportSystemError(errno, _("cannot remove config file
'%s'"),
+ path);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void
hostsfileFree(dnsmasqHostsfile *hostsfile)
{
unsigned int i;
@@ -220,21 +450,6 @@ hostsfileSave(dnsmasqHostsfile *hostsfile)
return 0;
}
-static int
-hostsfileDelete(dnsmasqHostsfile *hostsfile)
-{
- if (!virFileExists(hostsfile->path))
- return 0;
-
- if (unlink(hostsfile->path) < 0) {
- virReportSystemError(errno, _("cannot remove config file
'%s'"),
- hostsfile->path);
- return -1;
- }
-
- return 0;
-}
-
/**
* dnsmasqContextNew:
*
@@ -255,6 +470,8 @@ dnsmasqContextNew(const char *network_name,
if (!(ctx->hostsfile = hostsfileNew(network_name, config_dir)))
goto error;
+ if (!(ctx->addnhostsfile = addnhostsNew(network_name, config_dir)))
+ goto error;
return ctx;
@@ -277,6 +494,8 @@ dnsmasqContextFree(dnsmasqContext *ctx)
if (ctx->hostsfile)
hostsfileFree(ctx->hostsfile);
+ if (ctx->addnhostsfile)
+ addnhostsFree(ctx->addnhostsfile);
VIR_FREE(ctx);
}
@@ -300,6 +519,24 @@ dnsmasqAddDhcpHost(dnsmasqContext *ctx,
hostsfileAdd(ctx->hostsfile, mac, ip, name);
}
+/*
+ * dnsmasqAddHost:
+ * @ctx: pointer to the dnsmasq context for each network
+ * @ip: pointer to the socket address contains ip of the host
+ * @name: pointer to the string contains hostname of the host
+ *
+ * Add additional host entry.
+ */
+
+void
+dnsmasqAddHost(dnsmasqContext *ctx,
+ virSocketAddr *ip,
+ const char *name)
+{
+ if (ctx->addnhostsfile)
+ addnhostsAdd(ctx->addnhostsfile, ip, name);
+}
+
/**
* dnsmasqSave:
* @ctx: pointer to the dnsmasq context for each network
@@ -309,10 +546,16 @@ dnsmasqAddDhcpHost(dnsmasqContext *ctx,
int
dnsmasqSave(const dnsmasqContext *ctx)
{
+ int ret = 0;
+
if (ctx->hostsfile)
- return hostsfileSave(ctx->hostsfile);
+ ret = hostsfileSave(ctx->hostsfile);
+ if (ret == 0) {
+ if (ctx->addnhostsfile)
+ ret = addnhostsSave(ctx->addnhostsfile);
+ }
- return 0;
+ return ret;
}
@@ -325,10 +568,14 @@ dnsmasqSave(const dnsmasqContext *ctx)
int
dnsmasqDelete(const dnsmasqContext *ctx)
{
+ int ret = 0;
+
if (ctx->hostsfile)
- return hostsfileDelete(ctx->hostsfile);
+ ret = genericFileDelete(ctx->hostsfile->path);
+ if (ctx->addnhostsfile)
+ ret = genericFileDelete(ctx->addnhostsfile->path);
- return 0;
+ return ret;
}
/**
diff --git a/src/util/dnsmasq.h b/src/util/dnsmasq.h
index 02a961f..3f6320a 100644
--- a/src/util/dnsmasq.h
+++ b/src/util/dnsmasq.h
@@ -44,7 +44,24 @@ typedef struct
typedef struct
{
- dnsmasqHostsfile *hostsfile;
+ unsigned int nhostnames;
+ char *ip;
+ char **hostnames;
+
+} dnsmasqAddnHost;
+
+typedef struct
+{
+ unsigned int nhosts;
+ dnsmasqAddnHost *hosts;
+
+ char *path; /* Absolute path of dnsmasq's hostsfile. */
+} dnsmasqAddnHostsfile;
+
+typedef struct
+{
+ dnsmasqHostsfile *hostsfile;
+ dnsmasqAddnHostsfile *addnhostsfile;
} dnsmasqContext;
dnsmasqContext * dnsmasqContextNew(const char *network_name,
@@ -54,6 +71,9 @@ void dnsmasqAddDhcpHost(dnsmasqContext *ctx,
const char *mac,
virSocketAddr *ip,
const char *name);
+void dnsmasqAddHost(dnsmasqContext *ctx,
+ virSocketAddr *ip,
+ const char *name);
int dnsmasqSave(const dnsmasqContext *ctx);
int dnsmasqDelete(const dnsmasqContext *ctx);
int dnsmasqReload(pid_t pid);
--
1.7.3.2