brintos

brintos / linux-shallow public Read only

0
0
Text · 42.6 KiB · ae57bf6 Raw
1945 lines · c
1/*2 * An implementation of key value pair (KVP) functionality for Linux.3 *4 *5 * Copyright (C) 2010, Novell, Inc.6 * Author : K. Y. Srinivasan <ksrinivasan@novell.com>7 *8 * This program is free software; you can redistribute it and/or modify it9 * under the terms of the GNU General Public License version 2 as published10 * by the Free Software Foundation.11 *12 * This program is distributed in the hope that it will be useful, but13 * WITHOUT ANY WARRANTY; without even the implied warranty of14 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or15 * NON INFRINGEMENT.  See the GNU General Public License for more16 * details.17 *18 * You should have received a copy of the GNU General Public License19 * along with this program; if not, write to the Free Software20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.21 *22 */23 24 25#include <sys/poll.h>26#include <sys/utsname.h>27#include <stdio.h>28#include <stdlib.h>29#include <unistd.h>30#include <string.h>31#include <ctype.h>32#include <errno.h>33#include <arpa/inet.h>34#include <linux/hyperv.h>35#include <ifaddrs.h>36#include <netdb.h>37#include <syslog.h>38#include <sys/stat.h>39#include <fcntl.h>40#include <dirent.h>41#include <net/if.h>42#include <limits.h>43#include <getopt.h>44 45/*46 * KVP protocol: The user mode component first registers with the47 * kernel component. Subsequently, the kernel component requests, data48 * for the specified keys. In response to this message the user mode component49 * fills in the value corresponding to the specified key. We overload the50 * sequence field in the cn_msg header to define our KVP message types.51 *52 * We use this infrastructure for also supporting queries from user mode53 * application for state that may be maintained in the KVP kernel component.54 *55 */56 57 58enum key_index {59	FullyQualifiedDomainName = 0,60	IntegrationServicesVersion, /*This key is serviced in the kernel*/61	NetworkAddressIPv4,62	NetworkAddressIPv6,63	OSBuildNumber,64	OSName,65	OSMajorVersion,66	OSMinorVersion,67	OSVersion,68	ProcessorArchitecture69};70 71 72enum {73	IPADDR = 0,74	NETMASK,75	GATEWAY,76	DNS77};78 79enum {80	IPV4 = 1,81	IPV6,82	IP_TYPE_MAX83};84 85static int in_hand_shake;86 87static char *os_name = "";88static char *os_major = "";89static char *os_minor = "";90static char *processor_arch;91static char *os_build;92static char *os_version;93static char *lic_version = "Unknown version";94static char full_domain_name[HV_KVP_EXCHANGE_MAX_VALUE_SIZE];95static struct utsname uts_buf;96 97/*98 * The location of the interface configuration file.99 */100 101#define KVP_CONFIG_LOC	"/var/lib/hyperv"102 103#ifndef KVP_SCRIPTS_PATH104#define KVP_SCRIPTS_PATH "/usr/libexec/hypervkvpd/"105#endif106 107#define KVP_NET_DIR "/sys/class/net/"108 109#define MAX_FILE_NAME 100110#define ENTRIES_PER_BLOCK 50111/*112 * Change this entry if the number of addresses increases in future113 */114#define MAX_IP_ENTRIES 64115#define OUTSTR_BUF_SIZE ((INET6_ADDRSTRLEN + 1) * MAX_IP_ENTRIES)116 117struct kvp_record {118	char key[HV_KVP_EXCHANGE_MAX_KEY_SIZE];119	char value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE];120};121 122struct kvp_file_state {123	int fd;124	int num_blocks;125	struct kvp_record *records;126	int num_records;127	char fname[MAX_FILE_NAME];128};129 130static struct kvp_file_state kvp_file_info[KVP_POOL_COUNT];131 132static void kvp_acquire_lock(int pool)133{134	struct flock fl = {F_WRLCK, SEEK_SET, 0, 0, 0};135	fl.l_pid = getpid();136 137	if (fcntl(kvp_file_info[pool].fd, F_SETLKW, &fl) == -1) {138		syslog(LOG_ERR, "Failed to acquire the lock pool: %d; error: %d %s", pool,139				errno, strerror(errno));140		exit(EXIT_FAILURE);141	}142}143 144static void kvp_release_lock(int pool)145{146	struct flock fl = {F_UNLCK, SEEK_SET, 0, 0, 0};147	fl.l_pid = getpid();148 149	if (fcntl(kvp_file_info[pool].fd, F_SETLK, &fl) == -1) {150		syslog(LOG_ERR, "Failed to release the lock pool: %d; error: %d %s", pool,151				errno, strerror(errno));152		exit(EXIT_FAILURE);153	}154}155 156static void kvp_update_file(int pool)157{158	FILE *filep;159 160	/*161	 * We are going to write our in-memory registry out to162	 * disk; acquire the lock first.163	 */164	kvp_acquire_lock(pool);165 166	filep = fopen(kvp_file_info[pool].fname, "we");167	if (!filep) {168		syslog(LOG_ERR, "Failed to open file, pool: %d; error: %d %s", pool,169				errno, strerror(errno));170		kvp_release_lock(pool);171		exit(EXIT_FAILURE);172	}173 174	fwrite(kvp_file_info[pool].records, sizeof(struct kvp_record),175				kvp_file_info[pool].num_records, filep);176 177	if (ferror(filep) || fclose(filep)) {178		kvp_release_lock(pool);179		syslog(LOG_ERR, "Failed to write file, pool: %d", pool);180		exit(EXIT_FAILURE);181	}182 183	kvp_release_lock(pool);184}185 186static void kvp_update_mem_state(int pool)187{188	FILE *filep;189	size_t records_read = 0;190	struct kvp_record *record = kvp_file_info[pool].records;191	struct kvp_record *readp;192	int num_blocks = kvp_file_info[pool].num_blocks;193	int alloc_unit = sizeof(struct kvp_record) * ENTRIES_PER_BLOCK;194 195	kvp_acquire_lock(pool);196 197	filep = fopen(kvp_file_info[pool].fname, "re");198	if (!filep) {199		syslog(LOG_ERR, "Failed to open file, pool: %d; error: %d %s", pool,200				errno, strerror(errno));201		kvp_release_lock(pool);202		exit(EXIT_FAILURE);203	}204	for (;;) {205		readp = &record[records_read];206		records_read += fread(readp, sizeof(struct kvp_record),207				ENTRIES_PER_BLOCK * num_blocks - records_read,208				filep);209 210		if (ferror(filep)) {211			syslog(LOG_ERR,212				"Failed to read file, pool: %d; error: %d %s",213				 pool, errno, strerror(errno));214			kvp_release_lock(pool);215			exit(EXIT_FAILURE);216		}217 218		if (!feof(filep)) {219			/*220			 * We have more data to read.221			 */222			num_blocks++;223			record = realloc(record, alloc_unit * num_blocks);224 225			if (record == NULL) {226				syslog(LOG_ERR, "malloc failed");227				kvp_release_lock(pool);228				exit(EXIT_FAILURE);229			}230			continue;231		}232		break;233	}234 235	kvp_file_info[pool].num_blocks = num_blocks;236	kvp_file_info[pool].records = record;237	kvp_file_info[pool].num_records = records_read;238 239	fclose(filep);240	kvp_release_lock(pool);241}242 243static int kvp_file_init(void)244{245	int  fd;246	char *fname;247	int i;248	int alloc_unit = sizeof(struct kvp_record) * ENTRIES_PER_BLOCK;249 250	if (access(KVP_CONFIG_LOC, F_OK)) {251		if (mkdir(KVP_CONFIG_LOC, 0755 /* rwxr-xr-x */)) {252			syslog(LOG_ERR, "Failed to create '%s'; error: %d %s", KVP_CONFIG_LOC,253					errno, strerror(errno));254			exit(EXIT_FAILURE);255		}256	}257 258	for (i = 0; i < KVP_POOL_COUNT; i++) {259		fname = kvp_file_info[i].fname;260		sprintf(fname, "%s/.kvp_pool_%d", KVP_CONFIG_LOC, i);261		fd = open(fname, O_RDWR | O_CREAT | O_CLOEXEC, 0644 /* rw-r--r-- */);262 263		if (fd == -1)264			return 1;265 266		kvp_file_info[i].fd = fd;267		kvp_file_info[i].num_blocks = 1;268		kvp_file_info[i].records = malloc(alloc_unit);269		if (kvp_file_info[i].records == NULL)270			return 1;271		kvp_file_info[i].num_records = 0;272		kvp_update_mem_state(i);273	}274 275	return 0;276}277 278static int kvp_key_delete(int pool, const __u8 *key, int key_size)279{280	int i;281	int j, k;282	int num_records;283	struct kvp_record *record;284 285	/*286	 * First update the in-memory state.287	 */288	kvp_update_mem_state(pool);289 290	num_records = kvp_file_info[pool].num_records;291	record = kvp_file_info[pool].records;292 293	for (i = 0; i < num_records; i++) {294		if (memcmp(key, record[i].key, key_size))295			continue;296		/*297		 * Found a match; just move the remaining298		 * entries up.299		 */300		if (i == (num_records - 1)) {301			kvp_file_info[pool].num_records--;302			kvp_update_file(pool);303			return 0;304		}305 306		j = i;307		k = j + 1;308		for (; k < num_records; k++) {309			strcpy(record[j].key, record[k].key);310			strcpy(record[j].value, record[k].value);311			j++;312		}313 314		kvp_file_info[pool].num_records--;315		kvp_update_file(pool);316		return 0;317	}318	return 1;319}320 321static int kvp_key_add_or_modify(int pool, const __u8 *key, int key_size,322				 const __u8 *value, int value_size)323{324	int i;325	int num_records;326	struct kvp_record *record;327	int num_blocks;328 329	if ((key_size > HV_KVP_EXCHANGE_MAX_KEY_SIZE) ||330		(value_size > HV_KVP_EXCHANGE_MAX_VALUE_SIZE))331		return 1;332 333	/*334	 * First update the in-memory state.335	 */336	kvp_update_mem_state(pool);337 338	num_records = kvp_file_info[pool].num_records;339	record = kvp_file_info[pool].records;340	num_blocks = kvp_file_info[pool].num_blocks;341 342	for (i = 0; i < num_records; i++) {343		if (memcmp(key, record[i].key, key_size))344			continue;345		/*346		 * Found a match; just update the value -347		 * this is the modify case.348		 */349		memcpy(record[i].value, value, value_size);350		kvp_update_file(pool);351		return 0;352	}353 354	/*355	 * Need to add a new entry;356	 */357	if (num_records == (ENTRIES_PER_BLOCK * num_blocks)) {358		/* Need to allocate a larger array for reg entries. */359		record = realloc(record, sizeof(struct kvp_record) *360			 ENTRIES_PER_BLOCK * (num_blocks + 1));361 362		if (record == NULL)363			return 1;364		kvp_file_info[pool].num_blocks++;365 366	}367	memcpy(record[i].value, value, value_size);368	memcpy(record[i].key, key, key_size);369	kvp_file_info[pool].records = record;370	kvp_file_info[pool].num_records++;371	kvp_update_file(pool);372	return 0;373}374 375static int kvp_get_value(int pool, const __u8 *key, int key_size, __u8 *value,376			int value_size)377{378	int i;379	int num_records;380	struct kvp_record *record;381 382	if ((key_size > HV_KVP_EXCHANGE_MAX_KEY_SIZE) ||383		(value_size > HV_KVP_EXCHANGE_MAX_VALUE_SIZE))384		return 1;385 386	/*387	 * First update the in-memory state.388	 */389	kvp_update_mem_state(pool);390 391	num_records = kvp_file_info[pool].num_records;392	record = kvp_file_info[pool].records;393 394	for (i = 0; i < num_records; i++) {395		if (memcmp(key, record[i].key, key_size))396			continue;397		/*398		 * Found a match; just copy the value out.399		 */400		memcpy(value, record[i].value, value_size);401		return 0;402	}403 404	return 1;405}406 407static int kvp_pool_enumerate(int pool, int index, __u8 *key, int key_size,408				__u8 *value, int value_size)409{410	struct kvp_record *record;411 412	/*413	 * First update our in-memory database.414	 */415	kvp_update_mem_state(pool);416	record = kvp_file_info[pool].records;417 418	if (index >= kvp_file_info[pool].num_records) {419		return 1;420	}421 422	memcpy(key, record[index].key, key_size);423	memcpy(value, record[index].value, value_size);424	return 0;425}426 427 428void kvp_get_os_info(void)429{430	FILE	*file;431	char	*p, buf[512];432 433	uname(&uts_buf);434	os_version = uts_buf.release;435	os_build = strdup(uts_buf.release);436 437	os_name = uts_buf.sysname;438	processor_arch = uts_buf.machine;439 440	/*441	 * The current windows host (win7) expects the build442	 * string to be of the form: x.y.z443	 * Strip additional information we may have.444	 */445	p = strchr(os_version, '-');446	if (p)447		*p = '\0';448 449	/*450	 * Parse the /etc/os-release file if present:451	 * https://www.freedesktop.org/software/systemd/man/os-release.html452	 */453	file = fopen("/etc/os-release", "r");454	if (file != NULL) {455		while (fgets(buf, sizeof(buf), file)) {456			char *value, *q;457 458			/* Ignore comments */459			if (buf[0] == '#')460				continue;461 462			/* Split into name=value */463			p = strchr(buf, '=');464			if (!p)465				continue;466			*p++ = 0;467 468			/* Remove quotes and newline; un-escape */469			value = p;470			q = p;471			while (*p) {472				if (*p == '\\') {473					++p;474					if (!*p)475						break;476					*q++ = *p++;477				} else if (*p == '\'' || *p == '"' ||478					   *p == '\n') {479					++p;480				} else {481					*q++ = *p++;482				}483			}484			*q = 0;485 486			if (!strcmp(buf, "NAME")) {487				p = strdup(value);488				if (!p)489					break;490				os_name = p;491			} else if (!strcmp(buf, "VERSION_ID")) {492				p = strdup(value);493				if (!p)494					break;495				os_major = p;496			}497		}498		fclose(file);499		return;500	}501 502	/* Fallback for older RH/SUSE releases */503	file = fopen("/etc/SuSE-release", "r");504	if (file != NULL)505		goto kvp_osinfo_found;506	file  = fopen("/etc/redhat-release", "r");507	if (file != NULL)508		goto kvp_osinfo_found;509 510	/*511	 * We don't have information about the os.512	 */513	return;514 515kvp_osinfo_found:516	/* up to three lines */517	p = fgets(buf, sizeof(buf), file);518	if (p) {519		p = strchr(buf, '\n');520		if (p)521			*p = '\0';522		p = strdup(buf);523		if (!p)524			goto done;525		os_name = p;526 527		/* second line */528		p = fgets(buf, sizeof(buf), file);529		if (p) {530			p = strchr(buf, '\n');531			if (p)532				*p = '\0';533			p = strdup(buf);534			if (!p)535				goto done;536			os_major = p;537 538			/* third line */539			p = fgets(buf, sizeof(buf), file);540			if (p)  {541				p = strchr(buf, '\n');542				if (p)543					*p = '\0';544				p = strdup(buf);545				if (p)546					os_minor = p;547			}548		}549	}550 551done:552	fclose(file);553	return;554}555 556 557 558/*559 * Retrieve an interface name corresponding to the specified guid.560 * If there is a match, the function returns a pointer561 * to the interface name and if not, a NULL is returned.562 * If a match is found, the caller is responsible for563 * freeing the memory.564 */565 566static char *kvp_get_if_name(char *guid)567{568	DIR *dir;569	struct dirent *entry;570	FILE    *file;571	char    *p, *x;572	char    *if_name = NULL;573	char    buf[256];574	char dev_id[PATH_MAX];575 576	dir = opendir(KVP_NET_DIR);577	if (dir == NULL)578		return NULL;579 580	while ((entry = readdir(dir)) != NULL) {581		/*582		 * Set the state for the next pass.583		 */584		snprintf(dev_id, sizeof(dev_id), "%s%s/device/device_id",585			 KVP_NET_DIR, entry->d_name);586 587		file = fopen(dev_id, "r");588		if (file == NULL)589			continue;590 591		p = fgets(buf, sizeof(buf), file);592		if (p) {593			x = strchr(p, '\n');594			if (x)595				*x = '\0';596 597			if (!strcmp(p, guid)) {598				/*599				 * Found the guid match; return the interface600				 * name. The caller will free the memory.601				 */602				if_name = strdup(entry->d_name);603				fclose(file);604				break;605			}606		}607		fclose(file);608	}609 610	closedir(dir);611	return if_name;612}613 614/*615 * Retrieve the MAC address given the interface name.616 */617 618static char *kvp_if_name_to_mac(char *if_name)619{620	FILE    *file;621	char    *p, *x;622	char    buf[256];623	char addr_file[PATH_MAX];624	unsigned int i;625	char *mac_addr = NULL;626 627	snprintf(addr_file, sizeof(addr_file), "%s%s%s", KVP_NET_DIR,628		 if_name, "/address");629 630	file = fopen(addr_file, "r");631	if (file == NULL)632		return NULL;633 634	p = fgets(buf, sizeof(buf), file);635	if (p) {636		x = strchr(p, '\n');637		if (x)638			*x = '\0';639		for (i = 0; i < strlen(p); i++)640			p[i] = toupper(p[i]);641		mac_addr = strdup(p);642	}643 644	fclose(file);645	return mac_addr;646}647 648static void kvp_process_ipconfig_file(char *cmd,649					char *config_buf, unsigned int len,650					int element_size, int offset)651{652	char buf[256];653	char *p;654	char *x;655	FILE *file;656 657	/*658	 * First execute the command.659	 */660	file = popen(cmd, "r");661	if (file == NULL)662		return;663 664	if (offset == 0)665		memset(config_buf, 0, len);666	while ((p = fgets(buf, sizeof(buf), file)) != NULL) {667		if (len < strlen(config_buf) + element_size + 1)668			break;669 670		x = strchr(p, '\n');671		if (x)672			*x = '\0';673 674		strcat(config_buf, p);675		strcat(config_buf, ";");676	}677	pclose(file);678}679 680static void kvp_get_ipconfig_info(char *if_name,681				 struct hv_kvp_ipaddr_value *buffer)682{683	char cmd[512];684	char dhcp_info[128];685	char *p;686	FILE *file;687 688	/*689	 * Get the address of default gateway (ipv4).690	 */691	sprintf(cmd, "%s %s", "ip route show dev", if_name);692	strcat(cmd, " | awk '/default/ {print $3 }'");693 694	/*695	 * Execute the command to gather gateway info.696	 */697	kvp_process_ipconfig_file(cmd, (char *)buffer->gate_way,698				(MAX_GATEWAY_SIZE * 2), INET_ADDRSTRLEN, 0);699 700	/*701	 * Get the address of default gateway (ipv6).702	 */703	sprintf(cmd, "%s %s", "ip -f inet6  route show dev", if_name);704	strcat(cmd, " | awk '/default/ {print $3 }'");705 706	/*707	 * Execute the command to gather gateway info (ipv6).708	 */709	kvp_process_ipconfig_file(cmd, (char *)buffer->gate_way,710				(MAX_GATEWAY_SIZE * 2), INET6_ADDRSTRLEN, 1);711 712 713	/*714	 * Gather the DNS state.715	 * Since there is no standard way to get this information716	 * across various distributions of interest; we just invoke717	 * an external script that needs to be ported across distros718	 * of interest.719	 *720	 * Following is the expected format of the information from the script:721	 *722	 * ipaddr1 (nameserver1)723	 * ipaddr2 (nameserver2)724	 * .725	 * .726	 */727 728	sprintf(cmd, KVP_SCRIPTS_PATH "%s",  "hv_get_dns_info");729 730	/*731	 * Execute the command to gather DNS info.732	 */733	kvp_process_ipconfig_file(cmd, (char *)buffer->dns_addr,734				(MAX_IP_ADDR_SIZE * 2), INET_ADDRSTRLEN, 0);735 736	/*737	 * Gather the DHCP state.738	 * We will gather this state by invoking an external script.739	 * The parameter to the script is the interface name.740	 * Here is the expected output:741	 *742	 * Enabled: DHCP enabled.743	 */744 745	sprintf(cmd, KVP_SCRIPTS_PATH "%s %s", "hv_get_dhcp_info", if_name);746 747	file = popen(cmd, "r");748	if (file == NULL)749		return;750 751	p = fgets(dhcp_info, sizeof(dhcp_info), file);752	if (p == NULL) {753		pclose(file);754		return;755	}756 757	if (!strncmp(p, "Enabled", 7))758		buffer->dhcp_enabled = 1;759	else760		buffer->dhcp_enabled = 0;761 762	pclose(file);763}764 765 766static unsigned int hweight32(unsigned int *w)767{768	unsigned int res = *w - ((*w >> 1) & 0x55555555);769	res = (res & 0x33333333) + ((res >> 2) & 0x33333333);770	res = (res + (res >> 4)) & 0x0F0F0F0F;771	res = res + (res >> 8);772	return (res + (res >> 16)) & 0x000000FF;773}774 775static int kvp_process_ip_address(void *addrp,776				int family, char *buffer,777				int length,  int *offset)778{779	struct sockaddr_in *addr;780	struct sockaddr_in6 *addr6;781	int addr_length;782	char tmp[50];783	const char *str;784 785	if (family == AF_INET) {786		addr = addrp;787		str = inet_ntop(family, &addr->sin_addr, tmp, 50);788		addr_length = INET_ADDRSTRLEN;789	} else {790		addr6 = addrp;791		str = inet_ntop(family, &addr6->sin6_addr.s6_addr, tmp, 50);792		addr_length = INET6_ADDRSTRLEN;793	}794 795	if ((length - *offset) < addr_length + 2)796		return HV_E_FAIL;797	if (str == NULL) {798		strcpy(buffer, "inet_ntop failed\n");799		return HV_E_FAIL;800	}801	if (*offset == 0)802		strcpy(buffer, tmp);803	else {804		strcat(buffer, ";");805		strcat(buffer, tmp);806	}807 808	*offset += strlen(str) + 1;809 810	return 0;811}812 813static int814kvp_get_ip_info(int family, char *if_name, int op,815		 void  *out_buffer, unsigned int length)816{817	struct ifaddrs *ifap;818	struct ifaddrs *curp;819	int offset = 0;820	int sn_offset = 0;821	int error = 0;822	char *buffer;823	struct hv_kvp_ipaddr_value *ip_buffer = NULL;824	char cidr_mask[5]; /* /xyz */825	int weight;826	int i;827	unsigned int *w;828	char *sn_str;829	struct sockaddr_in6 *addr6;830 831	if (op == KVP_OP_ENUMERATE) {832		buffer = out_buffer;833	} else {834		ip_buffer = out_buffer;835		buffer = (char *)ip_buffer->ip_addr;836		ip_buffer->addr_family = 0;837	}838	/*839	 * On entry into this function, the buffer is capable of holding the840	 * maximum key value.841	 */842 843	if (getifaddrs(&ifap)) {844		strcpy(buffer, "getifaddrs failed\n");845		return HV_E_FAIL;846	}847 848	curp = ifap;849	while (curp != NULL) {850		if (curp->ifa_addr == NULL) {851			curp = curp->ifa_next;852			continue;853		}854 855		if ((if_name != NULL) &&856			(strncmp(curp->ifa_name, if_name, strlen(if_name)))) {857			/*858			 * We want info about a specific interface;859			 * just continue.860			 */861			curp = curp->ifa_next;862			continue;863		}864 865		/*866		 * We only support two address families: AF_INET and AF_INET6.867		 * If a family value of 0 is specified, we collect both868		 * supported address families; if not we gather info on869		 * the specified address family.870		 */871		if ((((family != 0) &&872			 (curp->ifa_addr->sa_family != family))) ||873			 (curp->ifa_flags & IFF_LOOPBACK)) {874			curp = curp->ifa_next;875			continue;876		}877		if ((curp->ifa_addr->sa_family != AF_INET) &&878			(curp->ifa_addr->sa_family != AF_INET6)) {879			curp = curp->ifa_next;880			continue;881		}882 883		if (op == KVP_OP_GET_IP_INFO) {884			/*885			 * Gather info other than the IP address.886			 * IP address info will be gathered later.887			 */888			if (curp->ifa_addr->sa_family == AF_INET) {889				ip_buffer->addr_family |= ADDR_FAMILY_IPV4;890				/*891				 * Get subnet info.892				 */893				error = kvp_process_ip_address(894							     curp->ifa_netmask,895							     AF_INET,896							     (char *)897							     ip_buffer->sub_net,898							     length,899							     &sn_offset);900				if (error)901					goto gather_ipaddr;902			} else {903				ip_buffer->addr_family |= ADDR_FAMILY_IPV6;904 905				/*906				 * Get subnet info in CIDR format.907				 */908				weight = 0;909				sn_str = (char *)ip_buffer->sub_net;910				addr6 = (struct sockaddr_in6 *)911					curp->ifa_netmask;912				w = addr6->sin6_addr.s6_addr32;913 914				for (i = 0; i < 4; i++)915					weight += hweight32(&w[i]);916 917				sprintf(cidr_mask, "/%d", weight);918				if (length < sn_offset + strlen(cidr_mask) + 1)919					goto gather_ipaddr;920 921				if (sn_offset == 0)922					strcpy(sn_str, cidr_mask);923				else {924					strcat((char *)ip_buffer->sub_net, ";");925					strcat(sn_str, cidr_mask);926				}927				sn_offset += strlen(sn_str) + 1;928			}929 930			/*931			 * Collect other ip related configuration info.932			 */933 934			kvp_get_ipconfig_info(if_name, ip_buffer);935		}936 937gather_ipaddr:938		error = kvp_process_ip_address(curp->ifa_addr,939						curp->ifa_addr->sa_family,940						buffer,941						length, &offset);942		if (error)943			goto getaddr_done;944 945		curp = curp->ifa_next;946	}947 948getaddr_done:949	freeifaddrs(ifap);950	return error;951}952 953/*954 * Retrieve the IP given the MAC address.955 */956static int kvp_mac_to_ip(struct hv_kvp_ipaddr_value *kvp_ip_val)957{958	char *mac = (char *)kvp_ip_val->adapter_id;959	DIR *dir;960	struct dirent *entry;961	FILE    *file;962	char    *p, *x;963	char    *if_name = NULL;964	char    buf[256];965	char dev_id[PATH_MAX];966	unsigned int i;967	int error = HV_E_FAIL;968 969	dir = opendir(KVP_NET_DIR);970	if (dir == NULL)971		return HV_E_FAIL;972 973	while ((entry = readdir(dir)) != NULL) {974		/*975		 * Set the state for the next pass.976		 */977		snprintf(dev_id, sizeof(dev_id), "%s%s/address", KVP_NET_DIR,978			 entry->d_name);979 980		file = fopen(dev_id, "r");981		if (file == NULL)982			continue;983 984		p = fgets(buf, sizeof(buf), file);985		fclose(file);986		if (!p)987			continue;988 989		x = strchr(p, '\n');990		if (x)991			*x = '\0';992 993		for (i = 0; i < strlen(p); i++)994			p[i] = toupper(p[i]);995 996		if (strcmp(p, mac))997			continue;998 999		/*1000		 * Found the MAC match.1001		 * A NIC (e.g. VF) matching the MAC, but without IP, is skipped.1002		 */1003		if_name = entry->d_name;1004		if (!if_name)1005			continue;1006 1007		error = kvp_get_ip_info(0, if_name, KVP_OP_GET_IP_INFO,1008					kvp_ip_val, MAX_IP_ADDR_SIZE * 2);1009 1010		if (!error && strlen((char *)kvp_ip_val->ip_addr))1011			break;1012	}1013 1014	closedir(dir);1015	return error;1016}1017 1018static int expand_ipv6(char *addr, int type)1019{1020	int ret;1021	struct in6_addr v6_addr;1022 1023	ret = inet_pton(AF_INET6, addr, &v6_addr);1024 1025	if (ret != 1) {1026		if (type == NETMASK)1027			return 1;1028		return 0;1029	}1030 1031	sprintf(addr, "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:"1032		"%02x%02x:%02x%02x:%02x%02x",1033		(int)v6_addr.s6_addr[0], (int)v6_addr.s6_addr[1],1034		(int)v6_addr.s6_addr[2], (int)v6_addr.s6_addr[3],1035		(int)v6_addr.s6_addr[4], (int)v6_addr.s6_addr[5],1036		(int)v6_addr.s6_addr[6], (int)v6_addr.s6_addr[7],1037		(int)v6_addr.s6_addr[8], (int)v6_addr.s6_addr[9],1038		(int)v6_addr.s6_addr[10], (int)v6_addr.s6_addr[11],1039		(int)v6_addr.s6_addr[12], (int)v6_addr.s6_addr[13],1040		(int)v6_addr.s6_addr[14], (int)v6_addr.s6_addr[15]);1041 1042	return 1;1043 1044}1045 1046static int is_ipv4(char *addr)1047{1048	int ret;1049	struct in_addr ipv4_addr;1050 1051	ret = inet_pton(AF_INET, addr, &ipv4_addr);1052 1053	if (ret == 1)1054		return 1;1055	return 0;1056}1057 1058static int parse_ip_val_buffer(char *in_buf, int *offset,1059				char *out_buf, int out_len)1060{1061	char *x;1062	char *start;1063 1064	/*1065	 * in_buf has sequence of characters that are separated by1066	 * the character ';'. The last sequence does not have the1067	 * terminating ";" character.1068	 */1069	start = in_buf + *offset;1070 1071	x = strchr(start, ';');1072	if (x)1073		*x = 0;1074	else1075		x = start + strlen(start);1076 1077	if (strlen(start) != 0) {1078		int i = 0;1079		/*1080		 * Get rid of leading spaces.1081		 */1082		while (start[i] == ' ')1083			i++;1084 1085		if ((x - start) <= out_len) {1086			strcpy(out_buf, (start + i));1087			*offset += (x - start) + 1;1088			return 1;1089		}1090	}1091	return 0;1092}1093 1094static int kvp_write_file(FILE *f, char *s1, char *s2, char *s3)1095{1096	int ret;1097 1098	ret = fprintf(f, "%s%s%s%s\n", s1, s2, "=", s3);1099 1100	if (ret < 0)1101		return HV_E_FAIL;1102 1103	return 0;1104}1105 1106 1107static int process_ip_string(FILE *f, char *ip_string, int type)1108{1109	int error = 0;1110	char addr[INET6_ADDRSTRLEN];1111	int i = 0;1112	int j = 0;1113	char str[256];1114	char sub_str[13];1115	int offset = 0;1116 1117	memset(addr, 0, sizeof(addr));1118 1119	while (parse_ip_val_buffer(ip_string, &offset, addr,1120					(MAX_IP_ADDR_SIZE * 2))) {1121 1122		sub_str[0] = 0;1123		if (is_ipv4(addr)) {1124			switch (type) {1125			case IPADDR:1126				snprintf(str, sizeof(str), "%s", "IPADDR");1127				break;1128			case NETMASK:1129				snprintf(str, sizeof(str), "%s", "NETMASK");1130				break;1131			case GATEWAY:1132				snprintf(str, sizeof(str), "%s", "GATEWAY");1133				break;1134			case DNS:1135				snprintf(str, sizeof(str), "%s", "DNS");1136				break;1137			}1138 1139			if (type == DNS) {1140				snprintf(sub_str, sizeof(sub_str), "%d", ++i);1141			} else if (type == GATEWAY && i == 0) {1142				++i;1143			} else {1144				snprintf(sub_str, sizeof(sub_str), "%d", i++);1145			}1146 1147 1148		} else if (expand_ipv6(addr, type)) {1149			switch (type) {1150			case IPADDR:1151				snprintf(str, sizeof(str), "%s", "IPV6ADDR");1152				break;1153			case NETMASK:1154				snprintf(str, sizeof(str), "%s", "IPV6NETMASK");1155				break;1156			case GATEWAY:1157				snprintf(str, sizeof(str), "%s",1158					"IPV6_DEFAULTGW");1159				break;1160			case DNS:1161				snprintf(str, sizeof(str), "%s",  "DNS");1162				break;1163			}1164 1165			if (type == DNS) {1166				snprintf(sub_str, sizeof(sub_str), "%d", ++i);1167			} else if (j == 0) {1168				++j;1169			} else {1170				snprintf(sub_str, sizeof(sub_str), "_%d", j++);1171			}1172		} else {1173			return  HV_INVALIDARG;1174		}1175 1176		error = kvp_write_file(f, str, sub_str, addr);1177		if (error)1178			return error;1179		memset(addr, 0, sizeof(addr));1180	}1181 1182	return 0;1183}1184 1185int ip_version_check(const char *input_addr)1186{1187	struct in6_addr addr;1188 1189	if (inet_pton(AF_INET, input_addr, &addr))1190		return IPV4;1191	else if (inet_pton(AF_INET6, input_addr, &addr))1192		return IPV6;1193 1194	return -EINVAL;1195}1196 1197/*1198 * Only IPv4 subnet strings needs to be converted to plen1199 * For IPv6 the subnet is already privided in plen format1200 */1201static int kvp_subnet_to_plen(char *subnet_addr_str)1202{1203	int plen = 0;1204	struct in_addr subnet_addr4;1205 1206	/*1207	 * Convert subnet address to binary representation1208	 */1209	if (inet_pton(AF_INET, subnet_addr_str, &subnet_addr4) == 1) {1210		uint32_t subnet_mask = ntohl(subnet_addr4.s_addr);1211 1212		while (subnet_mask & 0x80000000) {1213			plen++;1214			subnet_mask <<= 1;1215		}1216	} else {1217		return -1;1218	}1219 1220	return plen;1221}1222 1223static int process_dns_gateway_nm(FILE *f, char *ip_string, int type,1224				  int ip_sec)1225{1226	char addr[INET6_ADDRSTRLEN], *output_str;1227	int ip_offset = 0, error = 0, ip_ver;1228	char *param_name;1229 1230	if (type == DNS)1231		param_name = "dns";1232	else if (type == GATEWAY)1233		param_name = "gateway";1234	else1235		return -EINVAL;1236 1237	output_str = (char *)calloc(OUTSTR_BUF_SIZE, sizeof(char));1238	if (!output_str)1239		return -ENOMEM;1240 1241	while (1) {1242		memset(addr, 0, sizeof(addr));1243 1244		if (!parse_ip_val_buffer(ip_string, &ip_offset, addr,1245					 (MAX_IP_ADDR_SIZE * 2)))1246			break;1247 1248		ip_ver = ip_version_check(addr);1249		if (ip_ver < 0)1250			continue;1251 1252		if ((ip_ver == IPV4 && ip_sec == IPV4) ||1253		    (ip_ver == IPV6 && ip_sec == IPV6)) {1254			/*1255			 * do a bound check to avoid out-of bound writes1256			 */1257			if ((OUTSTR_BUF_SIZE - strlen(output_str)) >1258			    (strlen(addr) + 1)) {1259				strncat(output_str, addr,1260					OUTSTR_BUF_SIZE -1261					strlen(output_str) - 1);1262				strncat(output_str, ",",1263					OUTSTR_BUF_SIZE -1264					strlen(output_str) - 1);1265			}1266		} else {1267			continue;1268		}1269	}1270 1271	if (strlen(output_str)) {1272		/*1273		 * This is to get rid of that extra comma character1274		 * in the end of the string1275		 */1276		output_str[strlen(output_str) - 1] = '\0';1277		error = fprintf(f, "%s=%s\n", param_name, output_str);1278	}1279 1280	free(output_str);1281	return error;1282}1283 1284static int process_ip_string_nm(FILE *f, char *ip_string, char *subnet,1285				int ip_sec)1286{1287	char addr[INET6_ADDRSTRLEN];1288	char subnet_addr[INET6_ADDRSTRLEN];1289	int error = 0, i = 0;1290	int ip_offset = 0, subnet_offset = 0;1291	int plen, ip_ver;1292 1293	memset(addr, 0, sizeof(addr));1294	memset(subnet_addr, 0, sizeof(subnet_addr));1295 1296	while (parse_ip_val_buffer(ip_string, &ip_offset, addr,1297				   (MAX_IP_ADDR_SIZE * 2)) &&1298				   parse_ip_val_buffer(subnet,1299						       &subnet_offset,1300						       subnet_addr,1301						       (MAX_IP_ADDR_SIZE *1302							2))) {1303		ip_ver = ip_version_check(addr);1304		if (ip_ver < 0)1305			continue;1306 1307		if (ip_ver == IPV4 && ip_sec == IPV4)1308			plen = kvp_subnet_to_plen((char *)subnet_addr);1309		else if (ip_ver == IPV6 && ip_sec == IPV6)1310			plen = atoi(subnet_addr);1311		else1312			continue;1313 1314		if (plen < 0)1315			return plen;1316 1317		error = fprintf(f, "address%d=%s/%d\n", ++i, (char *)addr,1318				plen);1319		if (error < 0)1320			return error;1321 1322		memset(addr, 0, sizeof(addr));1323		memset(subnet_addr, 0, sizeof(subnet_addr));1324	}1325 1326	return error;1327}1328 1329static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val)1330{1331	int error = 0, ip_ver;1332	char if_filename[PATH_MAX];1333	char nm_filename[PATH_MAX];1334	FILE *ifcfg_file, *nmfile;1335	char cmd[PATH_MAX];1336	char *mac_addr;1337	int str_len;1338 1339	/*1340	 * Set the configuration for the specified interface with1341	 * the information provided. Since there is no standard1342	 * way to configure an interface, we will have an external1343	 * script that does the job of configuring the interface and1344	 * flushing the configuration.1345	 *1346	 * The parameters passed to this external script are:1347	 * 1. A configuration file that has the specified configuration.1348	 *1349	 * We will embed the name of the interface in the configuration1350	 * file: ifcfg-ethx (where ethx is the interface name).1351	 *1352	 * The information provided here may be more than what is needed1353	 * in a given distro to configure the interface and so are free1354	 * ignore information that may not be relevant.1355	 *1356	 * Here is the ifcfg format of the ip configuration file:1357	 *1358	 * HWADDR=macaddr1359	 * DEVICE=interface name1360	 * BOOTPROTO=<protocol> (where <protocol> is "dhcp" if DHCP is configured1361	 *                       or "none" if no boot-time protocol should be used)1362	 *1363	 * IPADDR0=ipaddr11364	 * IPADDR1=ipaddr21365	 * IPADDRx=ipaddry (where y = x + 1)1366	 *1367	 * NETMASK0=netmask11368	 * NETMASKx=netmasky (where y = x + 1)1369	 *1370	 * GATEWAY=ipaddr11371	 * GATEWAYx=ipaddry (where y = x + 1)1372	 *1373	 * DNSx=ipaddrx (where first DNS address is tagged as DNS1 etc)1374	 *1375	 * IPV6 addresses will be tagged as IPV6ADDR, IPV6 gateway will be1376	 * tagged as IPV6_DEFAULTGW and IPV6 NETMASK will be tagged as1377	 * IPV6NETMASK.1378	 *1379	 * Here is the keyfile format of the ip configuration file:1380	 *1381	 * [ethernet]1382	 * mac-address=macaddr1383	 * [connection]1384	 * interface-name=interface name1385	 *1386	 * [ipv4]1387	 * method=<protocol> (where <protocol> is "auto" if DHCP is configured1388	 *                       or "manual" if no boot-time protocol should be used)1389	 *1390	 * address1=ipaddr1/plen1391	 * address2=ipaddr2/plen1392	 *1393	 * gateway=gateway1;gateway21394	 *1395	 * dns=dns1;dns21396	 *1397	 * [ipv6]1398	 * address1=ipaddr1/plen1399	 * address2=ipaddr2/plen1400	 *1401	 * gateway=gateway1;gateway21402	 *1403	 * dns=dns1;dns21404	 *1405	 * The host can specify multiple ipv4 and ipv6 addresses to be1406	 * configured for the interface. Furthermore, the configuration1407	 * needs to be persistent. A subsequent GET call on the interface1408	 * is expected to return the configuration that is set via the SET1409	 * call.1410	 */1411 1412	/*1413	 * We are populating both ifcfg and nmconnection files1414	 */1415	snprintf(if_filename, sizeof(if_filename), "%s%s%s", KVP_CONFIG_LOC,1416		 "/ifcfg-", if_name);1417 1418	ifcfg_file = fopen(if_filename, "w");1419 1420	if (!ifcfg_file) {1421		syslog(LOG_ERR, "Failed to open config file; error: %d %s",1422		       errno, strerror(errno));1423		return HV_E_FAIL;1424	}1425 1426	snprintf(nm_filename, sizeof(nm_filename), "%s%s%s%s", KVP_CONFIG_LOC,1427		 "/", if_name, ".nmconnection");1428 1429	nmfile = fopen(nm_filename, "w");1430 1431	if (!nmfile) {1432		syslog(LOG_ERR, "Failed to open config file; error: %d %s",1433		       errno, strerror(errno));1434		fclose(ifcfg_file);1435		return HV_E_FAIL;1436	}1437 1438	/*1439	 * First write out the MAC address.1440	 */1441 1442	mac_addr = kvp_if_name_to_mac(if_name);1443	if (mac_addr == NULL) {1444		error = HV_E_FAIL;1445		goto setval_error;1446	}1447 1448	error = kvp_write_file(ifcfg_file, "HWADDR", "", mac_addr);1449	if (error < 0)1450		goto setmac_error;1451 1452	error = kvp_write_file(ifcfg_file, "DEVICE", "", if_name);1453	if (error < 0)1454		goto setmac_error;1455 1456	error = fprintf(nmfile, "\n[connection]\n");1457	if (error < 0)1458		goto setmac_error;1459 1460	error = kvp_write_file(nmfile, "interface-name", "", if_name);1461	if (error)1462		goto setmac_error;1463 1464	error = fprintf(nmfile, "\n[ethernet]\n");1465	if (error < 0)1466		goto setmac_error;1467 1468	error = kvp_write_file(nmfile, "mac-address", "", mac_addr);1469	if (error)1470		goto setmac_error;1471 1472	free(mac_addr);1473 1474	/*1475	 * The dhcp_enabled flag is only for IPv4. In the case the host only1476	 * injects an IPv6 address, the flag is true, but we still need to1477	 * proceed to parse and pass the IPv6 information to the1478	 * disto-specific script hv_set_ifconfig.1479	 */1480 1481	/*1482	 * First populate the ifcfg file format1483	 */1484	if (new_val->dhcp_enabled) {1485		error = kvp_write_file(ifcfg_file, "BOOTPROTO", "", "dhcp");1486		if (error)1487			goto setval_error;1488	} else {1489		error = kvp_write_file(ifcfg_file, "BOOTPROTO", "", "none");1490		if (error)1491			goto setval_error;1492	}1493 1494	error = process_ip_string(ifcfg_file, (char *)new_val->ip_addr,1495				  IPADDR);1496	if (error)1497		goto setval_error;1498 1499	error = process_ip_string(ifcfg_file, (char *)new_val->sub_net,1500				  NETMASK);1501	if (error)1502		goto setval_error;1503 1504	error = process_ip_string(ifcfg_file, (char *)new_val->gate_way,1505				  GATEWAY);1506	if (error)1507		goto setval_error;1508 1509	error = process_ip_string(ifcfg_file, (char *)new_val->dns_addr, DNS);1510	if (error)1511		goto setval_error;1512 1513	/*1514	 * Now we populate the keyfile format1515	 *1516	 * The keyfile format expects the IPv6 and IPv4 configuration in1517	 * different sections. Therefore we iterate through the list twice,1518	 * once to populate the IPv4 section and the next time for IPv61519	 */1520	ip_ver = IPV4;1521	do {1522		if (ip_ver == IPV4) {1523			error = fprintf(nmfile, "\n[ipv4]\n");1524			if (error < 0)1525				goto setval_error;1526		} else {1527			error = fprintf(nmfile, "\n[ipv6]\n");1528			if (error < 0)1529				goto setval_error;1530		}1531 1532		/*1533		 * Write the configuration for ipaddress, netmask, gateway and1534		 * name services1535		 */1536		error = process_ip_string_nm(nmfile, (char *)new_val->ip_addr,1537					     (char *)new_val->sub_net,1538					     ip_ver);1539		if (error < 0)1540			goto setval_error;1541 1542		/*1543		 * As dhcp_enabled is only valid for ipv4, we do not set dhcp1544		 * methods for ipv6 based on dhcp_enabled flag.1545		 *1546		 * For ipv4, set method to manual only when dhcp_enabled is1547		 * false and specific ipv4 addresses are configured. If neither1548		 * dhcp_enabled is true and no ipv4 addresses are configured,1549		 * set method to 'disabled'.1550		 *1551		 * For ipv6, set method to manual when we configure ipv61552		 * addresses. Otherwise set method to 'auto' so that SLAAC from1553		 * RA may be used.1554		 */1555		if (ip_ver == IPV4) {1556			if (new_val->dhcp_enabled) {1557				error = kvp_write_file(nmfile, "method", "",1558						       "auto");1559				if (error < 0)1560					goto setval_error;1561			} else if (error) {1562				error = kvp_write_file(nmfile, "method", "",1563						       "manual");1564				if (error < 0)1565					goto setval_error;1566			} else {1567				error = kvp_write_file(nmfile, "method", "",1568						       "disabled");1569				if (error < 0)1570					goto setval_error;1571			}1572		} else if (ip_ver == IPV6) {1573			if (error) {1574				error = kvp_write_file(nmfile, "method", "",1575						       "manual");1576				if (error < 0)1577					goto setval_error;1578			} else {1579				error = kvp_write_file(nmfile, "method", "",1580						       "auto");1581				if (error < 0)1582					goto setval_error;1583			}1584		}1585 1586		error = process_dns_gateway_nm(nmfile,1587					       (char *)new_val->gate_way,1588					       GATEWAY, ip_ver);1589		if (error < 0)1590			goto setval_error;1591 1592		error = process_dns_gateway_nm(nmfile,1593					       (char *)new_val->dns_addr, DNS,1594					       ip_ver);1595		if (error < 0)1596			goto setval_error;1597 1598		ip_ver++;1599	} while (ip_ver < IP_TYPE_MAX);1600 1601	fclose(nmfile);1602	fclose(ifcfg_file);1603 1604	/*1605	 * Now that we have populated the configuration file,1606	 * invoke the external script to do its magic.1607	 */1608 1609	str_len = snprintf(cmd, sizeof(cmd), KVP_SCRIPTS_PATH "%s %s %s",1610			   "hv_set_ifconfig", if_filename, nm_filename);1611	/*1612	 * This is a little overcautious, but it's necessary to suppress some1613	 * false warnings from gcc 8.0.1.1614	 */1615	if (str_len <= 0 || (unsigned int)str_len >= sizeof(cmd)) {1616		syslog(LOG_ERR, "Cmd '%s' (len=%d) may be too long",1617		       cmd, str_len);1618		return HV_E_FAIL;1619	}1620 1621	if (system(cmd)) {1622		syslog(LOG_ERR, "Failed to execute cmd '%s'; error: %d %s",1623		       cmd, errno, strerror(errno));1624		return HV_E_FAIL;1625	}1626	return 0;1627setmac_error:1628	free(mac_addr);1629setval_error:1630	syslog(LOG_ERR, "Failed to write config file");1631	fclose(ifcfg_file);1632	fclose(nmfile);1633	return error;1634}1635 1636 1637static void1638kvp_get_domain_name(char *buffer, int length)1639{1640	struct addrinfo	hints, *info ;1641	int error = 0;1642 1643	gethostname(buffer, length);1644	memset(&hints, 0, sizeof(hints));1645	hints.ai_family = AF_INET; /*Get only ipv4 addrinfo. */1646	hints.ai_socktype = SOCK_STREAM;1647	hints.ai_flags = AI_CANONNAME;1648 1649	error = getaddrinfo(buffer, NULL, &hints, &info);1650	if (error != 0) {1651		snprintf(buffer, length, "getaddrinfo failed: 0x%x %s",1652			error, gai_strerror(error));1653		return;1654	}1655	snprintf(buffer, length, "%s", info->ai_canonname);1656	freeaddrinfo(info);1657}1658 1659void print_usage(char *argv[])1660{1661	fprintf(stderr, "Usage: %s [options]\n"1662		"Options are:\n"1663		"  -n, --no-daemon        stay in foreground, don't daemonize\n"1664		"  -h, --help             print this help\n", argv[0]);1665}1666 1667int main(int argc, char *argv[])1668{1669	int kvp_fd = -1, len;1670	int error;1671	struct pollfd pfd;1672	char    *p;1673	struct hv_kvp_msg hv_msg[1];1674	char	*key_value;1675	char	*key_name;1676	int	op;1677	int	pool;1678	char	*if_name;1679	struct hv_kvp_ipaddr_value *kvp_ip_val;1680	int daemonize = 1, long_index = 0, opt;1681 1682	static struct option long_options[] = {1683		{"help",	no_argument,	   0,  'h' },1684		{"no-daemon",	no_argument,	   0,  'n' },1685		{0,		0,		   0,  0   }1686	};1687 1688	while ((opt = getopt_long(argc, argv, "hn", long_options,1689				  &long_index)) != -1) {1690		switch (opt) {1691		case 'n':1692			daemonize = 0;1693			break;1694		case 'h':1695			print_usage(argv);1696			exit(0);1697		default:1698			print_usage(argv);1699			exit(EXIT_FAILURE);1700		}1701	}1702 1703	if (daemonize && daemon(1, 0))1704		return 1;1705 1706	openlog("KVP", 0, LOG_USER);1707	syslog(LOG_INFO, "KVP starting; pid is:%d", getpid());1708 1709	/*1710	 * Retrieve OS release information.1711	 */1712	kvp_get_os_info();1713	/*1714	 * Cache Fully Qualified Domain Name because getaddrinfo takes an1715	 * unpredictable amount of time to finish.1716	 */1717	kvp_get_domain_name(full_domain_name, sizeof(full_domain_name));1718 1719	if (kvp_file_init()) {1720		syslog(LOG_ERR, "Failed to initialize the pools");1721		exit(EXIT_FAILURE);1722	}1723 1724reopen_kvp_fd:1725	if (kvp_fd != -1)1726		close(kvp_fd);1727	in_hand_shake = 1;1728	kvp_fd = open("/dev/vmbus/hv_kvp", O_RDWR | O_CLOEXEC);1729 1730	if (kvp_fd < 0) {1731		syslog(LOG_ERR, "open /dev/vmbus/hv_kvp failed; error: %d %s",1732		       errno, strerror(errno));1733		exit(EXIT_FAILURE);1734	}1735 1736	/*1737	 * Register ourselves with the kernel.1738	 */1739	hv_msg->kvp_hdr.operation = KVP_OP_REGISTER1;1740	len = write(kvp_fd, hv_msg, sizeof(struct hv_kvp_msg));1741	if (len != sizeof(struct hv_kvp_msg)) {1742		syslog(LOG_ERR, "registration to kernel failed; error: %d %s",1743		       errno, strerror(errno));1744		close(kvp_fd);1745		exit(EXIT_FAILURE);1746	}1747 1748	pfd.fd = kvp_fd;1749 1750	while (1) {1751		pfd.events = POLLIN;1752		pfd.revents = 0;1753 1754		if (poll(&pfd, 1, -1) < 0) {1755			syslog(LOG_ERR, "poll failed; error: %d %s", errno, strerror(errno));1756			if (errno == EINVAL) {1757				close(kvp_fd);1758				exit(EXIT_FAILURE);1759			}1760			else1761				continue;1762		}1763 1764		len = read(kvp_fd, hv_msg, sizeof(struct hv_kvp_msg));1765 1766		if (len != sizeof(struct hv_kvp_msg)) {1767			syslog(LOG_ERR, "read failed; error:%d %s",1768			       errno, strerror(errno));1769			goto reopen_kvp_fd;1770		}1771 1772		/*1773		 * We will use the KVP header information to pass back1774		 * the error from this daemon. So, first copy the state1775		 * and set the error code to success.1776		 */1777		op = hv_msg->kvp_hdr.operation;1778		pool = hv_msg->kvp_hdr.pool;1779		hv_msg->error = HV_S_OK;1780 1781		if ((in_hand_shake) && (op == KVP_OP_REGISTER1)) {1782			/*1783			 * Driver is registering with us; stash away the version1784			 * information.1785			 */1786			in_hand_shake = 0;1787			p = (char *)hv_msg->body.kvp_register.version;1788			lic_version = malloc(strlen(p) + 1);1789			if (lic_version) {1790				strcpy(lic_version, p);1791				syslog(LOG_INFO, "KVP LIC Version: %s",1792				       lic_version);1793			} else {1794				syslog(LOG_ERR, "malloc failed");1795			}1796			continue;1797		}1798 1799		switch (op) {1800		case KVP_OP_GET_IP_INFO:1801			kvp_ip_val = &hv_msg->body.kvp_ip_val;1802 1803			error = kvp_mac_to_ip(kvp_ip_val);1804 1805			if (error)1806				hv_msg->error = error;1807 1808			break;1809 1810		case KVP_OP_SET_IP_INFO:1811			kvp_ip_val = &hv_msg->body.kvp_ip_val;1812			if_name = kvp_get_if_name(1813					(char *)kvp_ip_val->adapter_id);1814			if (if_name == NULL) {1815				/*1816				 * We could not map the guid to an1817				 * interface name; return error.1818				 */1819				hv_msg->error = HV_GUID_NOTFOUND;1820				break;1821			}1822			error = kvp_set_ip_info(if_name, kvp_ip_val);1823			if (error)1824				hv_msg->error = error;1825 1826			free(if_name);1827			break;1828 1829		case KVP_OP_SET:1830			if (kvp_key_add_or_modify(pool,1831					hv_msg->body.kvp_set.data.key,1832					hv_msg->body.kvp_set.data.key_size,1833					hv_msg->body.kvp_set.data.value,1834					hv_msg->body.kvp_set.data.value_size))1835					hv_msg->error = HV_S_CONT;1836			break;1837 1838		case KVP_OP_GET:1839			if (kvp_get_value(pool,1840					hv_msg->body.kvp_set.data.key,1841					hv_msg->body.kvp_set.data.key_size,1842					hv_msg->body.kvp_set.data.value,1843					hv_msg->body.kvp_set.data.value_size))1844					hv_msg->error = HV_S_CONT;1845			break;1846 1847		case KVP_OP_DELETE:1848			if (kvp_key_delete(pool,1849					hv_msg->body.kvp_delete.key,1850					hv_msg->body.kvp_delete.key_size))1851					hv_msg->error = HV_S_CONT;1852			break;1853 1854		default:1855			break;1856		}1857 1858		if (op != KVP_OP_ENUMERATE)1859			goto kvp_done;1860 1861		/*1862		 * If the pool is KVP_POOL_AUTO, dynamically generate1863		 * both the key and the value; if not read from the1864		 * appropriate pool.1865		 */1866		if (pool != KVP_POOL_AUTO) {1867			if (kvp_pool_enumerate(pool,1868					hv_msg->body.kvp_enum_data.index,1869					hv_msg->body.kvp_enum_data.data.key,1870					HV_KVP_EXCHANGE_MAX_KEY_SIZE,1871					hv_msg->body.kvp_enum_data.data.value,1872					HV_KVP_EXCHANGE_MAX_VALUE_SIZE))1873					hv_msg->error = HV_S_CONT;1874			goto kvp_done;1875		}1876 1877		key_name = (char *)hv_msg->body.kvp_enum_data.data.key;1878		key_value = (char *)hv_msg->body.kvp_enum_data.data.value;1879 1880		switch (hv_msg->body.kvp_enum_data.index) {1881		case FullyQualifiedDomainName:1882			strcpy(key_value, full_domain_name);1883			strcpy(key_name, "FullyQualifiedDomainName");1884			break;1885		case IntegrationServicesVersion:1886			strcpy(key_name, "IntegrationServicesVersion");1887			strcpy(key_value, lic_version);1888			break;1889		case NetworkAddressIPv4:1890			kvp_get_ip_info(AF_INET, NULL, KVP_OP_ENUMERATE,1891				key_value, HV_KVP_EXCHANGE_MAX_VALUE_SIZE);1892			strcpy(key_name, "NetworkAddressIPv4");1893			break;1894		case NetworkAddressIPv6:1895			kvp_get_ip_info(AF_INET6, NULL, KVP_OP_ENUMERATE,1896				key_value, HV_KVP_EXCHANGE_MAX_VALUE_SIZE);1897			strcpy(key_name, "NetworkAddressIPv6");1898			break;1899		case OSBuildNumber:1900			strcpy(key_value, os_build);1901			strcpy(key_name, "OSBuildNumber");1902			break;1903		case OSName:1904			strcpy(key_value, os_name);1905			strcpy(key_name, "OSName");1906			break;1907		case OSMajorVersion:1908			strcpy(key_value, os_major);1909			strcpy(key_name, "OSMajorVersion");1910			break;1911		case OSMinorVersion:1912			strcpy(key_value, os_minor);1913			strcpy(key_name, "OSMinorVersion");1914			break;1915		case OSVersion:1916			strcpy(key_value, os_version);1917			strcpy(key_name, "OSVersion");1918			break;1919		case ProcessorArchitecture:1920			strcpy(key_value, processor_arch);1921			strcpy(key_name, "ProcessorArchitecture");1922			break;1923		default:1924			hv_msg->error = HV_S_CONT;1925			break;1926		}1927 1928		/*1929		 * Send the value back to the kernel. Note: the write() may1930		 * return an error due to hibernation; we can ignore the error1931		 * by resetting the dev file, i.e. closing and re-opening it.1932		 */1933kvp_done:1934		len = write(kvp_fd, hv_msg, sizeof(struct hv_kvp_msg));1935		if (len != sizeof(struct hv_kvp_msg)) {1936			syslog(LOG_ERR, "write failed; error: %d %s", errno,1937			       strerror(errno));1938			goto reopen_kvp_fd;1939		}1940	}1941 1942	close(kvp_fd);1943	exit(0);1944}1945