[libvirt] [PATCH v4 0/8] NSS module for libvirt

v4 of: https://www.redhat.com/archives/libvir-list/2016-February/msg00693.html diff to v3: - Introduced yet another test to catch linkage problems - Extended nsstest to test multiple addresses - Reworked src/libvirt_nss.la CFLAGS/LDFLAGS - dropped some dependencies - Dropped checking for lease timestamp from nss plugin implementation as it's already done by lease parsing function Michal Privoznik (8): 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 tests: Introduce nsslinktest cfg.mk | 2 +- config-post.h | 24 +++ configure.ac | 2 + libvirt.spec.in | 21 ++ m4/virt-nss.m4 | 51 +++++ po/POTFILES.in | 1 + src/Makefile.am | 60 ++++++ src/libvirt_private.syms | 6 + src/network/leaseshelper.c | 271 +------------------------- src/util/virfile.c | 2 +- src/util/virjson.c | 58 +++--- src/util/virjson.h | 54 +++--- src/util/virlease.c | 304 +++++++++++++++++++++++++++++ src/util/virlease.h | 44 +++++ tests/Makefile.am | 31 ++- tests/nssdata/virbr0.status | 20 ++ tests/nssdata/virbr1.status | 14 ++ tests/nsslinktest.c | 39 ++++ tests/nssmock.c | 140 ++++++++++++++ tests/nsstest.c | 195 +++++++++++++++++++ tools/Makefile.am | 44 +++++ tools/nss/libvirt_nss.c | 463 ++++++++++++++++++++++++++++++++++++++++++++ tools/nss/libvirt_nss.h | 52 +++++ tools/nss/libvirt_nss.syms | 12 ++ 24 files changed, 1580 insertions(+), 330 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/nssdata/virbr1.status create mode 100644 tests/nsslinktest.c 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 d57d303..2ba397f 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 3a1b9e1..a314cb5 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, Mar 03, 2016 at 06:11:39PM +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
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"
Keep this ^^ here, it's used in this file. ACK with that.

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, Mar 03, 2016 at 06:11:40PM +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(-)
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)
You're giving the function a const pointer, but it gives you back non-const pointer to a data in that object. That is because the only const part is the above object, however all the data in it (all pointers) are non-const. The function prototype might create false sense of security (the user will think it does not modify the contents, but it can modify most of it). This is one of the few exemptions in which I would rather pass a non-const pointer just so someone (who does not know how the objects are stored internally) is not fooled into thinking it will not do anything with the data. It might cause a problem when they think the returning value is not connected to the object and they can modify it without changing the object because the function they used took just const. Don't take me wrong, I'm all for const-correctness, but to any rule there has to be an exception, right? I would rather make const only those that are not the exception, like virJSONValueObjectGet{Key,String}(), virJSONValueObjectKeysNumber() and so on, but not every one that works. ACK to those from this list that return non-pointers (like int) or const pointers.

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 | 38 ++++++++++++++++++++++++++++++++++ tools/nss/libvirt_nss.c | 36 ++++++++++++++++++++++++++++++++ tools/nss/libvirt_nss.h | 36 ++++++++++++++++++++++++++++++++ tools/nss/libvirt_nss.syms | 9 ++++++++ 6 files changed, 172 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 eed2050..4d64998 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..9754e42 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -417,6 +417,44 @@ 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 + +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, Mar 03, 2016 at 06:11:41PM +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 | 38 ++++++++++++++++++++++++++++++++++ tools/nss/libvirt_nss.c | 36 ++++++++++++++++++++++++++++++++ tools/nss/libvirt_nss.h | 36 ++++++++++++++++++++++++++++++++ tools/nss/libvirt_nss.syms | 9 ++++++++ 6 files changed, 172 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 eed2050..4d64998 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"])
This ^^ needs to be ...
+ fi +
... here, otherwise the build will fail without nss.
+]) + +AC_DEFUN([LIBVIRT_RESULT_NSS],[ + LIBVIRT_RESULT([nss], [$with_nss_plugin]) +]) diff --git a/tools/Makefile.am b/tools/Makefile.am index 0be3567..9754e42 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -417,6 +417,44 @@ 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
Also move this ^^ ...
+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) +
... here, so that we can run tests even without NSS (which is what you have in place in later tests, they will now work only WITH_NSS) ACK with that changed.

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 03e2438..733b347 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, Mar 03, 2016 at 06:11:42PM +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(+)
diff --git a/libvirt.spec.in b/libvirt.spec.in index 03e2438..733b347 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} +
I would instead require libvirt-daemon-driver-network so it's clean, but not that it would change anything. Wanting network driver without the daemon doesn't make sense and the daemon requires the client. It' sjust that it would be a bit cleaner. ACK either way.

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> --- config-post.h | 24 ++++ src/Makefile.am | 59 ++++++++ src/util/virfile.c | 2 +- tests/Makefile.am | 2 +- tools/Makefile.am | 8 +- tools/nss/libvirt_nss.c | 341 ++++++++++++++++++++++++++++++++++++++++++++- tools/nss/libvirt_nss.h | 14 +- tools/nss/libvirt_nss.syms | 4 +- 8 files changed, 446 insertions(+), 8 deletions(-) diff --git a/config-post.h b/config-post.h index 8367200..2398d3d 100644 --- a/config-post.h +++ b/config-post.h @@ -43,3 +43,27 @@ # undef WITH_YAJL # undef WITH_YAJL2 #endif + +/* + * With the NSS module it's the same story as virt-login-shell. See the + * explanation above. + */ +#ifdef LIBVIRT_NSS +# undef HAVE_LIBDEVMAPPER_H +# undef HAVE_LIBNL +# undef HAVE_LIBNL3 +# undef HAVE_LIBSASL2 +# undef WITH_CAPNG +# undef WITH_CURL +# undef WITH_DTRACE_PROBES +# undef WITH_GNUTLS +# undef WITH_GNUTLS_GCRYPT +# undef WITH_MACVTAP +# undef WITH_NUMACTL +# undef WITH_SASL +# undef WITH_SSH2 +# undef WITH_VIRTUALPORT +# undef WITH_SECDRIVER_SELINUX +# undef WITH_SECDRIVER_APPARMOR +# undef WITH_CAPNG +#endif /* LIBVIRT_NSS */ diff --git a/src/Makefile.am b/src/Makefile.am index 2ba397f..8ea5422 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -2946,6 +2946,65 @@ endif WITH_LIBVIRTD endif WITH_SECDRIVER_APPARMOR EXTRA_DIST += $(SECURITY_DRIVER_APPARMOR_HELPER_SOURCES) +if WITH_NSS +noinst_LTLIBRARIES += libvirt-nss.la + +libvirt_nss_la_SOURCES = \ + util/viralloc.c \ + util/viralloc.h \ + util/virbitmap.c \ + util/virbitmap.h \ + util/virbuffer.c \ + util/virbuffer.h \ + util/vircommand.c \ + util/vircommand.h \ + util/virerror.c \ + util/virerror.h \ + util/virfile.c \ + util/virfile.h \ + util/virjson.c \ + util/virjson.h \ + util/virkmod.c \ + util/virkmod.h \ + util/virlease.c \ + util/virlease.h \ + util/virlog.c \ + util/virlog.h \ + util/virobject.c \ + util/virobject.h \ + util/virpidfile.c \ + util/virpidfile.h \ + util/virprocess.c \ + util/virprocess.h \ + util/virsocketaddr.c \ + util/virsocketaddr.h \ + util/virstring.c \ + util/virstring.h \ + util/virthread.c \ + util/virthread.h \ + util/virthreadjob.c \ + util/virthreadjob.h \ + util/virtime.c \ + util/virtime.h \ + util/virutil.c \ + util/virutil.h \ + $(NULL) + +libvirt_nss_la_CFLAGS = \ + -DLIBVIRT_NSS \ + $(AM_CFLAGS) \ + $(YAJL_CFLAGS) \ + $(NULL) +libvirt_nss_la_LDFLAGS = \ + $(AM_LDFLAGS) \ + $(NULL) + +libvirt_nss_la_LIBADD = \ + $(YAJL_LIBS) \ + $(NULL) +endif WITH_NSS + + install-data-local: install-init install-systemd if WITH_LIBVIRTD $(MKDIR_P) "$(DESTDIR)$(localstatedir)/lib/libvirt/lockd" diff --git a/src/util/virfile.c b/src/util/virfile.c index f45e18f..0fae0f5 100644 --- a/src/util/virfile.c +++ b/src/util/virfile.c @@ -554,7 +554,7 @@ int virFileUpdatePerm(const char *path, #if defined(__linux__) && HAVE_DECL_LO_FLAGS_AUTOCLEAR && \ - !defined(LIBVIRT_SETUID_RPC_CLIENT) + !defined(LIBVIRT_SETUID_RPC_CLIENT) && !defined(LIBVIRT_NSS) # if HAVE_DECL_LOOP_CTL_GET_FREE diff --git a/tests/Makefile.am b/tests/Makefile.am index 90981dc..55e8432 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -65,7 +65,7 @@ GNULIB_LIBS = \ ../gnulib/lib/libgnu.la LDADDS = \ - $(WARN_CFLAGS) \ + $(WARN_CFLAGS) \ $(NO_INDIRECT_LDFLAGS) \ $(PROBES_O) \ $(GNULIB_LIBS) \ diff --git a/tools/Makefile.am b/tools/Makefile.am index 9754e42..8312111 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -433,7 +433,13 @@ 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) \ + ../gnulib/lib/libgnu.la \ + ../src/libvirt-nss.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..192d8e3 100644 --- a/tools/nss/libvirt_nss.c +++ b/tools/nss/libvirt_nss.c @@ -29,8 +29,343 @@ #include "libvirt_nss.h" -int -blah(int c) +#include <resolv.h> +#include <sys/types.h> +#include <dirent.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 0 +# 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); + VIR_FREE(server_duid); + } + + 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; + 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 (!(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); + 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, Mar 03, 2016 at 06:11:43PM +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> --- config-post.h | 24 ++++ src/Makefile.am | 59 ++++++++ src/util/virfile.c | 2 +- tests/Makefile.am | 2 +- tools/Makefile.am | 8 +- tools/nss/libvirt_nss.c | 341 ++++++++++++++++++++++++++++++++++++++++++++- tools/nss/libvirt_nss.h | 14 +- tools/nss/libvirt_nss.syms | 4 +- 8 files changed, 446 insertions(+), 8 deletions(-)
diff --git a/config-post.h b/config-post.h index 8367200..2398d3d 100644 --- a/config-post.h +++ b/config-post.h @@ -43,3 +43,27 @@ # undef WITH_YAJL # undef WITH_YAJL2 #endif + +/* + * With the NSS module it's the same story as virt-login-shell. See the + * explanation above. + */ +#ifdef LIBVIRT_NSS +# undef HAVE_LIBDEVMAPPER_H +# undef HAVE_LIBNL +# undef HAVE_LIBNL3 +# undef HAVE_LIBSASL2 +# undef WITH_CAPNG +# undef WITH_CURL +# undef WITH_DTRACE_PROBES +# undef WITH_GNUTLS +# undef WITH_GNUTLS_GCRYPT +# undef WITH_MACVTAP +# undef WITH_NUMACTL +# undef WITH_SASL +# undef WITH_SSH2 +# undef WITH_VIRTUALPORT +# undef WITH_SECDRIVER_SELINUX +# undef WITH_SECDRIVER_APPARMOR +# undef WITH_CAPNG +#endif /* LIBVIRT_NSS */ diff --git a/src/Makefile.am b/src/Makefile.am index 2ba397f..8ea5422 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -2946,6 +2946,65 @@ endif WITH_LIBVIRTD endif WITH_SECDRIVER_APPARMOR EXTRA_DIST += $(SECURITY_DRIVER_APPARMOR_HELPER_SOURCES)
+if WITH_NSS
Remove this condition, otherwise you won't be able to build tests without nss.
+noinst_LTLIBRARIES += libvirt-nss.la + +libvirt_nss_la_SOURCES = \ + util/viralloc.c \ + util/viralloc.h \ + util/virbitmap.c \ + util/virbitmap.h \ + util/virbuffer.c \ + util/virbuffer.h \ + util/vircommand.c \ + util/vircommand.h \ + util/virerror.c \ + util/virerror.h \ + util/virfile.c \ + util/virfile.h \ + util/virjson.c \ + util/virjson.h \ + util/virkmod.c \ + util/virkmod.h \ + util/virlease.c \ + util/virlease.h \ + util/virlog.c \ + util/virlog.h \ + util/virobject.c \ + util/virobject.h \ + util/virpidfile.c \ + util/virpidfile.h \ + util/virprocess.c \ + util/virprocess.h \ + util/virsocketaddr.c \ + util/virsocketaddr.h \ + util/virstring.c \ + util/virstring.h \ + util/virthread.c \ + util/virthread.h \ + util/virthreadjob.c \ + util/virthreadjob.h \ + util/virtime.c \ + util/virtime.h \ + util/virutil.c \ + util/virutil.h \ + $(NULL) + +libvirt_nss_la_CFLAGS = \ + -DLIBVIRT_NSS \ + $(AM_CFLAGS) \ + $(YAJL_CFLAGS) \ + $(NULL) +libvirt_nss_la_LDFLAGS = \ + $(AM_LDFLAGS) \ + $(NULL) + +libvirt_nss_la_LIBADD = \ + $(YAJL_LIBS) \ + $(NULL) +endif WITH_NSS +
^^ of course, here too :)
+ install-data-local: install-init install-systemd if WITH_LIBVIRTD $(MKDIR_P) "$(DESTDIR)$(localstatedir)/lib/libvirt/lockd" diff --git a/src/util/virfile.c b/src/util/virfile.c index f45e18f..0fae0f5 100644 --- a/src/util/virfile.c +++ b/src/util/virfile.c @@ -554,7 +554,7 @@ int virFileUpdatePerm(const char *path,
#if defined(__linux__) && HAVE_DECL_LO_FLAGS_AUTOCLEAR && \ - !defined(LIBVIRT_SETUID_RPC_CLIENT) + !defined(LIBVIRT_SETUID_RPC_CLIENT) && !defined(LIBVIRT_NSS)
# if HAVE_DECL_LOOP_CTL_GET_FREE
diff --git a/tests/Makefile.am b/tests/Makefile.am index 90981dc..55e8432 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -65,7 +65,7 @@ GNULIB_LIBS = \ ../gnulib/lib/libgnu.la
LDADDS = \ - $(WARN_CFLAGS) \ + $(WARN_CFLAGS) \ $(NO_INDIRECT_LDFLAGS) \ $(PROBES_O) \ $(GNULIB_LIBS) \
If I were to nit-pick I would say this has nothing to do here... But I'm not like that... I won't even mention anything related to that...
diff --git a/tools/Makefile.am b/tools/Makefile.am index 9754e42..8312111 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -433,7 +433,13 @@ nss_libnss_libvirt_impl_la_CFLAGS = \ $(AM_CFLAGS) \ $(WARN_CFLAGS) \ $(PIE_CFLAGS) \ - $(COVERAGE_CFLAGS) + $(COVERAGE_CFLAGS) \ + $(LIBXML_CFLAGS)
Couldn't we use more #undef's in config-post to get rid of this? It's not used anywhere in the module implementation.
+ +nss_libnss_libvirt_impl_la_LIBADD = \ + $(LIBXML_LIBS) \ + ../gnulib/lib/libgnu.la \ + ../src/libvirt-nss.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..192d8e3 100644 --- a/tools/nss/libvirt_nss.c +++ b/tools/nss/libvirt_nss.c @@ -29,8 +29,343 @@
#include "libvirt_nss.h"
-int -blah(int c) +#include <resolv.h> +#include <sys/types.h> +#include <dirent.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 0 +# 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 +
This is kinda gross. You can avoid needing to do alignment but proper casts (as I will point out later on so this compiles on clang as well). But even if you need to go with this, why not do: #define ALIGN(x) (((x) + __SIZEOF_POINTER__ - 1) & ~(__SIZEOF_POINTER__ - 1)) so that you don't have to create the macro for each size and then select between them?
+#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); + VIR_FREE(server_duid);
I would rather change virLeaseReadCustomLeaseFile() so that it treats server_duid == NULL gracefully so that we don't need to do such things here. But no harm done so far ;)
+ } + + 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; + 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 (!(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); + 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;
Most of these point to memory that we don't address by bytes, so they could be changed to void pointers to indicate that. Or to whatever they point to so you can use the pointer arithmetic and not that many casts.
+ 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); +
So here you base every address' size on the family of the first returned one. But if there are IPv6 addresses, this function returns random stuff (tried). I started fixing this and then I realised there were no links in the commits on how these functions should behave, so I can only guess how they are supposed to work in case multiple addresses are returned. I'm guessing the biggest one is used and then it just works because of the ::<ipv4 address here> ipv6 feature or something like that, right? I have some changes for this stored, you might like some of them or not. They are not split by commit, but see them on my public github and feel free to incorporate some of it if you like it. At least some pointers are handled nicely but I haven't rewritten all of the uses. If you say you only support IPv4 for now, I think we can accept that for the time being and deal with IPv6 later. But if you want to write the whole thing, be my guest. Martin

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 192d8e3..35de23b 100644 --- a/tools/nss/libvirt_nss.c +++ b/tools/nss/libvirt_nss.c @@ -369,3 +369,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 | 20 +++++ tests/nssdata/virbr1.status | 14 ++++ tests/nssmock.c | 140 +++++++++++++++++++++++++++++++ tests/nsstest.c | 195 ++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 388 insertions(+), 1 deletion(-) create mode 100644 tests/nssdata/virbr0.status create mode 100644 tests/nssdata/virbr1.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 55e8432..2bb9d28 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..6ebe954 --- /dev/null +++ b/tests/nssdata/virbr0.status @@ -0,0 +1,20 @@ +[ + { + "ip-address": "192.168.122.197", + "mac-address": "52:54:00:a4:6f:91", + "hostname": "fedora", + "expiry-time": 1900000000 + }, + { + "ip-address": "192.168.122.198", + "mac-address": "52:54:00:a4:6f:92", + "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/nssdata/virbr1.status b/tests/nssdata/virbr1.status new file mode 100644 index 0000000..6715383 --- /dev/null +++ b/tests/nssdata/virbr1.status @@ -0,0 +1,14 @@ +[ + { + "ip-address": "192.168.122.199", + "mac-address": "52:54:00:a4:6f:93", + "hostname": "fedora", + "expiry-time": 1900000000 + }, + { + "ip-address": "192.168.122.200", + "mac-address": "52:54:00:a4:6f:94", + "hostname": "fedora", + "expiry-time": 1 + } +] 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..7b899fe --- /dev/null +++ b/tests/nsstest.c @@ -0,0 +1,195 @@ +/* + * 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 *const *ipAddr; +}; + +static int +testGetHostByName(const void *opaque) +{ + const struct testNSSData *data = opaque; + const bool existent = data->hostname && data->ipAddr && data->ipAddr[0]; + 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 (!(ipAddr = virSocketAddrFormat(&sa))) { + /* error reported by helper */ + goto cleanup; + } + + if (!data->ipAddr[i]) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "Unexpected address %s", ipAddr); + VIR_FREE(ipAddr); + goto cleanup; + } + + if (STRNEQ(data->ipAddr[i], ipAddr)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "Address mismatch. Expected %s got %s", + data->ipAddr[i], ipAddr); + VIR_FREE(ipAddr); + goto cleanup; + } + VIR_FREE(ipAddr); + + addrList++; + i++; + } + + if (data->ipAddr[i]) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "Address mismatch. Expected %s got nothing", + data->ipAddr[i]); + goto cleanup; + } + + ret = 0; + cleanup: + return ret; +} + +static int +mymain(void) +{ + int ret = 0; + +# define DO_TEST(name, ...) \ + do { \ + const char *addr[] = { __VA_ARGS__, NULL}; \ + struct testNSSData data = { \ + .hostname = name, .ipAddr = addr, \ + }; \ + if (virtTestRun(name, testGetHostByName, &data) < 0) \ + ret = -1; \ + } while (0) + + DO_TEST("fedora", "192.168.122.197", "192.168.122.198", "192.168.122.199"); + 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

On Thu, Mar 03, 2016 at 06:11:45PM +0100, Michal Privoznik wrote:
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 | 20 +++++ tests/nssdata/virbr1.status | 14 ++++ tests/nssmock.c | 140 +++++++++++++++++++++++++++++++ tests/nsstest.c | 195 ++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 388 insertions(+), 1 deletion(-) create mode 100644 tests/nssdata/virbr0.status create mode 100644 tests/nssdata/virbr1.status create mode 100644 tests/nssmock.c create mode 100644 tests/nsstest.c
+int +open(const char *path, int flags, ...) +{ + int ret; + char *newpath = NULL; + + init_syms(); + + if (STRPREFIX(path, LEASEDIR) &&
No need to prefix it since getrealpath() does that. Call it unconditionally and then...
+ 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);
you don't need to do this ternary stuff here. [...]
+ if (data->ipAddr[i]) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "Address mismatch. Expected %s got nothing", + data->ipAddr[i]); + goto cleanup; + }
We could get more info out of this more easily if those ERROR and DEBUG macros in PATCH 5/8 were defined and used based on environment variable. I know it's a bit slower, but if you build --with-debug or something like that, I think it might be useful. Otherwise this and all patches I did not reply too look fine (apart from all the unnecessary arithmetic, but I already said that, didn't I). Martin

