[libvirt] [PATCH v2 0/7] NSS module for libvirt

v2 of: https://www.redhat.com/archives/libvir-list/2016-February/msg00693.html diff to v1: - Rework to return multiple IPs instead of the first one found - Move the module into libvirt-nss rpm package - Fix linking (drop libvirt_util.la from LDADD) Michal Privoznik (7): Export virLease* functions for leases file handling virjson: Resolve const correctness Initial support for NSS plugin skeleton libvirt.spec.in: Introduce libvirt-nss package nss: Implement _nss_libvirt_gethostbyname3_r Implement _nss_libvirt_gethostbyname4_r nss: Introduce a test cfg.mk | 2 +- configure.ac | 2 + libvirt.spec.in | 21 ++ m4/virt-nss.m4 | 51 +++++ po/POTFILES.in | 1 + src/Makefile.am | 1 + src/libvirt_private.syms | 6 + src/network/leaseshelper.c | 271 +------------------------ src/util/virjson.c | 58 +++--- src/util/virjson.h | 54 ++--- src/util/virlease.c | 304 ++++++++++++++++++++++++++++ src/util/virlease.h | 44 ++++ tests/Makefile.am | 18 ++ tests/nssdata/virbr0.status | 14 ++ tests/nssmock.c | 140 +++++++++++++ tests/nsstest.c | 184 +++++++++++++++++ tools/Makefile.am | 44 ++++ tools/nss/libvirt_nss.c | 476 ++++++++++++++++++++++++++++++++++++++++++++ tools/nss/libvirt_nss.h | 52 +++++ tools/nss/libvirt_nss.syms | 12 ++ 20 files changed, 1428 insertions(+), 327 deletions(-) create mode 100644 m4/virt-nss.m4 create mode 100644 src/util/virlease.c create mode 100644 src/util/virlease.h create mode 100644 tests/nssdata/virbr0.status create mode 100644 tests/nssmock.c create mode 100644 tests/nsstest.c create mode 100644 tools/nss/libvirt_nss.c create mode 100644 tools/nss/libvirt_nss.h create mode 100644 tools/nss/libvirt_nss.syms -- 2.4.10

