Re: [PATCH v1] tools/hv: async name resolution in kvp_daemon

From: Vitaly Kuznetsov
Date: Thu Nov 07 2019 - 08:39:20 EST


Olaf Hering <olaf@xxxxxxxxx> writes:

> The hostname is resolved just once since commit 58125210ab3b ("Tools:
> hv: cache FQDN in kvp_daemon to avoid timeouts") to make sure the VM
> responds within the timeout limits to requests from the host.
>
> If for some reason getaddrinfo fails, the string returned by the
> "FullyQualifiedDomainName" request contains some error string, which is
> then used by tools on the host side.
>
> Adjust the code to resolve the current hostname in a separate thread.
> This thread loops until getaddrinfo returns success. During this time
> all "FullyQualifiedDomainName" requests will be answered with an empty
> string.
>
> Signed-off-by: Olaf Hering <olaf@xxxxxxxxx>
> ---
> tools/hv/Makefile | 2 ++
> tools/hv/hv_kvp_daemon.c | 69 ++++++++++++++++++++++++++++++++----------------
> 2 files changed, 48 insertions(+), 23 deletions(-)
>
> diff --git a/tools/hv/Makefile b/tools/hv/Makefile
> index b57143d9459c..3b5481015a84 100644
> --- a/tools/hv/Makefile
> +++ b/tools/hv/Makefile
> @@ -22,6 +22,8 @@ ALL_PROGRAMS := $(patsubst %,$(OUTPUT)%,$(ALL_TARGETS))
>
> ALL_SCRIPTS := hv_get_dhcp_info.sh hv_get_dns_info.sh hv_set_ifconfig.sh
>
> +$(OUTPUT)hv_kvp_daemon: LDFLAGS += -lpthread
> +
> all: $(ALL_PROGRAMS)
>
> export srctree OUTPUT CC LD CFLAGS
> diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c
> index e9ef4ca6a655..22cf1c4dbf5c 100644
> --- a/tools/hv/hv_kvp_daemon.c
> +++ b/tools/hv/hv_kvp_daemon.c
> @@ -41,6 +41,7 @@
> #include <net/if.h>
> #include <limits.h>
> #include <getopt.h>
> +#include <pthread.h>
>
> /*
> * KVP protocol: The user mode component first registers with the
> @@ -85,7 +86,7 @@ static char *processor_arch;
> static char *os_build;
> static char *os_version;
> static char *lic_version = "Unknown version";
> -static char full_domain_name[HV_KVP_EXCHANGE_MAX_VALUE_SIZE];
> +static char *full_domain_name;
> static struct utsname uts_buf;
>
> /*
> @@ -1327,27 +1328,53 @@ static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val)
> return error;
> }
>
> -
> -static void
> -kvp_get_domain_name(char *buffer, int length)
> +/*
> + * Async retrival of Fully Qualified Domain Name because getaddrinfo takes an
> + * unpredictable amount of time to finish.
> + */
> +static void *kvp_getaddrinfo(void *p)
> {
> - struct addrinfo hints, *info ;
> - int error = 0;
> + char *tmp, **str_ptr = (char **)p;
> + char hostname[HOST_NAME_MAX + 1];
> + struct addrinfo *info, hints = {
> + .ai_family = AF_INET, /* Get only ipv4 addrinfo. */
> + .ai_socktype = SOCK_STREAM,
> + .ai_flags = AI_CANONNAME,
> + };
> + int ret;
> +
> + if (gethostname(hostname, sizeof(hostname) - 1) < 0)
> + goto out;
> +
> + do {
> + ret = getaddrinfo(hostname, NULL, &hints, &info);
> + if (ret)
> + sleep(1);

Is it only EAI_AGAIN or do you see any other return values which justify
the retry? I'm afraid that in case of a e.g. non-existing hostname we'll
be infinitely looping with EAI_FAIL.

> + } while (ret);
> +
> + ret = asprintf(&tmp, "%s", info->ai_canonname);
> + freeaddrinfo(info);
> + if (ret <= 0)
> + goto out;
> +
> + if (ret > HV_KVP_EXCHANGE_MAX_VALUE_SIZE)
> + tmp[HV_KVP_EXCHANGE_MAX_VALUE_SIZE - 1] = '\0';
> + *str_ptr = tmp;
>
> - gethostname(buffer, length);
> - memset(&hints, 0, sizeof(hints));
> - hints.ai_family = AF_INET; /*Get only ipv4 addrinfo. */
> - hints.ai_socktype = SOCK_STREAM;
> - hints.ai_flags = AI_CANONNAME;
> +out:
> + pthread_exit(NULL);
> +}
> +
> +static void kvp_obtain_domain_name(char **str_ptr)
> +{
> + pthread_t t;
>
> - error = getaddrinfo(buffer, NULL, &hints, &info);
> - if (error != 0) {
> - snprintf(buffer, length, "getaddrinfo failed: 0x%x %s",
> - error, gai_strerror(error));
> + if (pthread_create(&t, NULL, kvp_getaddrinfo, str_ptr)) {
> + syslog(LOG_ERR, "pthread_create failed; error: %d %s",
> + errno, strerror(errno));
> return;
> }
> - snprintf(buffer, length, "%s", info->ai_canonname);
> - freeaddrinfo(info);
> + pthread_detach(t);

I think this should be complemented with pthread_cancel/pthread_join
before exiting main().

> }
>
> void print_usage(char *argv[])
> @@ -1412,11 +1439,7 @@ int main(int argc, char *argv[])
> * Retrieve OS release information.
> */
> kvp_get_os_info();
> - /*
> - * Cache Fully Qualified Domain Name because getaddrinfo takes an
> - * unpredictable amount of time to finish.
> - */
> - kvp_get_domain_name(full_domain_name, sizeof(full_domain_name));
> + kvp_obtain_domain_name(&full_domain_name);
>
> if (kvp_file_init()) {
> syslog(LOG_ERR, "Failed to initialize the pools");
> @@ -1571,7 +1594,7 @@ int main(int argc, char *argv[])
>
> switch (hv_msg->body.kvp_enum_data.index) {
> case FullyQualifiedDomainName:
> - strcpy(key_value, full_domain_name);
> + strcpy(key_value, full_domain_name ? : "");
> strcpy(key_name, "FullyQualifiedDomainName");
> break;
> case IntegrationServicesVersion:

--
Vitaly