The only purpose of this test is to catch possible linking problems with libnss_libvirt.so.2. One of the problems I faced was that the NSS plugin was unloaded immediately after it got loaded and the name resolution process continued with next configured option. Without any error. It was very hard to debug why until I created this simple test and found out immediately that there were some symbols missing. The reason why problem was not caught in nsstest is that in the test we want to use all the fancy stuff and therefore link it with libvirt.la. So even if there's a symbol missing in the NSS plugin it will be found in the libvirt.la. But even after I resolved the issue we still need this test because files the NSS plugin is built from are still live (mostly those under utils/ dir). So as they change new symbol might be required which would render the NSS plugin unusable. Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- tests/Makefile.am | 11 ++++++++++- tests/nsslinktest.c | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 tests/nsslinktest.c diff --git a/tests/Makefile.am b/tests/Makefile.am index 2bb9d28..6d040b1 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -158,7 +158,7 @@ EXTRA_DIST = \ xml2sexprdata \ xml2vmxdata -test_helpers = commandhelper ssh test_conf +test_helpers = commandhelper ssh test_conf nsslinktest test_programs = virshtest sockettest \ nodeinfotest virbuftest \ commandtest seclabeltest \ @@ -1085,6 +1085,15 @@ nssmock_la_CFLAGS = $(AM_CFLAGS) nssmock_la_LDFLAGS = $(MOCKLIBS_LDFLAGS) nssmock_la_LIBADD = $(MOCKLIBS_LIBS) +## Intentionaly not linking with anything else. +## See the test source for more detailed explanation. +nsslinktest_SOURCES = nsslinktest.c +nsslinktest_CFLAGS = \ + $(AM_CFLAGS) \ + -I$(top_srcdir)/tools/nss +nsslinktest_LDADD = ../tools/nss/libnss_libvirt_impl.la +nsslinktest_LDFLAGS = $(NULL) + virnetdevtest_SOURCES = \ virnetdevtest.c testutils.h testutils.c virnetdevtest_CFLAGS = $(AM_CFLAGS) $(LIBNL_CFLAGS) diff --git a/tests/nsslinktest.c b/tests/nsslinktest.c new file mode 100644 index 0000000..0232e36 --- /dev/null +++ b/tests/nsslinktest.c @@ -0,0 +1,39 @@ +/* + * 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 "internal.h" +#include "libvirt_nss.h" + +int main(int argc ATTRIBUTE_UNUSED, + char *argv[] ATTRIBUTE_UNUSED) +{ + int err, herrno; /* Dummy variables to prevent SIGSEGV */ + + /* The only aim of this test is to catch link errors as those + * are hard to trace for resulting .so library. Therefore, + * the fact this test has been built successfully means + * there's no linkage problem and therefore success is + * returned. */ + _nss_libvirt_gethostbyname_r(NULL, NULL, NULL, 0, &err, &herrno); + + return EXIT_SUCCESS; +} -- 2.4.10
participants (2)
-
Martin Kletzander
-
Michal Privoznik