These functions are going to be reused very shortly. So instead of duplicating the code, lets move them into utils module. Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- po/POTFILES.in | 1 + src/Makefile.am | 1 + src/libvirt_private.syms | 6 + src/network/leaseshelper.c | 271 +--------------------------------------- src/util/virlease.c | 304 +++++++++++++++++++++++++++++++++++++++++++++ src/util/virlease.h | 44 +++++++ 6 files changed, 357 insertions(+), 270 deletions(-) create mode 100644 src/util/virlease.c create mode 100644 src/util/virlease.h diff --git a/po/POTFILES.in b/po/POTFILES.in index ff207cb..75a5785 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -206,6 +206,7 @@ src/util/viriptables.c src/util/viriscsi.c src/util/virjson.c src/util/virkeyfile.c +src/util/virlease.c src/util/virlockspace.c src/util/virnetdev.c src/util/virnetdevbandwidth.c diff --git a/src/Makefile.am b/src/Makefile.am index a316b4d..7d31839 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -126,6 +126,7 @@ UTIL_SOURCES = \ util/virkeycode.c util/virkeycode.h \ util/virkeyfile.c util/virkeyfile.h \ util/virkeymaps.h \ + util/virlease.c util/virlease.h \ util/virlockspace.c util/virlockspace.h \ util/virlog.c util/virlog.h \ util/virmacaddr.h util/virmacaddr.c \ diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 4b40612..11a6a34 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1718,6 +1718,12 @@ virKModLoad; virKModUnload; +# util/virlease.h +virLeaseNew; +virLeasePrintLeases; +virLeaseReadCustomLeaseFile; + + # util/virlockspace.h virLockSpaceAcquireResource; virLockSpaceCreateResource; diff --git a/src/network/leaseshelper.c b/src/network/leaseshelper.c index 55ddd58..94a6163 100644 --- a/src/network/leaseshelper.c +++ b/src/network/leaseshelper.c @@ -28,34 +28,18 @@ #include <locale.h> #include <stdio.h> #include <stdlib.h> -#include <sys/stat.h> -#include "virutil.h" #include "virthread.h" #include "virfile.h" #include "virpidfile.h" -#include "virbuffer.h" #include "virstring.h" #include "virerror.h" #include "viralloc.h" -#include "virjson.h" +#include "virlease.h" #include "configmake.h" #define VIR_FROM_THIS VIR_FROM_NETWORK -/** - * VIR_NETWORK_DHCP_LEASE_FILE_SIZE_MAX: - * - * Macro providing the upper limit on the size of leases file - */ -#define VIR_NETWORK_DHCP_LEASE_FILE_SIZE_MAX (32 * 1024 * 1024) - -/* - * Use this when passing possibly-NULL strings to printf-a-likes. - * Required for unknown parameters during init call. - */ -#define EMPTY_STR(s) ((s) ? (s) : "*") - static const char *program_name; /* Display version information. */ @@ -105,259 +89,6 @@ VIR_ENUM_DECL(virLeaseAction); VIR_ENUM_IMPL(virLeaseAction, VIR_LEASE_ACTION_LAST, "add", "old", "del", "init"); -static int -virLeaseReadCustomLeaseFile(virJSONValuePtr leases_array_new, - const char *custom_lease_file, - const char *ip_to_delete, - char **server_duid) -{ - char *lease_entries = NULL; - virJSONValuePtr leases_array = NULL; - long long currtime = 0; - long long expirytime; - int custom_lease_file_len = 0; - virJSONValuePtr lease_tmp = NULL; - const char *ip_tmp = NULL; - const char *server_duid_tmp = NULL; - size_t i; - int ret = -1; - - currtime = (long long) time(NULL); - - /* Read entire contents */ - if ((custom_lease_file_len = virFileReadAll(custom_lease_file, - VIR_NETWORK_DHCP_LEASE_FILE_SIZE_MAX, - &lease_entries)) < 0) { - goto cleanup; - } - - /* Check for previous leases */ - if (custom_lease_file_len == 0) { - ret = 0; - goto cleanup; - } - - if (!(leases_array = virJSONValueFromString(lease_entries))) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("invalid json in file: %s, rewriting it"), - custom_lease_file); - ret = 0; - goto cleanup; - } - - if (!virJSONValueIsArray(leases_array)) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("couldn't fetch array of leases")); - goto cleanup; - } - - i = 0; - while (i < virJSONValueArraySize(leases_array)) { - if (!(lease_tmp = virJSONValueArrayGet(leases_array, i))) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("failed to parse json")); - goto cleanup; - } - - if (!(ip_tmp = virJSONValueObjectGetString(lease_tmp, "ip-address")) || - (virJSONValueObjectGetNumberLong(lease_tmp, "expiry-time", &expirytime) < 0)) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("failed to parse json")); - goto cleanup; - } - /* Check whether lease has expired or not */ - if (expirytime < currtime) { - i++; - continue; - } - - /* Check whether lease has to be included or not */ - if (ip_to_delete && STREQ(ip_tmp, ip_to_delete)) { - i++; - continue; - } - - if (strchr(ip_tmp, ':')) { - /* This is an ipv6 lease */ - if ((server_duid_tmp - = virJSONValueObjectGetString(lease_tmp, "server-duid"))) { - if (!*server_duid && VIR_STRDUP(*server_duid, server_duid_tmp) < 0) { - /* Control reaches here when the 'action' is not for an - * ipv6 lease or, for some weird reason the env var - * DNSMASQ_SERVER_DUID wasn't set*/ - goto cleanup; - } - } else { - /* Inject server-duid into those ipv6 leases which - * didn't have it previously, for example, those - * created by leaseshelper from libvirt 1.2.6 */ - if (virJSONValueObjectAppendString(lease_tmp, "server-duid", *server_duid) < 0) - goto cleanup; - } - } - - /* Move old lease to new array */ - if (virJSONValueArrayAppend(leases_array_new, lease_tmp) < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("failed to create json")); - goto cleanup; - } - - ignore_value(virJSONValueArraySteal(leases_array, i)); - } - - ret = 0; - - cleanup: - virJSONValueFree(leases_array); - VIR_FREE(lease_entries); - return ret; -} - -static int -virLeasePrintLeases(virJSONValuePtr leases_array_new, - const char *server_duid) -{ - virJSONValuePtr lease_tmp = NULL; - const char *ip_tmp = NULL; - long long expirytime = 0; - int ret = -1; - size_t i; - - /* Man page of dnsmasq says: the script (helper program, in our case) - * should write the saved state of the lease database, in dnsmasq - * leasefile format, to stdout and exit with zero exit code, when - * called with argument init. Format: - * $expirytime $mac $ip $hostname $clientid # For all ipv4 leases - * duid $server-duid # If DHCPv6 is present - * $expirytime $iaid $ip $hostname $clientduid # For all ipv6 leases */ - - /* Traversing the ipv4 leases */ - for (i = 0; i < virJSONValueArraySize(leases_array_new); i++) { - lease_tmp = virJSONValueArrayGet(leases_array_new, i); - if (!(ip_tmp = virJSONValueObjectGetString(lease_tmp, "ip-address"))) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("failed to parse json")); - goto cleanup; - } - if (!strchr(ip_tmp, ':')) { - if (virJSONValueObjectGetNumberLong(lease_tmp, "expiry-time", - &expirytime) < 0) - continue; - - printf("%lld %s %s %s %s\n", - expirytime, - virJSONValueObjectGetString(lease_tmp, "mac-address"), - virJSONValueObjectGetString(lease_tmp, "ip-address"), - EMPTY_STR(virJSONValueObjectGetString(lease_tmp, "hostname")), - EMPTY_STR(virJSONValueObjectGetString(lease_tmp, "client-id"))); - } - } - - /* Traversing the ipv6 leases */ - if (server_duid) { - printf("duid %s\n", server_duid); - for (i = 0; i < virJSONValueArraySize(leases_array_new); i++) { - lease_tmp = virJSONValueArrayGet(leases_array_new, i); - if (!(ip_tmp = virJSONValueObjectGetString(lease_tmp, "ip-address"))) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("failed to parse json")); - goto cleanup; - } - if (strchr(ip_tmp, ':')) { - if (virJSONValueObjectGetNumberLong(lease_tmp, "expiry-time", - &expirytime) < 0) - continue; - - printf("%lld %s %s %s %s\n", - expirytime, - virJSONValueObjectGetString(lease_tmp, "iaid"), - virJSONValueObjectGetString(lease_tmp, "ip-address"), - EMPTY_STR(virJSONValueObjectGetString(lease_tmp, "hostname")), - EMPTY_STR(virJSONValueObjectGetString(lease_tmp, "client-id"))); - } - } - } - - ret = 0; - - cleanup: - return ret; -} - -static int -virLeaseNew(virJSONValuePtr *lease_ret, - const char *mac, - const char *clientid, - const char *ip, - const char *hostname, - const char *iaid, - const char *server_duid) -{ - virJSONValuePtr lease_new = NULL; - const char *exptime_tmp = virGetEnvAllowSUID("DNSMASQ_LEASE_EXPIRES"); - long long expirytime = 0; - char *exptime = NULL; - int ret = -1; - - /* In case hostname is still unknown, use the last known one */ - if (!hostname) - hostname = virGetEnvAllowSUID("DNSMASQ_OLD_HOSTNAME"); - - if (!mac) { - ret = 0; - goto cleanup; - } - - if (exptime_tmp) { - if (VIR_STRDUP(exptime, exptime_tmp) < 0) - goto cleanup; - - /* Removed extraneous trailing space in DNSMASQ_LEASE_EXPIRES - * (dnsmasq < 2.52) */ - if (exptime[strlen(exptime) - 1] == ' ') - exptime[strlen(exptime) - 1] = '\0'; - } - - if (!exptime || - virStrToLong_ll(exptime, NULL, 10, &expirytime) < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("Unable to convert lease expiry time to long long: %s"), - NULLSTR(exptime)); - goto cleanup; - } - - /* Create new lease */ - if (!(lease_new = virJSONValueNewObject())) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("failed to create json")); - goto cleanup; - } - - if (iaid && virJSONValueObjectAppendString(lease_new, "iaid", iaid) < 0) - goto cleanup; - if (ip && virJSONValueObjectAppendString(lease_new, "ip-address", ip) < 0) - goto cleanup; - if (mac && virJSONValueObjectAppendString(lease_new, "mac-address", mac) < 0) - goto cleanup; - if (hostname && virJSONValueObjectAppendString(lease_new, "hostname", hostname) < 0) - goto cleanup; - if (clientid && virJSONValueObjectAppendString(lease_new, "client-id", clientid) < 0) - goto cleanup; - if (server_duid && virJSONValueObjectAppendString(lease_new, "server-duid", server_duid) < 0) - goto cleanup; - if (expirytime && virJSONValueObjectAppendNumberLong(lease_new, "expiry-time", expirytime) < 0) - goto cleanup; - - ret = 0; - *lease_ret = lease_new; - lease_new = NULL; - cleanup: - VIR_FREE(exptime); - virJSONValueFree(lease_new); - return ret; -} - int main(int argc, char **argv) { diff --git a/src/util/virlease.c b/src/util/virlease.c new file mode 100644 index 0000000..b8e9d8b --- /dev/null +++ b/src/util/virlease.c @@ -0,0 +1,304 @@ +/* + * virlease.c: Leases file handling + * + * Copyright (C) 2014 Red Hat, Inc. + * Copyright (C) 2014 Nehal J Wani + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + * + */ + +#include <config.h> + +#include "virlease.h" + +#include <time.h> + +#include "virfile.h" +#include "virstring.h" +#include "virerror.h" +#include "viralloc.h" + +#define VIR_FROM_THIS VIR_FROM_NETWORK + +/** + * VIR_NETWORK_DHCP_LEASE_FILE_SIZE_MAX: + * + * Macro providing the upper limit on the size of leases file + */ +#define VIR_NETWORK_DHCP_LEASE_FILE_SIZE_MAX (32 * 1024 * 1024) + + +/* + * Use this when passing possibly-NULL strings to printf-a-likes. + * Required for unknown parameters during init call. + */ +#define EMPTY_STR(s) ((s) ? (s) : "*") + + +int +virLeaseReadCustomLeaseFile(virJSONValuePtr leases_array_new, + const char *custom_lease_file, + const char *ip_to_delete, + char **server_duid) +{ + char *lease_entries = NULL; + virJSONValuePtr leases_array = NULL; + long long currtime = 0; + long long expirytime; + int custom_lease_file_len = 0; + virJSONValuePtr lease_tmp = NULL; + const char *ip_tmp = NULL; + const char *server_duid_tmp = NULL; + size_t i; + int ret = -1; + + currtime = (long long) time(NULL); + + /* Read entire contents */ + if ((custom_lease_file_len = virFileReadAll(custom_lease_file, + VIR_NETWORK_DHCP_LEASE_FILE_SIZE_MAX, + &lease_entries)) < 0) { + goto cleanup; + } + + /* Check for previous leases */ + if (custom_lease_file_len == 0) { + ret = 0; + goto cleanup; + } + + if (!(leases_array = virJSONValueFromString(lease_entries))) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("invalid json in file: %s, rewriting it"), + custom_lease_file); + ret = 0; + goto cleanup; + } + + if (!virJSONValueIsArray(leases_array)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("couldn't fetch array of leases")); + goto cleanup; + } + + i = 0; + while (i < virJSONValueArraySize(leases_array)) { + if (!(lease_tmp = virJSONValueArrayGet(leases_array, i))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to parse json")); + goto cleanup; + } + + if (!(ip_tmp = virJSONValueObjectGetString(lease_tmp, "ip-address")) || + (virJSONValueObjectGetNumberLong(lease_tmp, "expiry-time", &expirytime) < 0)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to parse json")); + goto cleanup; + } + /* Check whether lease has expired or not */ + if (expirytime < currtime) { + i++; + continue; + } + + /* Check whether lease has to be included or not */ + if (ip_to_delete && STREQ(ip_tmp, ip_to_delete)) { + i++; + continue; + } + + if (strchr(ip_tmp, ':')) { + /* This is an ipv6 lease */ + if ((server_duid_tmp + = virJSONValueObjectGetString(lease_tmp, "server-duid"))) { + if (!*server_duid && VIR_STRDUP(*server_duid, server_duid_tmp) < 0) { + /* Control reaches here when the 'action' is not for an + * ipv6 lease or, for some weird reason the env var + * DNSMASQ_SERVER_DUID wasn't set*/ + goto cleanup; + } + } else { + /* Inject server-duid into those ipv6 leases which + * didn't have it previously, for example, those + * created by leaseshelper from libvirt 1.2.6 */ + if (virJSONValueObjectAppendString(lease_tmp, "server-duid", *server_duid) < 0) + goto cleanup; + } + } + + /* Move old lease to new array */ + if (virJSONValueArrayAppend(leases_array_new, lease_tmp) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to create json")); + goto cleanup; + } + + ignore_value(virJSONValueArraySteal(leases_array, i)); + } + + ret = 0; + + cleanup: + virJSONValueFree(leases_array); + VIR_FREE(lease_entries); + return ret; +} + + +int +virLeasePrintLeases(virJSONValuePtr leases_array_new, + const char *server_duid) +{ + virJSONValuePtr lease_tmp = NULL; + const char *ip_tmp = NULL; + long long expirytime = 0; + int ret = -1; + size_t i; + + /* Man page of dnsmasq says: the script (helper program, in our case) + * should write the saved state of the lease database, in dnsmasq + * leasefile format, to stdout and exit with zero exit code, when + * called with argument init. Format: + * $expirytime $mac $ip $hostname $clientid # For all ipv4 leases + * duid $server-duid # If DHCPv6 is present + * $expirytime $iaid $ip $hostname $clientduid # For all ipv6 leases */ + + /* Traversing the ipv4 leases */ + for (i = 0; i < virJSONValueArraySize(leases_array_new); i++) { + lease_tmp = virJSONValueArrayGet(leases_array_new, i); + if (!(ip_tmp = virJSONValueObjectGetString(lease_tmp, "ip-address"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to parse json")); + goto cleanup; + } + if (!strchr(ip_tmp, ':')) { + if (virJSONValueObjectGetNumberLong(lease_tmp, "expiry-time", + &expirytime) < 0) + continue; + + printf("%lld %s %s %s %s\n", + expirytime, + virJSONValueObjectGetString(lease_tmp, "mac-address"), + virJSONValueObjectGetString(lease_tmp, "ip-address"), + EMPTY_STR(virJSONValueObjectGetString(lease_tmp, "hostname")), + EMPTY_STR(virJSONValueObjectGetString(lease_tmp, "client-id"))); + } + } + + /* Traversing the ipv6 leases */ + if (server_duid) { + printf("duid %s\n", server_duid); + for (i = 0; i < virJSONValueArraySize(leases_array_new); i++) { + lease_tmp = virJSONValueArrayGet(leases_array_new, i); + if (!(ip_tmp = virJSONValueObjectGetString(lease_tmp, "ip-address"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to parse json")); + goto cleanup; + } + if (strchr(ip_tmp, ':')) { + if (virJSONValueObjectGetNumberLong(lease_tmp, "expiry-time", + &expirytime) < 0) + continue; + + printf("%lld %s %s %s %s\n", + expirytime, + virJSONValueObjectGetString(lease_tmp, "iaid"), + virJSONValueObjectGetString(lease_tmp, "ip-address"), + EMPTY_STR(virJSONValueObjectGetString(lease_tmp, "hostname")), + EMPTY_STR(virJSONValueObjectGetString(lease_tmp, "client-id"))); + } + } + } + + ret = 0; + + cleanup: + return ret; +} + + +int +virLeaseNew(virJSONValuePtr *lease_ret, + const char *mac, + const char *clientid, + const char *ip, + const char *hostname, + const char *iaid, + const char *server_duid) +{ + virJSONValuePtr lease_new = NULL; + const char *exptime_tmp = virGetEnvAllowSUID("DNSMASQ_LEASE_EXPIRES"); + long long expirytime = 0; + char *exptime = NULL; + int ret = -1; + + /* In case hostname is still unknown, use the last known one */ + if (!hostname) + hostname = virGetEnvAllowSUID("DNSMASQ_OLD_HOSTNAME"); + + if (!mac) { + ret = 0; + goto cleanup; + } + + if (exptime_tmp) { + if (VIR_STRDUP(exptime, exptime_tmp) < 0) + goto cleanup; + + /* Removed extraneous trailing space in DNSMASQ_LEASE_EXPIRES + * (dnsmasq < 2.52) */ + if (exptime[strlen(exptime) - 1] == ' ') + exptime[strlen(exptime) - 1] = '\0'; + } + + if (!exptime || + virStrToLong_ll(exptime, NULL, 10, &expirytime) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to convert lease expiry time to long long: %s"), + NULLSTR(exptime)); + goto cleanup; + } + + /* Create new lease */ + if (!(lease_new = virJSONValueNewObject())) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to create json")); + goto cleanup; + } + + if (iaid && virJSONValueObjectAppendString(lease_new, "iaid", iaid) < 0) + goto cleanup; + if (ip && virJSONValueObjectAppendString(lease_new, "ip-address", ip) < 0) + goto cleanup; + if (mac && virJSONValueObjectAppendString(lease_new, "mac-address", mac) < 0) + goto cleanup; + if (hostname && virJSONValueObjectAppendString(lease_new, "hostname", hostname) < 0) + goto cleanup; + if (clientid && virJSONValueObjectAppendString(lease_new, "client-id", clientid) < 0) + goto cleanup; + if (server_duid && virJSONValueObjectAppendString(lease_new, "server-duid", server_duid) < 0) + goto cleanup; + if (expirytime && virJSONValueObjectAppendNumberLong(lease_new, "expiry-time", expirytime) < 0) + goto cleanup; + + ret = 0; + *lease_ret = lease_new; + lease_new = NULL; + cleanup: + VIR_FREE(exptime); + virJSONValueFree(lease_new); + return ret; +} diff --git a/src/util/virlease.h b/src/util/virlease.h new file mode 100644 index 0000000..b9f394e --- /dev/null +++ b/src/util/virlease.h @@ -0,0 +1,44 @@ +/* + * virlease.h: Leases file handling + * + * Copyright (C) 2014 Red Hat, Inc. + * Copyright (C) 2014 Nehal J Wani + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + * + */ + +#ifndef __VIR_LEASE_H_ +# define __VIR_LEASE_H_ + +# include "virjson.h" + +int virLeaseReadCustomLeaseFile(virJSONValuePtr leases_array_new, + const char *custom_lease_file, + const char *ip_to_delete, + char **server_duid); + +int virLeasePrintLeases(virJSONValuePtr leases_array_new, + const char *server_duid); + + +int virLeaseNew(virJSONValuePtr *lease_ret, + const char *mac, + const char *clientid, + const char *ip, + const char *hostname, + const char *iaid, + const char *server_duid); +#endif /* __VIR_LEASE_H */ -- 2.4.10

