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

From: Olaf Hering
Date: Fri Mar 19 2021 - 10:45:36 EST


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>
---
v2:
resend, the thread aims for success.

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 1e6fd6ca513b..3951b927aa3d 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;

- 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;
+ do {
+ ret = getaddrinfo(hostname, NULL, &hints, &info);
+ if (ret)
+ sleep(1);
+ } while (ret);
+
+ ret = asprintf(&tmp, "%s", info->ai_canonname);
+ freeaddrinfo(info);
+ if (ret <= 0)
+ goto out;

- error = getaddrinfo(buffer, NULL, &hints, &info);
- if (error != 0) {
- snprintf(buffer, length, "getaddrinfo failed: 0x%x %s",
- error, gai_strerror(error));
+ if (ret > HV_KVP_EXCHANGE_MAX_VALUE_SIZE)
+ tmp[HV_KVP_EXCHANGE_MAX_VALUE_SIZE - 1] = '\0';
+ *str_ptr = tmp;
+
+out:
+ pthread_exit(NULL);
+}
+
+static void kvp_obtain_domain_name(char **str_ptr)
+{
+ pthread_t t;
+
+ 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);
}

void print_usage(char *argv[])
@@ -1404,11 +1431,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");
@@ -1573,7 +1596,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: