Index: src/driver.h =================================================================== RCS file: /data/cvs/libvirt/src/driver.h,v retrieving revision 1.42 diff -u -r1.42 driver.h --- src/driver.h 20 Feb 2008 15:06:53 -0000 1.42 +++ src/driver.h 21 Feb 2008 20:43:48 -0000 @@ -377,6 +377,22 @@ (*virDrvNetworkSetAutostart) (virNetworkPtr network, int autostart); +typedef int + (*virDrvNetworkNumOfDHCPHostMappings) (virNetworkPtr network); +typedef int + (*virDrvNetworkListDHCPHostMappings) (virNetworkPtr network, + virNetworkDHCPHostMappingPtr *const mappings, + int maxmappings); +typedef int + (*virDrvNetworkAddDHCPHostMapping) (virNetworkPtr network, + const char *hwaddr, + const char *ipaddr, + const char *hostname, + unsigned int flags); +typedef int + (*virDrvNetworkDeleteDHCPHostMapping) (virNetworkPtr network, + const char *hwaddr); + typedef struct _virNetworkDriver virNetworkDriver; typedef virNetworkDriver *virNetworkDriverPtr; @@ -410,6 +426,10 @@ virDrvNetworkGetBridgeName networkGetBridgeName; virDrvNetworkGetAutostart networkGetAutostart; virDrvNetworkSetAutostart networkSetAutostart; + virDrvNetworkNumOfDHCPHostMappings networkNumOfDHCPHostMappings; + virDrvNetworkListDHCPHostMappings networkListDHCPHostMappings; + virDrvNetworkAddDHCPHostMapping networkAddDHCPHostMapping; + virDrvNetworkDeleteDHCPHostMapping networkDeleteDHCPHostMapping; }; Index: src/qemu_driver.c =================================================================== RCS file: /data/cvs/libvirt/src/qemu_driver.c,v retrieving revision 1.52 diff -u -r1.52 qemu_driver.c --- src/qemu_driver.c 7 Feb 2008 16:50:17 -0000 1.52 +++ src/qemu_driver.c 21 Feb 2008 20:43:50 -0000 @@ -831,6 +831,7 @@ 2 + /* --except-interface lo */ 2 + /* --listen-address 10.0.0.1 */ 1 + /* --dhcp-leasefile=path */ + 1 + /* --dhcp-hostsfile=path */ (2 * network->def->nranges) + /* --dhcp-range 10.0.0.2,10.0.0.254 */ 1; /* NULL */ @@ -884,6 +885,10 @@ LOCAL_STATE_DIR, network->def->name); APPEND_ARG(*argv, i++, buf); + snprintf(buf, sizeof(buf), + "--dhcp-hostsfile=" DHCP_HOSTS_FORMAT, network->def->name); + APPEND_ARG(*argv, i++, buf); + range = network->def->ranges; while (range) { snprintf(buf, sizeof(buf), "%s,%s", @@ -914,6 +919,7 @@ dhcpStartDhcpDaemon(virConnectPtr conn, struct qemud_network *network) { + char buf[PATH_MAX]; char **argv; int ret, i; @@ -923,6 +929,15 @@ return -1; } + /* Touch the DHCP hosts file so it exists before dnsmasq starts up. */ + snprintf (buf, sizeof buf, DHCP_HOSTS_FORMAT, network->def->name); + ret = open (buf, O_CREAT|O_WRONLY, 0644); + if (ret == -1) { + qemudReportError (conn, NULL, NULL, VIR_ERR_SYSTEM_ERROR, + "%s: %s", buf, strerror (errno)); + return -1; + } + argv = NULL; if (qemudBuildDnsmasqArgv(conn, network, &argv) < 0) return -1; @@ -2853,6 +2868,302 @@ return 0; } +static void +qemudFreeDHCPHostMappings (virNetworkDHCPHostMappingPtr *mappings, + int nr_mappings) +{ + int i; + + for (i = 0; i < nr_mappings; ++i) + if (mappings[i]) { + free (mappings[i]->hwaddr); + free (mappings[i]->ipaddr); + free (mappings[i]->hostname); + free (mappings[i]); + } + free (mappings); +} + +/* This allocates the array and returns the number of mappings in the + * array or -1 on error. + */ +static int +qemudParseDHCPHostMappingsFile (virNetworkPtr net, + struct qemud_network *network, + virNetworkDHCPHostMappingPtr **mappings) +{ + char buf[PATH_MAX]; + FILE *fp; + int col; + int lines = 0; + int allocated = 0; + char *str; + char *token; + char *saveptr = NULL; + virNetworkDHCPHostMappingPtr *old_mappings; + + if (mappings) *mappings = NULL; + + snprintf (buf, sizeof buf, DHCP_HOSTS_FORMAT, network->def->name); + fp = fopen (buf, "r"); + if (fp == NULL) { + qemudReportError (net->conn, NULL, net, VIR_ERR_SYSTEM_ERROR, + "%s: %s", buf, strerror (errno)); + return -1; + } + + while (fgets (buf, sizeof buf, fp) != 0) { + char *hwaddr = 0, *ipaddr = 0, *hostname = 0; + + /* Dnsmasq manpage is fuzzy about what is accepted as a + * parameter for dhcp-host. Hopefully only we will be + * writing to this file, but be generous in what we + * accept anyway. + */ + if (buf[0] == '\0' || buf[0] == '#' || buf[0] == '\n') + continue; + + for (str = buf, col = 0; + (token = strtok_r (str, ",", &saveptr)) != NULL; + str = NULL, col++) { + if (!STREQLEN (token, "net:", 4) && + !STREQLEN (token, "id:", 3) && + !STREQ (token, "ignore")) { + /* First column is the hardware address. */ + if (col == 0) hwaddr = token; + else { + if (isdigit (token[0])) { + if (!ipaddr) ipaddr = token; + } else { + if (!hostname) hostname = token; + } + } + } + } + + if (hwaddr && ipaddr) { + if (mappings) { + if (lines >= allocated) { + allocated += 16; + old_mappings = *mappings; + *mappings = realloc (*mappings, + sizeof (virNetworkDHCPHostMappingPtr) * + allocated); + if (!*mappings) { + *mappings = old_mappings; + goto mem_error; + } + } + *mappings[lines] = malloc (sizeof (virNetworkDHCPHostMapping)); + if (!*mappings[lines]) goto mem_error; + (*mappings[lines])->hwaddr = hwaddr; + (*mappings[lines])->ipaddr = ipaddr; + (*mappings[lines])->hostname = hostname; + } + lines++; + } + } + + fclose (fp); + + return lines; + + mem_error: + fclose (fp); + + if (mappings) qemudFreeDHCPHostMappings (*mappings, lines); + + qemudReportError (net->conn, NULL, net, VIR_ERR_NO_MEMORY, + __FUNCTION__); + + return -1; +} + +static int +qemudNetworkNumOfDHCPHostMappings (virNetworkPtr net) +{ + struct qemud_driver *driver = + (struct qemud_driver *)net->conn->networkPrivateData; + struct qemud_network *network = + qemudFindNetworkByUUID(driver, net->uuid); + + if (!network) { + qemudReportError (net->conn, NULL, net, VIR_ERR_INVALID_DOMAIN, + "no network with matching uuid"); + return -1; + } + + return qemudParseDHCPHostMappingsFile (net, network, NULL); +} + +static int +qemudNetworkListDHCPHostMappings (virNetworkPtr net, + virNetworkDHCPHostMappingPtr *const mappings_r, + int maxmappings) +{ + virNetworkDHCPHostMappingPtr *mappings; + int nr_mappings; + struct qemud_driver *driver = + (struct qemud_driver *)net->conn->networkPrivateData; + struct qemud_network *network = + qemudFindNetworkByUUID(driver, net->uuid); + + if (!network) { + qemudReportError (net->conn, NULL, net, VIR_ERR_INVALID_DOMAIN, + "no network with matching uuid"); + return -1; + } + + nr_mappings = qemudParseDHCPHostMappingsFile (net, network, &mappings); + if (nr_mappings == -1) return -1; + + if (maxmappings >= nr_mappings) { + /* The return array is large enough. */ + memcpy (mappings_r, mappings, + nr_mappings *sizeof (virNetworkDHCPHostMappingPtr)); + } else { + /* Partially copy the array, free the unused mappings that + * we didn't copy. + */ + memcpy (mappings_r, mappings, + maxmappings * sizeof (virNetworkDHCPHostMappingPtr)); + nr_mappings -= maxmappings; + qemudFreeDHCPHostMappings (mappings + maxmappings, nr_mappings); + } + + free (mappings); /* NB: Just free the array itself. */ + return 0; +} + +static int +qemudNetworkAddDHCPHostMapping (virNetworkPtr net, + const char *hwaddr, + const char *ipaddr, + const char *hostname, + unsigned int flags ATTRIBUTE_UNUSED) +{ + char buf[PATH_MAX]; + virNetworkDHCPHostMappingPtr *mappings; + int nr_mappings, i, written = 0; + FILE *fp; + struct qemud_driver *driver = + (struct qemud_driver *)net->conn->networkPrivateData; + struct qemud_network *network = + qemudFindNetworkByUUID(driver, net->uuid); + + if (!network) { + qemudReportError (net->conn, NULL, net, VIR_ERR_INVALID_DOMAIN, + "no network with matching uuid"); + return -1; + } + + nr_mappings = qemudParseDHCPHostMappingsFile (net, network, &mappings); + if (nr_mappings == -1) return -1; + + snprintf (buf, sizeof buf, DHCP_HOSTS_FORMAT, network->def->name); + fp = fopen (buf, "w"); + if (fp == NULL) { + qemudReportError (net->conn, NULL, net, VIR_ERR_SYSTEM_ERROR, + "%s: %s", buf, strerror (errno)); + return -1; + } + + for (i = 0; i < nr_mappings; ++i) { + if (STREQ (mappings[i]->hwaddr, hwaddr)) { + /* Replace this entry. */ + fputs (hwaddr, fp); + fputc (',', fp); + fputs (ipaddr, fp); + if (hostname) { + fputc (',', fp); + fputs (hostname, fp); + } + fputc ('\n', fp); + written = 1; + } else { + /* Write this entry again. */ + fputs (mappings[i]->hwaddr, fp); + fputc (',', fp); + fputs (mappings[i]->ipaddr, fp); + if (mappings[i]->hostname) { + fputc (',', fp); + fputs (mappings[i]->hostname, fp); + } + fputc ('\n', fp); + } + } + + if (!written) { + fputs (hwaddr, fp); + fputc (',', fp); + fputs (ipaddr, fp); + if (hostname) { + fputc (',', fp); + fputs (hostname, fp); + } + fputc ('\n', fp); + } + + fclose (fp); + + /* Tell dnsmasq to reread the configuration file. */ + kill (network->dnsmasqPid, SIGHUP); + + return 0; +} + +static int +qemudNetworkDeleteDHCPHostMapping (virNetworkPtr net, + const char *hwaddr) +{ + char buf[PATH_MAX]; + virNetworkDHCPHostMappingPtr *mappings; + int nr_mappings, i; + FILE *fp; + struct qemud_driver *driver = + (struct qemud_driver *)net->conn->networkPrivateData; + struct qemud_network *network = + qemudFindNetworkByUUID(driver, net->uuid); + + if (!network) { + qemudReportError (net->conn, NULL, net, VIR_ERR_INVALID_DOMAIN, + "no network with matching uuid"); + return -1; + } + + nr_mappings = qemudParseDHCPHostMappingsFile (net, network, &mappings); + if (nr_mappings == -1) return -1; + + snprintf (buf, sizeof buf, DHCP_HOSTS_FORMAT, network->def->name); + fp = fopen (buf, "w"); + if (fp == NULL) { + qemudReportError (net->conn, NULL, net, VIR_ERR_SYSTEM_ERROR, + "%s: %s", buf, strerror (errno)); + return -1; + } + + for (i = 0; i < nr_mappings; ++i) { + if (!STREQ (mappings[i]->hwaddr, hwaddr)) { + /* Write this entry again. */ + fputs (mappings[i]->hwaddr, fp); + fputc (',', fp); + fputs (mappings[i]->ipaddr, fp); + if (mappings[i]->hostname) { + fputc (',', fp); + fputs (mappings[i]->hostname, fp); + } + fputc ('\n', fp); + } + } + + fclose (fp); + + /* Tell dnsmasq to reread the configuration file. */ + kill (network->dnsmasqPid, SIGHUP); + + return 0; +} + static virDriver qemuDriver = { VIR_DRV_QEMU, "QEMU", @@ -2931,6 +3242,10 @@ qemudNetworkGetBridgeName, /* networkGetBridgeName */ qemudNetworkGetAutostart, /* networkGetAutostart */ qemudNetworkSetAutostart, /* networkSetAutostart */ + qemudNetworkNumOfDHCPHostMappings, /* networkNumOfDHCPHostMappings */ + qemudNetworkListDHCPHostMappings, /* networkListDHCPHostMappings */ + qemudNetworkAddDHCPHostMapping, /* networkAddDHCPHostMapping */ + qemudNetworkDeleteDHCPHostMapping, /* networkDeleteDHCPHostMapping */ }; static virStateDriver qemuStateDriver = { Index: src/qemu_driver.h =================================================================== RCS file: /data/cvs/libvirt/src/qemu_driver.h,v retrieving revision 1.4 diff -u -r1.4 qemu_driver.h --- src/qemu_driver.h 29 Jan 2008 18:15:54 -0000 1.4 +++ src/qemu_driver.h 21 Feb 2008 20:43:50 -0000 @@ -31,6 +31,9 @@ #include "internal.h" +/* %s is replaced with the network name. */ +#define DHCP_HOSTS_FORMAT LOCAL_STATE_DIR "/lib/libvirt/hosts-%s.conf" + int qemudRegister(void); #endif /* WITH_QEMU */