On Thu, Feb 18, 2016 at 03:21:01PM +0100, Michal Privoznik wrote:
These functions are going to be reused very shortly. So instead of duplicating the code, lets move them into utils module.
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- po/POTFILES.in | 1 + src/Makefile.am | 1 + src/libvirt_private.syms | 6 + src/network/leaseshelper.c | 271 +--------------------------------------- src/util/virlease.c | 304 +++++++++++++++++++++++++++++++++++++++++++++ src/util/virlease.h | 44 +++++++ 6 files changed, 357 insertions(+), 270 deletions(-) create mode 100644 src/util/virlease.c create mode 100644 src/util/virlease.h
ACK Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

Plenty of our virJSON*() APIs don't modify passed object. They merely get a value stored in it. Note this fact in their definition and enforce const correctness. Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/util/virjson.c | 58 +++++++++++++++++++++++++++--------------------------- src/util/virjson.h | 54 +++++++++++++++++++++++++------------------------- 2 files changed, 56 insertions(+), 56 deletions(-) diff --git a/src/util/virjson.c b/src/util/virjson.c index ae6362b..0a595b9 100644 --- a/src/util/virjson.c +++ b/src/util/virjson.c @@ -735,7 +735,7 @@ virJSONValueArrayAppend(virJSONValuePtr array, int -virJSONValueObjectHasKey(virJSONValuePtr object, +virJSONValueObjectHasKey(const virJSONValue *object, const char *key) { size_t i; @@ -753,7 +753,7 @@ virJSONValueObjectHasKey(virJSONValuePtr object, virJSONValuePtr -virJSONValueObjectGet(virJSONValuePtr object, +virJSONValueObjectGet(const virJSONValue *object, const char *key) { size_t i; @@ -773,7 +773,7 @@ virJSONValueObjectGet(virJSONValuePtr object, /* Return the value associated with KEY within OBJECT, but return NULL * if the key is missing or if value is not the correct TYPE. */ virJSONValuePtr -virJSONValueObjectGetByType(virJSONValuePtr object, +virJSONValueObjectGetByType(const virJSONValue *object, const char *key, virJSONType type) { @@ -786,7 +786,7 @@ virJSONValueObjectGetByType(virJSONValuePtr object, int -virJSONValueObjectKeysNumber(virJSONValuePtr object) +virJSONValueObjectKeysNumber(const virJSONValue *object) { if (object->type != VIR_JSON_TYPE_OBJECT) return -1; @@ -796,7 +796,7 @@ virJSONValueObjectKeysNumber(virJSONValuePtr object) const char * -virJSONValueObjectGetKey(virJSONValuePtr object, +virJSONValueObjectGetKey(const virJSONValue *object, unsigned int n) { if (object->type != VIR_JSON_TYPE_OBJECT) @@ -844,7 +844,7 @@ virJSONValueObjectRemoveKey(virJSONValuePtr object, virJSONValuePtr -virJSONValueObjectGetValue(virJSONValuePtr object, +virJSONValueObjectGetValue(const virJSONValue *object, unsigned int n) { if (object->type != VIR_JSON_TYPE_OBJECT) @@ -858,7 +858,7 @@ virJSONValueObjectGetValue(virJSONValuePtr object, bool -virJSONValueIsArray(virJSONValuePtr array) +virJSONValueIsArray(const virJSONValue *array) { return array->type == VIR_JSON_TYPE_ARRAY; } @@ -875,7 +875,7 @@ virJSONValueArraySize(const virJSONValue *array) virJSONValuePtr -virJSONValueArrayGet(virJSONValuePtr array, +virJSONValueArrayGet(const virJSONValue *array, unsigned int element) { if (array->type != VIR_JSON_TYPE_ARRAY) @@ -911,7 +911,7 @@ virJSONValueArraySteal(virJSONValuePtr array, const char * -virJSONValueGetString(virJSONValuePtr string) +virJSONValueGetString(const virJSONValue *string) { if (string->type != VIR_JSON_TYPE_STRING) return NULL; @@ -921,7 +921,7 @@ virJSONValueGetString(virJSONValuePtr string) int -virJSONValueGetNumberInt(virJSONValuePtr number, +virJSONValueGetNumberInt(const virJSONValue *number, int *value) { if (number->type != VIR_JSON_TYPE_NUMBER) @@ -932,7 +932,7 @@ virJSONValueGetNumberInt(virJSONValuePtr number, int -virJSONValueGetNumberUint(virJSONValuePtr number, +virJSONValueGetNumberUint(const virJSONValue *number, unsigned int *value) { if (number->type != VIR_JSON_TYPE_NUMBER) @@ -943,7 +943,7 @@ virJSONValueGetNumberUint(virJSONValuePtr number, int -virJSONValueGetNumberLong(virJSONValuePtr number, +virJSONValueGetNumberLong(const virJSONValue *number, long long *value) { if (number->type != VIR_JSON_TYPE_NUMBER) @@ -954,7 +954,7 @@ virJSONValueGetNumberLong(virJSONValuePtr number, int -virJSONValueGetNumberUlong(virJSONValuePtr number, +virJSONValueGetNumberUlong(const virJSONValue *number, unsigned long long *value) { if (number->type != VIR_JSON_TYPE_NUMBER) @@ -965,7 +965,7 @@ virJSONValueGetNumberUlong(virJSONValuePtr number, int -virJSONValueGetNumberDouble(virJSONValuePtr number, +virJSONValueGetNumberDouble(const virJSONValue *number, double *value) { if (number->type != VIR_JSON_TYPE_NUMBER) @@ -976,7 +976,7 @@ virJSONValueGetNumberDouble(virJSONValuePtr number, int -virJSONValueGetBoolean(virJSONValuePtr val, +virJSONValueGetBoolean(const virJSONValue *val, bool *value) { if (val->type != VIR_JSON_TYPE_BOOLEAN) @@ -1077,14 +1077,14 @@ virJSONValueNewArrayFromBitmap(virBitmapPtr bitmap) bool -virJSONValueIsNull(virJSONValuePtr val) +virJSONValueIsNull(const virJSONValue *val) { return val->type == VIR_JSON_TYPE_NULL; } const char * -virJSONValueObjectGetString(virJSONValuePtr object, +virJSONValueObjectGetString(const virJSONValue *object, const char *key) { virJSONValuePtr val = virJSONValueObjectGet(object, key); @@ -1097,7 +1097,7 @@ virJSONValueObjectGetString(virJSONValuePtr object, int -virJSONValueObjectGetNumberInt(virJSONValuePtr object, +virJSONValueObjectGetNumberInt(const virJSONValue *object, const char *key, int *value) { @@ -1111,7 +1111,7 @@ virJSONValueObjectGetNumberInt(virJSONValuePtr object, int -virJSONValueObjectGetNumberUint(virJSONValuePtr object, +virJSONValueObjectGetNumberUint(const virJSONValue *object, const char *key, unsigned int *value) { @@ -1125,7 +1125,7 @@ virJSONValueObjectGetNumberUint(virJSONValuePtr object, int -virJSONValueObjectGetNumberLong(virJSONValuePtr object, +virJSONValueObjectGetNumberLong(const virJSONValue *object, const char *key, long long *value) { @@ -1139,7 +1139,7 @@ virJSONValueObjectGetNumberLong(virJSONValuePtr object, int -virJSONValueObjectGetNumberUlong(virJSONValuePtr object, +virJSONValueObjectGetNumberUlong(const virJSONValue *object, const char *key, unsigned long long *value) { @@ -1153,7 +1153,7 @@ virJSONValueObjectGetNumberUlong(virJSONValuePtr object, int -virJSONValueObjectGetNumberDouble(virJSONValuePtr object, +virJSONValueObjectGetNumberDouble(const virJSONValue *object, const char *key, double *value) { @@ -1167,7 +1167,7 @@ virJSONValueObjectGetNumberDouble(virJSONValuePtr object, int -virJSONValueObjectGetBoolean(virJSONValuePtr object, +virJSONValueObjectGetBoolean(const virJSONValue *object, const char *key, bool *value) { @@ -1195,7 +1195,7 @@ virJSONValueObjectGetArray(virJSONValuePtr object, const char *key) int -virJSONValueObjectIsNull(virJSONValuePtr object, +virJSONValueObjectIsNull(const virJSONValue *object, const char *key) { virJSONValuePtr val = virJSONValueObjectGet(object, key); @@ -1220,7 +1220,7 @@ virJSONValueObjectIsNull(virJSONValuePtr object, * during iteration and -1 on generic errors. */ int -virJSONValueObjectForeachKeyValue(virJSONValuePtr object, +virJSONValueObjectForeachKeyValue(const virJSONValue *object, virJSONValueObjectIteratorFunc cb, void *opaque) { @@ -1241,7 +1241,7 @@ virJSONValueObjectForeachKeyValue(virJSONValuePtr object, virJSONValuePtr -virJSONValueCopy(virJSONValuePtr in) +virJSONValueCopy(const virJSONValue *in) { size_t i; virJSONValuePtr out = NULL; @@ -1683,7 +1683,7 @@ virJSONValueFromString(const char *jsonstring) static int -virJSONValueToStringOne(virJSONValuePtr object, +virJSONValueToStringOne(const virJSONValue *object, yajl_gen g) { size_t i; @@ -1748,7 +1748,7 @@ virJSONValueToStringOne(virJSONValuePtr object, char * -virJSONValueToString(virJSONValuePtr object, +virJSONValueToString(const virJSONValue *object, bool pretty) { yajl_gen g; @@ -1809,7 +1809,7 @@ virJSONValueFromString(const char *jsonstring ATTRIBUTE_UNUSED) char * -virJSONValueToString(virJSONValuePtr object ATTRIBUTE_UNUSED, +virJSONValueToString(const virJSONValue *object ATTRIBUTE_UNUSED, bool pretty ATTRIBUTE_UNUSED) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", diff --git a/src/util/virjson.h b/src/util/virjson.h index 66ed48a..133e976 100644 --- a/src/util/virjson.h +++ b/src/util/virjson.h @@ -108,43 +108,43 @@ virJSONValuePtr virJSONValueNewArrayFromBitmap(virBitmapPtr bitmap); int virJSONValueObjectAppend(virJSONValuePtr object, const char *key, virJSONValuePtr value); int virJSONValueArrayAppend(virJSONValuePtr object, virJSONValuePtr value); -int virJSONValueObjectHasKey(virJSONValuePtr object, const char *key); -virJSONValuePtr virJSONValueObjectGet(virJSONValuePtr object, const char *key); -virJSONValuePtr virJSONValueObjectGetByType(virJSONValuePtr object, +int virJSONValueObjectHasKey(const virJSONValue *object, const char *key); +virJSONValuePtr virJSONValueObjectGet(const virJSONValue *object, const char *key); +virJSONValuePtr virJSONValueObjectGetByType(const virJSONValue *object, const char *key, virJSONType type); -bool virJSONValueIsArray(virJSONValuePtr array); +bool virJSONValueIsArray(const virJSONValue *array); ssize_t virJSONValueArraySize(const virJSONValue *array); -virJSONValuePtr virJSONValueArrayGet(virJSONValuePtr object, unsigned int element); +virJSONValuePtr virJSONValueArrayGet(const virJSONValue *object, unsigned int element); virJSONValuePtr virJSONValueArraySteal(virJSONValuePtr object, unsigned int element); -int virJSONValueObjectKeysNumber(virJSONValuePtr object); -const char *virJSONValueObjectGetKey(virJSONValuePtr object, unsigned int n); -virJSONValuePtr virJSONValueObjectGetValue(virJSONValuePtr object, unsigned int n); +int virJSONValueObjectKeysNumber(const virJSONValue *object); +const char *virJSONValueObjectGetKey(const virJSONValue *object, unsigned int n); +virJSONValuePtr virJSONValueObjectGetValue(const virJSONValue *object, unsigned int n); -const char *virJSONValueGetString(virJSONValuePtr object); -int virJSONValueGetNumberInt(virJSONValuePtr object, int *value); -int virJSONValueGetNumberUint(virJSONValuePtr object, unsigned int *value); -int virJSONValueGetNumberLong(virJSONValuePtr object, long long *value); -int virJSONValueGetNumberUlong(virJSONValuePtr object, unsigned long long *value); -int virJSONValueGetNumberDouble(virJSONValuePtr object, double *value); -int virJSONValueGetBoolean(virJSONValuePtr object, bool *value); +const char *virJSONValueGetString(const virJSONValue *object); +int virJSONValueGetNumberInt(const virJSONValue *object, int *value); +int virJSONValueGetNumberUint(const virJSONValue *object, unsigned int *value); +int virJSONValueGetNumberLong(const virJSONValue *object, long long *value); +int virJSONValueGetNumberUlong(const virJSONValue *object, unsigned long long *value); +int virJSONValueGetNumberDouble(const virJSONValue *object, double *value); +int virJSONValueGetBoolean(const virJSONValue *object, bool *value); int virJSONValueGetArrayAsBitmap(const virJSONValue *val, virBitmapPtr *bitmap) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); -bool virJSONValueIsNull(virJSONValuePtr object); +bool virJSONValueIsNull(const virJSONValue *object); virJSONValuePtr virJSONValueObjectGetObject(virJSONValuePtr object, const char *key); virJSONValuePtr virJSONValueObjectGetArray(virJSONValuePtr object, const char *key); -const char *virJSONValueObjectGetString(virJSONValuePtr object, const char *key); -int virJSONValueObjectGetNumberInt(virJSONValuePtr object, const char *key, int *value); -int virJSONValueObjectGetNumberUint(virJSONValuePtr object, const char *key, unsigned int *value); -int virJSONValueObjectGetNumberLong(virJSONValuePtr object, const char *key, long long *value); -int virJSONValueObjectGetNumberUlong(virJSONValuePtr object, const char *key, unsigned long long *value); -int virJSONValueObjectGetNumberDouble(virJSONValuePtr object, const char *key, double *value); -int virJSONValueObjectGetBoolean(virJSONValuePtr object, const char *key, bool *value); -int virJSONValueObjectIsNull(virJSONValuePtr object, const char *key); +const char *virJSONValueObjectGetString(const virJSONValue *object, const char *key); +int virJSONValueObjectGetNumberInt(const virJSONValue *object, const char *key, int *value); +int virJSONValueObjectGetNumberUint(const virJSONValue *object, const char *key, unsigned int *value); +int virJSONValueObjectGetNumberLong(const virJSONValue *object, const char *key, long long *value); +int virJSONValueObjectGetNumberUlong(const virJSONValue *object, const char *key, unsigned long long *value); +int virJSONValueObjectGetNumberDouble(const virJSONValue *object, const char *key, double *value); +int virJSONValueObjectGetBoolean(const virJSONValue *object, const char *key, bool *value); +int virJSONValueObjectIsNull(const virJSONValue *object, const char *key); int virJSONValueObjectAppendString(virJSONValuePtr object, const char *key, const char *value); int virJSONValueObjectAppendNumberInt(virJSONValuePtr object, const char *key, int number); @@ -160,17 +160,17 @@ int virJSONValueObjectRemoveKey(virJSONValuePtr object, const char *key, ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); virJSONValuePtr virJSONValueFromString(const char *jsonstring); -char *virJSONValueToString(virJSONValuePtr object, +char *virJSONValueToString(const virJSONValue *object, bool pretty); typedef int (*virJSONValueObjectIteratorFunc)(const char *key, const virJSONValue *value, void *opaque); -int virJSONValueObjectForeachKeyValue(virJSONValuePtr object, +int virJSONValueObjectForeachKeyValue(const virJSONValue *object, virJSONValueObjectIteratorFunc cb, void *opaque); -virJSONValuePtr virJSONValueCopy(virJSONValuePtr in); +virJSONValuePtr virJSONValueCopy(const virJSONValue *in); #endif /* __VIR_JSON_H_ */ -- 2.4.10

On Thu, Feb 18, 2016 at 03:21:02PM +0100, Michal Privoznik wrote:
Plenty of our virJSON*() APIs don't modify passed object. They merely get a value stored in it. Note this fact in their definition and enforce const correctness.
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/util/virjson.c | 58 +++++++++++++++++++++++++++--------------------------- src/util/virjson.h | 54 +++++++++++++++++++++++++------------------------- 2 files changed, 56 insertions(+), 56 deletions(-)
ACK Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

Name Service Switch is a glibc feature responsible for many things. Translating domain names into IP addresses and vice versa is just one of them. However, currently it's the only functionality that this commit is tickling. Well, in this commit the plugin skeleton is introduced. Implementation to come in next patches. Because of the future testing, where the implementation is to be linked with a test, this needs to go into static library. Linking a program with an .so statically is not portable. Therefore a dummy libnss_libvirt_impl library is being introduced too. Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- configure.ac | 2 ++ m4/virt-nss.m4 | 51 ++++++++++++++++++++++++++++++++++++++++++++++ tools/Makefile.am | 39 +++++++++++++++++++++++++++++++++++ tools/nss/libvirt_nss.c | 36 ++++++++++++++++++++++++++++++++ tools/nss/libvirt_nss.h | 36 ++++++++++++++++++++++++++++++++ tools/nss/libvirt_nss.syms | 9 ++++++++ 6 files changed, 173 insertions(+) create mode 100644 m4/virt-nss.m4 create mode 100644 tools/nss/libvirt_nss.c create mode 100644 tools/nss/libvirt_nss.h create mode 100644 tools/nss/libvirt_nss.syms diff --git a/configure.ac b/configure.ac index 93d347c..f790e6a 100644 --- a/configure.ac +++ b/configure.ac @@ -257,6 +257,7 @@ LIBVIRT_CHECK_SSH2 LIBVIRT_CHECK_SYSTEMD_DAEMON LIBVIRT_CHECK_UDEV LIBVIRT_CHECK_WIRESHARK +LIBVIRT_CHECK_NSS LIBVIRT_CHECK_YAJL AC_MSG_CHECKING([for CPUID instruction]) @@ -2843,6 +2844,7 @@ LIBVIRT_RESULT_SSH2 LIBVIRT_RESULT_SYSTEMD_DAEMON LIBVIRT_RESULT_UDEV LIBVIRT_RESULT_WIRESHARK +LIBVIRT_RESULT_NSS LIBVIRT_RESULT_YAJL AC_MSG_NOTICE([ libxml: $LIBXML_CFLAGS $LIBXML_LIBS]) AC_MSG_NOTICE([ dlopen: $DLOPEN_LIBS]) diff --git a/m4/virt-nss.m4 b/m4/virt-nss.m4 new file mode 100644 index 0000000..207cd34 --- /dev/null +++ b/m4/virt-nss.m4 @@ -0,0 +1,51 @@ +dnl The libvirt nsswitch plugin +dnl +dnl Copyright (C) 2016 Red Hat, Inc. +dnl +dnl This library is free software; you can redistribute it and/or +dnl modify it under the terms of the GNU Lesser General Public +dnl License as published by the Free Software Foundation; either +dnl version 2.1 of the License, or (at your option) any later version. +dnl +dnl This library is distributed in the hope that it will be useful, +dnl but WITHOUT ANY WARRANTY; without even the implied warranty of +dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +dnl Lesser General Public License for more details. +dnl +dnl You should have received a copy of the GNU Lesser General Public +dnl License along with this library. If not, see +dnl <http://www.gnu.org/licenses/>. +dnl + +AC_DEFUN([LIBVIRT_CHECK_NSS],[ + AC_ARG_WITH([nss-plugin], + [AS_HELP_STRING([--with-nss-plugin], + [enable Name Servie Switch plugin for resolving guest IP addresses])], + [], [with_nss_plugin=check]) + + fail=0 + if test "x$with_nss_plugin" != "xno" ; then + AC_CHECK_HEADERS([nss.h], [ + with_nss_plugin=yes + ],[ + if test "x$with_nss_plugin" = "xyes" ; then + fail = 1 + fi + ]) + + if test $fail = 1 ; then + AC_MSG_ERROR([Can't build nss plugin without nss.h]) + fi + + if test "x$with_nss_plugin" = "xyes" ; then + AC_DEFINE_UNQUOTED([NSS], 1, [whether nss plugin is enabled]) + fi + + AM_CONDITIONAL(WITH_NSS, [test "x$with_nss_plugin" = "xyes"]) + fi + +]) + +AC_DEFUN([LIBVIRT_RESULT_NSS],[ + LIBVIRT_RESULT([nss], [$with_nss_plugin]) +]) diff --git a/tools/Makefile.am b/tools/Makefile.am index 0be3567..a850adb 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -417,6 +417,45 @@ CLEANFILES += wireshark/src/plugin.c endif WITH_WIRESHARK_DISSECTOR +LIBVIRT_NSS_SYMBOL_FILE = \ + $(srcdir)/nss/libvirt_nss.syms + +LIBVIRT_NSS_SOURCES = \ + nss/libvirt_nss.c \ + nss/libvirt_nss.h + +if WITH_NSS +noinst_LTLIBRARIES += nss/libnss_libvirt_impl.la +nss_libnss_libvirt_impl_la_SOURCES = \ + $(LIBVIRT_NSS_SOURCES) + +nss_libnss_libvirt_impl_la_CFLAGS = \ + $(AM_CFLAGS) \ + $(WARN_CFLAGS) \ + $(PIE_CFLAGS) \ + $(COVERAGE_CFLAGS) + +nss_libnss_libvirt_la_SOURCES = +nss_libnss_libvirt_la_LDFLAGS = \ + $(VERSION_SCRIPT_FLAGS)$(LIBVIRT_NSS_SYMBOL_FILE) \ + $(AM_LDFLAGS) \ + -module \ + -export-dynamic \ + -avoid-version \ + -shared \ + -shrext .so.2 + +nss_libnss_libvirt_la_LIBADD = \ + nss/libnss_libvirt_impl.la \ + ../gnulib/lib/libgnu.la + +lib_LTLIBRARIES = \ + nss/libnss_libvirt.la + +endif WITH_NSS + +EXTRA_DIST += $(LIBVIRT_NSS_SYMBOL_FILE) \ + $(LIBVIRT_NSS_SOURCES) clean-local: -rm -rf wireshark/src/libvirt diff --git a/tools/nss/libvirt_nss.c b/tools/nss/libvirt_nss.c new file mode 100644 index 0000000..461d8ca --- /dev/null +++ b/tools/nss/libvirt_nss.c @@ -0,0 +1,36 @@ +/* + * libvirt_nss: Name Service Switch plugin + * + * The aim is to enable users and applications to translate + * domain names into IP addresses. However, this is currently + * available only for those domains which gets their IP addresses + * from a libvirt managed network. + * + * Copyright (C) 2016 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + * + * Authors: + * Michal Privoznik <mprivozn@redhat.com> + */ +#include <config.h> + +#include "libvirt_nss.h" + +int +blah(int c) +{ + return c; +} diff --git a/tools/nss/libvirt_nss.h b/tools/nss/libvirt_nss.h new file mode 100644 index 0000000..b54e5e3 --- /dev/null +++ b/tools/nss/libvirt_nss.h @@ -0,0 +1,36 @@ +/* + * libvirt_nss: Name Service Switch plugin + * + * The aim is to enable users and applications to translate + * domain names into IP addresses. However, this is currently + * available only for those domains which gets their IP addresses + * from a libvirt managed network. + * + * Copyright (C) 2016 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + * + * Authors: + * Michal Privoznik <mprivozn@redhat.com> + */ + +#ifndef __LIBVIRT_NSS_H__ +# define __LIBVIRT_NSS_H__ + +# include <nss.h> +# include <netdb.h> + +int blah(int c); +#endif /* __LIBVIRT_NSS_H__ */ diff --git a/tools/nss/libvirt_nss.syms b/tools/nss/libvirt_nss.syms new file mode 100644 index 0000000..3246213 --- /dev/null +++ b/tools/nss/libvirt_nss.syms @@ -0,0 +1,9 @@ +# +# Officially exported symbols. +# + +{ +global: + blah; +local: *; +}; -- 2.4.10

On Thu, Feb 18, 2016 at 03:21:03PM +0100, Michal Privoznik wrote:
Name Service Switch is a glibc feature responsible for many things. Translating domain names into IP addresses and vice versa is just one of them. However, currently it's the only functionality that this commit is tickling. Well, in this commit the plugin skeleton is introduced. Implementation to come in next patches. Because of the future testing, where the implementation is to be linked with a test, this needs to go into static library. Linking a program with an .so statically is not portable. Therefore a dummy libnss_libvirt_impl library is being introduced too.
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- configure.ac | 2 ++ m4/virt-nss.m4 | 51 ++++++++++++++++++++++++++++++++++++++++++++++ tools/Makefile.am | 39 +++++++++++++++++++++++++++++++++++ tools/nss/libvirt_nss.c | 36 ++++++++++++++++++++++++++++++++ tools/nss/libvirt_nss.h | 36 ++++++++++++++++++++++++++++++++ tools/nss/libvirt_nss.syms | 9 ++++++++ 6 files changed, 173 insertions(+) create mode 100644 m4/virt-nss.m4 create mode 100644 tools/nss/libvirt_nss.c create mode 100644 tools/nss/libvirt_nss.h create mode 100644 tools/nss/libvirt_nss.syms
ACK Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

Lets put the NSS module into its own package. Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- libvirt.spec.in | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/libvirt.spec.in b/libvirt.spec.in index fc2e2cf..3490142 100644 --- a/libvirt.spec.in +++ b/libvirt.spec.in @@ -155,6 +155,7 @@ # Non-server/HV driver defaults which are always enabled %define with_sasl 0%{!?_without_sasl:1} %define with_audit 0%{!?_without_audit:1} +%define with_nss_plugin 0%{!?_without_nss_plugin:1} # Finally set the OS / architecture specific special cases @@ -1218,6 +1219,16 @@ Includes the Sanlock lock manager plugin for the QEMU driver %endif +%if %{with_nss_plugin} +%package nss +Summary: Libvirt plugin for Name Service Switch +Group: Development/Libraries +Requires: %{name}-client = %{version}-%{release} + +%description nss +Libvirt plugin for NSS for translating domain names into IP addresses. +%endif + %prep %setup -q @@ -1451,6 +1462,10 @@ rm -rf .git %define _without_pm_utils --without-pm-utils %endif +%if ! %{with_nss_plugin} + %define _without_nss_plugin --without-nss-plugin +%endif + %define when %(date +"%%F-%%T") %define where %(hostname) %define who %{?packager}%{!?packager:Unknown} @@ -1528,6 +1543,7 @@ rm -f po/stamp-po %{?_without_wireshark} \ %{?_without_systemd_daemon} \ %{?_without_pm_utils} \ + %{?_without_nss_plugin} \ %{with_packager} \ %{with_packager_version} \ --with-qemu-user=%{qemu_user} \ @@ -2324,6 +2340,11 @@ exit 0 %{_libdir}/wireshark/plugins/libvirt.so %endif +%if %{with_nss_plugin} +%files nss +%{_libdir}/libnss_libvirt.so.2 +%endif + %if %{with_lxc} %files login-shell %attr(4750, root, virtlogin) %{_bindir}/virt-login-shell -- 2.4.10

On Thu, Feb 18, 2016 at 03:21:04PM +0100, Michal Privoznik wrote:
Lets put the NSS module into its own package.
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- libvirt.spec.in | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+)
ACK Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

The implementation is pretty straightforward. Moreover, because of the nature of things, gethostbyname_r and gethostbyname2_r can be implemented at the same time too. Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- tools/Makefile.am | 7 +- tools/nss/libvirt_nss.c | 354 ++++++++++++++++++++++++++++++++++++++++++++- tools/nss/libvirt_nss.h | 14 +- tools/nss/libvirt_nss.syms | 4 +- 4 files changed, 373 insertions(+), 6 deletions(-) diff --git a/tools/Makefile.am b/tools/Makefile.am index a850adb..e938e80 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -433,7 +433,12 @@ nss_libnss_libvirt_impl_la_CFLAGS = \ $(AM_CFLAGS) \ $(WARN_CFLAGS) \ $(PIE_CFLAGS) \ - $(COVERAGE_CFLAGS) + $(COVERAGE_CFLAGS) \ + $(LIBXML_CFLAGS) + +nss_libnss_libvirt_impl_la_LIBADD = \ + $(LIBXML_LIBS) \ + ../src/libvirt.la nss_libnss_libvirt_la_SOURCES = nss_libnss_libvirt_la_LDFLAGS = \ diff --git a/tools/nss/libvirt_nss.c b/tools/nss/libvirt_nss.c index 461d8ca..8a3dee3 100644 --- a/tools/nss/libvirt_nss.c +++ b/tools/nss/libvirt_nss.c @@ -29,8 +29,356 @@ #include "libvirt_nss.h" -int -blah(int c) +#include <resolv.h> +#include <sys/types.h> +#include <dirent.h> +#include <time.h> +#include <arpa/inet.h> + +#include "virlease.h" +#include "viralloc.h" +#include "virfile.h" +#include "virerror.h" +#include "virstring.h" +#include "virsocketaddr.h" +#include "configmake.h" + +#if 1 +# define ERROR(...) \ +do { \ + char ebuf[1024]; \ + fprintf(stderr, "ERROR %s:%d : ", __FUNCTION__, __LINE__); \ + fprintf(stderr, __VA_ARGS__); \ + fprintf(stderr, " : %s\n", virStrerror(errno, ebuf, sizeof(ebuf))); \ + fprintf(stderr, "\n"); \ +} while (0) + +# define DEBUG(...) \ +do { \ + fprintf(stderr, "DEBUG %s:%d : ", __FUNCTION__, __LINE__); \ + fprintf(stderr, __VA_ARGS__); \ + fprintf(stderr, "\n"); \ +} while (0) +#else +# define ERROR(...) do { } while (0) +# define DEBUG(...) do { } while (0) +#endif + +#define LEASEDIR LOCALSTATEDIR "/lib/libvirt/dnsmasq/" + +#define ALIGN4(l) (((l) + 3) & ~3) +#define ALIGN8(l) (((l) + 7) & ~7) + +#if __SIZEOF_POINTER__ == 8 +# define ALIGN(l) ALIGN8(l) +#elif __SIZEOF_POINTER__ == 4 +# define ALIGN(l) ALIGN4(l) +#else +# error "Wut? Pointers are neither 4 nor 8 bytes long?" +#endif + +#define FAMILY_ADDRESS_SIZE(family) ((family) == AF_INET6 ? 16 : 4) + +typedef struct { + unsigned char addr[16]; + int af; +} leaseAddress; + +/** + * findLease: + * @name: domain name to lookup + * @af: address family + * @address: all the addresses found for selected @af + * @naddress: number of elements in @address array + * @found: whether @name has been found + * @errnop: errno pointer + * + * Lookup @name in libvirt's IP database, parse it and store all + * addresses found in @address array. Callers can choose which + * address family (@af) should be returned. Currently only + * AF_INET (IPv4) and AF_INET6 (IPv6) are supported. As a corner + * case, AF_UNSPEC may be passed to @af in which case no address + * filtering is done and addresses from both families are + * returned. + * + * Returns -1 on error + * 0 on success + */ +static int +findLease(const char *name, + int af, + leaseAddress **address, + size_t *naddress, + bool *found, + int *errnop) { - return c; + DIR *dir = NULL; + int ret = -1; + const char *leaseDir = LEASEDIR; + struct dirent *entry; + virJSONValuePtr leases_array = NULL; + char *server_duid = NULL; + ssize_t i, nleases; + leaseAddress *tmpAddress = NULL; + size_t ntmpAddress = 0; + + *address = NULL; + *naddress = 0; + *found = false; + + if (af != AF_UNSPEC && af != AF_INET && af != AF_INET6) { + errno = EAFNOSUPPORT; + goto cleanup; + } + + + if (!(dir = opendir(leaseDir))) { + ERROR("Failed to open dir '%s'", leaseDir); + goto cleanup; + } + + if (!(leases_array = virJSONValueNewArray())) { + ERROR("Failed to create json array"); + goto cleanup; + } + + DEBUG("Dir: %s", leaseDir); + while ((ret = virDirRead(dir, &entry, leaseDir)) > 0) { + char *path; + + if (entry->d_name[0] == '.') + continue; + + if (!virFileHasSuffix(entry->d_name, ".status")) + continue; + + if (!(path = virFileBuildPath(leaseDir, entry->d_name, NULL))) + goto cleanup; + + DEBUG("Processing %s", path); + + if (virLeaseReadCustomLeaseFile(leases_array, path, NULL, &server_duid) < 0) { + ERROR("Unable to parse %s", path); + VIR_FREE(path); + goto cleanup; + } + + VIR_FREE(path); + } + + closedir(dir); + dir = NULL; + + nleases = virJSONValueArraySize(leases_array); + DEBUG("Read %zd leases", nleases); + + for (i = 0; i < nleases; i++) { + virJSONValue const *lease; + const char *lease_name; + long long expirytime; + virSocketAddr sa; + const char *ipAddr; + int family; + + lease = virJSONValueArrayGet(leases_array, i); + + if (!lease) { + /* This should never happen (TM) */ + ERROR("Unable to get element %zd of %zd", i, nleases); + goto cleanup; + } + + lease_name = virJSONValueObjectGetString(lease, "hostname"); + + if (STRNEQ_NULLABLE(name, lease_name)) + continue; + + DEBUG("Found record for %s", lease_name); + *found = true; + + if (virJSONValueObjectGetNumberLong(lease, "expiry-time", &expirytime) < 0) { + ERROR("Unable to read expiry-time on %s", lease_name); + goto cleanup; + } + + /* Check whether lease has expired or not */ + if (expirytime < (long long) time(NULL)) { + DEBUG("Lease for %s expired", lease_name); + continue; + } + + if (!(ipAddr = virJSONValueObjectGetString(lease, "ip-address"))) { + ERROR("ip-address field missing for %s", name); + goto cleanup; + } + + DEBUG("IP address: %s", ipAddr); + + if (virSocketAddrParse(&sa, ipAddr, AF_UNSPEC) < 0) { + ERROR("Unable to parse %s", ipAddr); + goto cleanup; + } + + family = VIR_SOCKET_ADDR_FAMILY(&sa); + if (af != AF_UNSPEC && af != family) { + DEBUG("Skipping address which family is %d, %d requested", family, af); + continue; + } + + if (VIR_REALLOC_N_QUIET(tmpAddress, ntmpAddress + 1) < 0) { + ERROR("Out of memory"); + goto cleanup; + } + + tmpAddress[ntmpAddress].af = family; + memcpy(tmpAddress[ntmpAddress].addr, + (family == AF_INET ? + (void *) &sa.data.inet4.sin_addr.s_addr : + (void *) &sa.data.inet6.sin6_addr.s6_addr), + FAMILY_ADDRESS_SIZE(family)); + ntmpAddress++; + } + + *address = tmpAddress; + *naddress = ntmpAddress; + tmpAddress = NULL; + ntmpAddress = 0; + + ret = 0; + + cleanup: + *errnop = errno; + VIR_FREE(tmpAddress); + VIR_FREE(server_duid); + virJSONValueFree(leases_array); + if (dir) + closedir(dir); + return ret; +} + + +enum nss_status +_nss_libvirt_gethostbyname_r(const char *name, struct hostent *result, + char *buffer, size_t buflen, int *errnop, + int *herrnop) +{ + int af = ((_res.options & RES_USE_INET6) ? AF_INET6 : AF_INET); + + return _nss_libvirt_gethostbyname3_r(name, af, result, buffer, buflen, + errnop, herrnop, NULL, NULL); +} + +enum nss_status +_nss_libvirt_gethostbyname2_r(const char *name, int af, struct hostent *result, + char *buffer, size_t buflen, int *errnop, + int *herrnop) +{ + return _nss_libvirt_gethostbyname3_r(name, af, result, buffer, buflen, + errnop, herrnop, NULL, NULL); +} + +enum nss_status +_nss_libvirt_gethostbyname3_r(const char *name, int af, struct hostent *result, + char *buffer, size_t buflen, int *errnop, + int *herrnop, int32_t *ttlp, char **canonp) +{ + enum nss_status ret = NSS_STATUS_UNAVAIL; + char *r_name, *r_aliases, *r_addr, *r_addr_list; + leaseAddress *addr = NULL; + size_t naddr, i; + bool found = false; + size_t nameLen, need, idx; + int alen; + int r; + + if ((r = findLease(name, af, &addr, &naddr, &found, errnop)) < 0) { + /* Error occurred. Return immediately. */ + if (*errnop == EAGAIN) { + *herrnop = TRY_AGAIN; + return NSS_STATUS_TRYAGAIN; + } else { + *herrnop = NO_RECOVERY; + return NSS_STATUS_UNAVAIL; + } + } + + if (!found) { + /* NOT found */ + *errnop = ESRCH; + *herrnop = HOST_NOT_FOUND; + return NSS_STATUS_NOTFOUND; + } else if (!naddr) { + /* Found, but no data */ + *errnop = ENXIO; + *herrnop = NO_DATA; + return NSS_STATUS_UNAVAIL; + } + + /* Found and have data */ + + af = addr[0].af; + alen = FAMILY_ADDRESS_SIZE(af); + + nameLen = strlen(name); + /* We need space for: + * a) name + * b) alias + * c) addresses + * d) NULL stem */ + need = ALIGN(nameLen + 1) + naddr * ALIGN(alen) + (naddr + 2) * sizeof(char*); + + if (buflen < need) { + *errnop = ENOMEM; + *herrnop = TRY_AGAIN; + ret = NSS_STATUS_TRYAGAIN; + goto cleanup; + } + + /* First, append name */ + r_name = buffer; + memcpy(r_name, name, nameLen + 1); + idx = ALIGN(nameLen + 1); + + /* Second, create aliases array */ + r_aliases = buffer + idx; + ((char**) r_aliases)[0] = NULL; + idx += sizeof(char*); + + /* Third, append address */ + r_addr = buffer + idx; + for (i = 0; i < naddr; i++) + memcpy(r_addr + i * ALIGN(alen), addr[i].addr, alen); + idx += naddr * ALIGN(alen); + + /* Third, append address pointer array */ + r_addr_list = buffer + idx; + for (i = 0; i < naddr; i++) + ((char**) r_addr_list)[i] = r_addr + i * ALIGN(alen); + ((char**) r_addr_list)[i] = NULL; + idx += (naddr + 1) * sizeof(char*); + + /* At this point, idx == need */ + DEBUG("Done idx:%zd need:%zd", idx, need); + + result->h_name = r_name; + result->h_aliases = (char**) r_aliases; + result->h_addrtype = af; + result->h_length = alen; + result->h_addr_list = (char**) r_addr_list; + + if (ttlp) + *ttlp = 0; + + if (canonp) + *canonp = r_name; + + /* Explicitly reset all error variables */ + *errnop = 0; + *herrnop = NETDB_SUCCESS; + h_errno = 0; + + ret = NSS_STATUS_SUCCESS; + cleanup: + VIR_FREE(addr); + return ret; } diff --git a/tools/nss/libvirt_nss.h b/tools/nss/libvirt_nss.h index b54e5e3..dd037f5 100644 --- a/tools/nss/libvirt_nss.h +++ b/tools/nss/libvirt_nss.h @@ -32,5 +32,17 @@ # include <nss.h> # include <netdb.h> -int blah(int c); +enum nss_status +_nss_libvirt_gethostbyname_r(const char *name, struct hostent *result, + char *buffer, size_t buflen, int *errnop, + int *herrnop); + +enum nss_status +_nss_libvirt_gethostbyname2_r(const char *name, int af, struct hostent *result, + char *buffer, size_t buflen, int *errnop, + int *herrnop); +enum nss_status +_nss_libvirt_gethostbyname3_r(const char *name, int af, struct hostent *result, + char *buffer, size_t buflen, int *errnop, + int *herrnop, int32_t *ttlp, char **canonp); #endif /* __LIBVIRT_NSS_H__ */ diff --git a/tools/nss/libvirt_nss.syms b/tools/nss/libvirt_nss.syms index 3246213..b88b8be 100644 --- a/tools/nss/libvirt_nss.syms +++ b/tools/nss/libvirt_nss.syms @@ -4,6 +4,8 @@ { global: - blah; + _nss_libvirt_gethostbyname_r; + _nss_libvirt_gethostbyname2_r; + _nss_libvirt_gethostbyname3_r; local: *; }; -- 2.4.10

On Thu, Feb 18, 2016 at 03:21:05PM +0100, Michal Privoznik wrote:
The implementation is pretty straightforward. Moreover, because of the nature of things, gethostbyname_r and gethostbyname2_r can be implemented at the same time too.
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- tools/Makefile.am | 7 +- tools/nss/libvirt_nss.c | 354 ++++++++++++++++++++++++++++++++++++++++++++- tools/nss/libvirt_nss.h | 14 +- tools/nss/libvirt_nss.syms | 4 +- 4 files changed, 373 insertions(+), 6 deletions(-)
diff --git a/tools/Makefile.am b/tools/Makefile.am index a850adb..e938e80 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -433,7 +433,12 @@ nss_libnss_libvirt_impl_la_CFLAGS = \ $(AM_CFLAGS) \ $(WARN_CFLAGS) \ $(PIE_CFLAGS) \ - $(COVERAGE_CFLAGS) + $(COVERAGE_CFLAGS) \ + $(LIBXML_CFLAGS) + +nss_libnss_libvirt_impl_la_LIBADD = \ + $(LIBXML_LIBS) \ + ../src/libvirt.la
I'm thinking that linking to libvirt.la is probably not the best idea. We link libvirt.so with '-z nodelete', so once it is resident in a process it can never be unmapped. In addition libvirt.so will whine if you try to use it from a setuid program. Since the nss modules can be loaded into any program on the system, I think we want to take a similar approach to that done for virt-login-shell, and explicitly link to the smallest possible set of files we can, and not link to any 3rd party shared libraries, with exception of yajl which you need for the json parsing. Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

This function is a different beast compared to previous ones. But yet again, nothing surprising is happening here. Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- tools/nss/libvirt_nss.c | 92 ++++++++++++++++++++++++++++++++++++++++++++++ tools/nss/libvirt_nss.h | 4 ++ tools/nss/libvirt_nss.syms | 1 + 3 files changed, 97 insertions(+) diff --git a/tools/nss/libvirt_nss.c b/tools/nss/libvirt_nss.c index 8a3dee3..b36f087 100644 --- a/tools/nss/libvirt_nss.c +++ b/tools/nss/libvirt_nss.c @@ -382,3 +382,95 @@ _nss_libvirt_gethostbyname3_r(const char *name, int af, struct hostent *result, VIR_FREE(addr); return ret; } + +enum nss_status +_nss_libvirt_gethostbyname4_r(const char *name, struct gaih_addrtuple **pat, + char *buffer, size_t buflen, int *errnop, + int *herrnop, int32_t *ttlp) +{ + enum nss_status ret = NSS_STATUS_UNAVAIL; + leaseAddress *addr = NULL; + size_t naddr, i; + bool found = false; + int r; + size_t nameLen, need, idx; + struct gaih_addrtuple *r_tuple, *r_tuple_first = NULL; + char *r_name; + + if ((r = findLease(name, AF_UNSPEC, &addr, &naddr, &found, errnop)) < 0) { + /* Error occurred. Return immediately. */ + if (*errnop == EAGAIN) { + *herrnop = TRY_AGAIN; + return NSS_STATUS_TRYAGAIN; + } else { + *herrnop = NO_RECOVERY; + return NSS_STATUS_UNAVAIL; + } + } + + if (!found) { + /* NOT found */ + *errnop = ESRCH; + *herrnop = HOST_NOT_FOUND; + return NSS_STATUS_NOTFOUND; + } else if (!naddr) { + /* Found, but no data */ + *errnop = ENXIO; + *herrnop = NO_DATA; + return NSS_STATUS_UNAVAIL; + } + + /* Found and have data */ + + nameLen = strlen(name); + /* We need space for: + * a) name + * b) address */ + need = ALIGN(nameLen + 1) + ALIGN(sizeof(struct gaih_addrtuple)); + + if (buflen < need) { + *errnop = ENOMEM; + *herrnop = TRY_AGAIN; + ret = NSS_STATUS_TRYAGAIN; + goto cleanup; + } + + /* First, append name */ + r_name = buffer; + memcpy(r_name, name, nameLen + 1); + idx = ALIGN(nameLen + 1); + + + /* Second, append addresses */ + r_tuple_first = (struct gaih_addrtuple*) (buffer + idx); + for (i = 0; i < naddr; i++) { + int family = addr[i].af; + + r_tuple = (struct gaih_addrtuple*) (buffer + idx); + r_tuple->next = i == naddr - 1 ? NULL : (struct gaih_addrtuple*) ((char*) r_tuple + ALIGN(sizeof(struct gaih_addrtuple))); + r_tuple->name = r_name; + r_tuple->family = family; + r_tuple->scopeid = 0; + memcpy(r_tuple->addr, addr[i].addr, FAMILY_ADDRESS_SIZE(family)); + + idx += ALIGN(sizeof(struct gaih_addrtuple)); + } + + /* At this point, idx == need */ + DEBUG("Done idx:%zd need:%zd", idx, need); + + if (*pat) + **pat = *r_tuple_first; + else + *pat = r_tuple_first; + + if (ttlp) + *ttlp = 0; + + /* Explicitly reset all error variables */ + *errnop = 0; + *herrnop = NETDB_SUCCESS; + ret = NSS_STATUS_SUCCESS; + cleanup: + return ret; +} diff --git a/tools/nss/libvirt_nss.h b/tools/nss/libvirt_nss.h index dd037f5..589c1e6 100644 --- a/tools/nss/libvirt_nss.h +++ b/tools/nss/libvirt_nss.h @@ -45,4 +45,8 @@ enum nss_status _nss_libvirt_gethostbyname3_r(const char *name, int af, struct hostent *result, char *buffer, size_t buflen, int *errnop, int *herrnop, int32_t *ttlp, char **canonp); +enum nss_status +_nss_libvirt_gethostbyname4_r(const char *name, struct gaih_addrtuple **pat, + char *buffer, size_t buflen, int *errnop, + int *herrnop, int32_t *ttlp); #endif /* __LIBVIRT_NSS_H__ */ diff --git a/tools/nss/libvirt_nss.syms b/tools/nss/libvirt_nss.syms index b88b8be..5a793e4 100644 --- a/tools/nss/libvirt_nss.syms +++ b/tools/nss/libvirt_nss.syms @@ -7,5 +7,6 @@ global: _nss_libvirt_gethostbyname_r; _nss_libvirt_gethostbyname2_r; _nss_libvirt_gethostbyname3_r; + _nss_libvirt_gethostbyname4_r; local: *; }; -- 2.4.10

A small test to see how is the nss module working. Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- cfg.mk | 2 +- tests/Makefile.am | 18 +++++ tests/nssdata/virbr0.status | 14 ++++ tests/nssmock.c | 140 +++++++++++++++++++++++++++++++++ tests/nsstest.c | 184 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 357 insertions(+), 1 deletion(-) create mode 100644 tests/nssdata/virbr0.status create mode 100644 tests/nssmock.c create mode 100644 tests/nsstest.c diff --git a/cfg.mk b/cfg.mk index 5b864af..6f28eef 100644 --- a/cfg.mk +++ b/cfg.mk @@ -1139,7 +1139,7 @@ exclude_file_name_regexp--sc_copyright_usage = \ ^COPYING(|\.LESSER)$$ exclude_file_name_regexp--sc_flags_usage = \ - ^(docs/|src/util/virnetdevtap\.c$$|tests/vir(cgroup|pci|usb)mock\.c$$) + ^(docs/|src/util/virnetdevtap\.c$$|tests/(vir(cgroup|pci|usb)|nss)mock\.c$$) exclude_file_name_regexp--sc_libvirt_unmarked_diagnostics = \ ^(src/rpc/gendispatch\.pl$$|tests/) diff --git a/tests/Makefile.am b/tests/Makefile.am index 90981dc..99ef99f 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -108,6 +108,7 @@ EXTRA_DIST = \ nodedevschemadata \ nodedevschematest \ nodeinfodata \ + nssdata \ nwfilterschematest \ nwfilterxml2firewalldata \ nwfilterxml2xmlin \ @@ -190,6 +191,7 @@ test_programs = virshtest sockettest \ vircaps2xmltest \ virnetdevtest \ virtypedparamtest \ + nsstest \ $(NULL) if WITH_REMOTE @@ -421,6 +423,7 @@ test_libraries = libshunload.la \ virpcimock.la \ virnetdevmock.la \ nodeinfomock.la \ + nssmock.la \ $(NULL) if WITH_QEMU test_libraries += libqemumonitortestutils.la \ @@ -1067,6 +1070,21 @@ nodeinfomock_la_CFLAGS = $(AM_CFLAGS) nodeinfomock_la_LDFLAGS = $(MOCKLIBS_LDFLAGS) nodeinfomock_la_LIBADD = $(MOCKLIBS_LIBS) +nsstest_SOURCES = \ + nsstest.c testutils.h testutils.c +nsstest_CFLAGS = \ + $(AM_CFLAGS) \ + -I$(top_srcdir)/tools/nss +nsstest_LDADD = \ + $(LDADDS) \ + ../tools/nss/libnss_libvirt_impl.la + +nssmock_la_SOURCES = \ + nssmock.c +nssmock_la_CFLAGS = $(AM_CFLAGS) +nssmock_la_LDFLAGS = $(MOCKLIBS_LDFLAGS) +nssmock_la_LIBADD = $(MOCKLIBS_LIBS) + virnetdevtest_SOURCES = \ virnetdevtest.c testutils.h testutils.c virnetdevtest_CFLAGS = $(AM_CFLAGS) $(LIBNL_CFLAGS) diff --git a/tests/nssdata/virbr0.status b/tests/nssdata/virbr0.status new file mode 100644 index 0000000..de5aceb --- /dev/null +++ b/tests/nssdata/virbr0.status @@ -0,0 +1,14 @@ +[ + { + "ip-address": "192.168.122.197", + "mac-address": "52:54:00:a4:6f:91", + "hostname": "fedora", + "expiry-time": 1900000000 + }, + { + "ip-address": "192.168.122.254", + "mac-address": "52:54:00:3a:b5:0c", + "hostname": "gentoo", + "expiry-time": 2000000000 + } +] diff --git a/tests/nssmock.c b/tests/nssmock.c new file mode 100644 index 0000000..b4a4260 --- /dev/null +++ b/tests/nssmock.c @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2016 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + * + * Author: Michal Privoznik <mprivozn@redhat.com> + */ + +#include <config.h> + +#ifdef __linux__ +# include <stdio.h> +# include <stdlib.h> +# include <dlfcn.h> +# include <sys/types.h> +# include <dirent.h> +# include <sys/stat.h> +# include <fcntl.h> + +# include "configmake.h" +# include "internal.h" +# include "virstring.h" +# include "viralloc.h" + +static int (*realopen)(const char *path, int flags, ...); +static DIR * (*realopendir)(const char *name); + +# define LEASEDIR LOCALSTATEDIR "/lib/libvirt/dnsmasq/" + +# define STDERR(...) \ + fprintf(stderr, "%s %zu: ", __FUNCTION__, (size_t) __LINE__); \ + fprintf(stderr, __VA_ARGS__); \ + fprintf(stderr, "\n"); \ + +# define ABORT(...) \ + do { \ + STDERR(__VA_ARGS__); \ + abort(); \ + } while (0) + +# define ABORT_OOM() \ + ABORT("Out of memory") + +/* + * Functions to load the symbols and init the environment + */ +static void +init_syms(void) +{ + if (realopen) + return; + +# define LOAD_SYM(name) \ + do { \ + if (!(real ## name = dlsym(RTLD_NEXT, #name))) \ + ABORT("Cannot find real '%s' symbol\n", #name); \ + } while (0) + + LOAD_SYM(open); + LOAD_SYM(opendir); +} + +static int +getrealpath(char **newpath, + const char *path) +{ + if (STRPREFIX(path, LEASEDIR)) { + if (virAsprintfQuiet(newpath, "%s/nssdata/%s", + abs_srcdir, + path + strlen(LEASEDIR)) < 0) { + errno = ENOMEM; + return -1; + } + } else { + if (VIR_STRDUP_QUIET(*newpath, path) < 0) + return -1; + } + + return 0; +} + +int +open(const char *path, int flags, ...) +{ + int ret; + char *newpath = NULL; + + init_syms(); + + if (STRPREFIX(path, LEASEDIR) && + getrealpath(&newpath, path) < 0) + return -1; + + if (flags & O_CREAT) { + va_list ap; + mode_t mode; + va_start(ap, flags); + mode = va_arg(ap, mode_t); + va_end(ap); + ret = realopen(newpath ? newpath : path, flags, mode); + } else { + ret = realopen(newpath ? newpath : path, flags); + } + + VIR_FREE(newpath); + return ret; +} + +DIR * +opendir(const char *path) +{ + DIR *ret; + char *newpath = NULL; + + init_syms(); + + if (STRPREFIX(path, LEASEDIR) && + getrealpath(&newpath, path) < 0) + return NULL; + + ret = realopendir(newpath ? newpath : path); + + VIR_FREE(newpath); + return ret; +} +#else +/* Nothing to override on non-__linux__ platforms */ +#endif diff --git a/tests/nsstest.c b/tests/nsstest.c new file mode 100644 index 0000000..932f9a8 --- /dev/null +++ b/tests/nsstest.c @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2016 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + * + * Author: Michal Privoznik <mprivozn@redhat.com> + */ + +#include <config.h> + +#include "testutils.h" + +#ifdef __linux__ + +# include <stdbool.h> +# include <arpa/inet.h> +# include "libvirt_nss.h" + +# define VIR_FROM_THIS VIR_FROM_NONE + +# define BUF_SIZE 1024 + +struct testNSSData { + const char *hostname; + const char *ipAddr; +}; + +static int +testGetHostByName(const void *opaque) +{ + const struct testNSSData *data = opaque; + const bool existent = data->hostname && data->ipAddr; + int ret = -1; + struct hostent resolved; + char buf[BUF_SIZE] = { 0 }; + char **addrList; + int rv, tmp_errno = 0, tmp_herrno = 0; + size_t i; + + if (!data) + goto cleanup; + + memset(&resolved, 0, sizeof(resolved)); + + rv = _nss_libvirt_gethostbyname_r(data->hostname, + &resolved, + buf, sizeof(buf), + &tmp_errno, + &tmp_herrno); + + if (rv == NSS_STATUS_TRYAGAIN || + rv == NSS_STATUS_UNAVAIL || + rv == NSS_STATUS_RETURN) { + /* Resolving failed in unexpected fashion. */ + virReportError(VIR_ERR_INTERNAL_ERROR, + "Resolving of %s failed due to internal error", + data->hostname); + goto cleanup; + } else if (rv == NSS_STATUS_NOTFOUND) { + /* Resolving failed. Should it? */ + if (!existent) + ret = 0; + else + virReportError(VIR_ERR_INTERNAL_ERROR, + "Resolving of %s failed", + data->hostname); + goto cleanup; + } + + /* Resolving succeeded. Should it? */ + if (!existent) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "Resolving of %s succeeded but was expected to fail", + data->hostname); + goto cleanup; + } + + /* Now lets see if resolved address match our expectations. */ + + if (!resolved.h_name) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + "resolved.h_name empty"); + goto cleanup; + } + + if (resolved.h_addrtype != AF_INET) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "Expected AF_INET (%d) got %d", + AF_INET, resolved.h_addrtype); + goto cleanup; + } + + if (resolved.h_length != 4) { + /* IPv4 addresses are encoded into 4 bytes */ + virReportError(VIR_ERR_INTERNAL_ERROR, + "Expected 4 bytes long address, got %d", + resolved.h_length); + goto cleanup; + } + + if (!resolved.h_addr_list) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + "resolved.h_addr_list empty"); + goto cleanup; + } + + addrList = resolved.h_addr_list; + i = 0; + while (*addrList) { + virSocketAddr sa; + char *ipAddr; + + /* For some reason, virSocketAddrSetIPv4Addr does htonl() conversion. + * But the data we already have is in network order. */ + virSocketAddrSetIPv4Addr(&sa, ntohl(*((uint32_t *) *addrList))); + + if (i == 0) { + if (!(ipAddr = virSocketAddrFormat(&sa))) { + /* error reported by helper */ + goto cleanup; + } + if (STRNEQ(data->ipAddr, ipAddr)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "Address mismatch. Expected %s got %s", + data->ipAddr, ipAddr); + VIR_FREE(ipAddr); + goto cleanup; + } + VIR_FREE(ipAddr); + } else { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + "Got more addresses than expected (1)"); + goto cleanup; + } + + addrList++; + } + + ret = 0; + cleanup: + return ret; +} + +static int +mymain(void) +{ + int ret = 0; + +# define DO_TEST(name, address) \ + do { \ + struct testNSSData data = { \ + .hostname = name, .ipAddr = address, \ + }; \ + if (virtTestRun(name, testGetHostByName, &data) < 0) \ + ret = -1; \ + } while (0) + + DO_TEST("fedora", "192.168.122.197"); + DO_TEST("gentoo", "192.168.122.254"); + DO_TEST("non-existent", NULL); + + return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; +} + +VIRT_TEST_MAIN_PRELOAD(mymain, abs_builddir "/.libs/nssmock.so") +#else +int +main(void) +{ + return EXIT_AM_SKIP; +} +#endif -- 2.4.10
participants (2)
-
Daniel P. Berrange
-
Michal Privoznik