18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * An implementation of key value pair (KVP) functionality for Linux.
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2010, Novell, Inc.
68c2ecf20Sopenharmony_ci * Author : K. Y. Srinivasan <ksrinivasan@novell.com>
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify it
98c2ecf20Sopenharmony_ci * under the terms of the GNU General Public License version 2 as published
108c2ecf20Sopenharmony_ci * by the Free Software Foundation.
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci * This program is distributed in the hope that it will be useful, but
138c2ecf20Sopenharmony_ci * WITHOUT ANY WARRANTY; without even the implied warranty of
148c2ecf20Sopenharmony_ci * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
158c2ecf20Sopenharmony_ci * NON INFRINGEMENT.  See the GNU General Public License for more
168c2ecf20Sopenharmony_ci * details.
178c2ecf20Sopenharmony_ci *
188c2ecf20Sopenharmony_ci * You should have received a copy of the GNU General Public License
198c2ecf20Sopenharmony_ci * along with this program; if not, write to the Free Software
208c2ecf20Sopenharmony_ci * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
218c2ecf20Sopenharmony_ci *
228c2ecf20Sopenharmony_ci */
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#include <sys/poll.h>
268c2ecf20Sopenharmony_ci#include <sys/utsname.h>
278c2ecf20Sopenharmony_ci#include <stdio.h>
288c2ecf20Sopenharmony_ci#include <stdlib.h>
298c2ecf20Sopenharmony_ci#include <unistd.h>
308c2ecf20Sopenharmony_ci#include <string.h>
318c2ecf20Sopenharmony_ci#include <ctype.h>
328c2ecf20Sopenharmony_ci#include <errno.h>
338c2ecf20Sopenharmony_ci#include <arpa/inet.h>
348c2ecf20Sopenharmony_ci#include <linux/hyperv.h>
358c2ecf20Sopenharmony_ci#include <ifaddrs.h>
368c2ecf20Sopenharmony_ci#include <netdb.h>
378c2ecf20Sopenharmony_ci#include <syslog.h>
388c2ecf20Sopenharmony_ci#include <sys/stat.h>
398c2ecf20Sopenharmony_ci#include <fcntl.h>
408c2ecf20Sopenharmony_ci#include <dirent.h>
418c2ecf20Sopenharmony_ci#include <net/if.h>
428c2ecf20Sopenharmony_ci#include <limits.h>
438c2ecf20Sopenharmony_ci#include <getopt.h>
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci/*
468c2ecf20Sopenharmony_ci * KVP protocol: The user mode component first registers with the
478c2ecf20Sopenharmony_ci * the kernel component. Subsequently, the kernel component requests, data
488c2ecf20Sopenharmony_ci * for the specified keys. In response to this message the user mode component
498c2ecf20Sopenharmony_ci * fills in the value corresponding to the specified key. We overload the
508c2ecf20Sopenharmony_ci * sequence field in the cn_msg header to define our KVP message types.
518c2ecf20Sopenharmony_ci *
528c2ecf20Sopenharmony_ci * We use this infrastructure for also supporting queries from user mode
538c2ecf20Sopenharmony_ci * application for state that may be maintained in the KVP kernel component.
548c2ecf20Sopenharmony_ci *
558c2ecf20Sopenharmony_ci */
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cienum key_index {
598c2ecf20Sopenharmony_ci	FullyQualifiedDomainName = 0,
608c2ecf20Sopenharmony_ci	IntegrationServicesVersion, /*This key is serviced in the kernel*/
618c2ecf20Sopenharmony_ci	NetworkAddressIPv4,
628c2ecf20Sopenharmony_ci	NetworkAddressIPv6,
638c2ecf20Sopenharmony_ci	OSBuildNumber,
648c2ecf20Sopenharmony_ci	OSName,
658c2ecf20Sopenharmony_ci	OSMajorVersion,
668c2ecf20Sopenharmony_ci	OSMinorVersion,
678c2ecf20Sopenharmony_ci	OSVersion,
688c2ecf20Sopenharmony_ci	ProcessorArchitecture
698c2ecf20Sopenharmony_ci};
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cienum {
738c2ecf20Sopenharmony_ci	IPADDR = 0,
748c2ecf20Sopenharmony_ci	NETMASK,
758c2ecf20Sopenharmony_ci	GATEWAY,
768c2ecf20Sopenharmony_ci	DNS
778c2ecf20Sopenharmony_ci};
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_cistatic int in_hand_shake;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_cistatic char *os_name = "";
828c2ecf20Sopenharmony_cistatic char *os_major = "";
838c2ecf20Sopenharmony_cistatic char *os_minor = "";
848c2ecf20Sopenharmony_cistatic char *processor_arch;
858c2ecf20Sopenharmony_cistatic char *os_build;
868c2ecf20Sopenharmony_cistatic char *os_version;
878c2ecf20Sopenharmony_cistatic char *lic_version = "Unknown version";
888c2ecf20Sopenharmony_cistatic char full_domain_name[HV_KVP_EXCHANGE_MAX_VALUE_SIZE];
898c2ecf20Sopenharmony_cistatic struct utsname uts_buf;
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci/*
928c2ecf20Sopenharmony_ci * The location of the interface configuration file.
938c2ecf20Sopenharmony_ci */
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci#define KVP_CONFIG_LOC	"/var/lib/hyperv"
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci#ifndef KVP_SCRIPTS_PATH
988c2ecf20Sopenharmony_ci#define KVP_SCRIPTS_PATH "/usr/libexec/hypervkvpd/"
998c2ecf20Sopenharmony_ci#endif
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci#define KVP_NET_DIR "/sys/class/net/"
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci#define MAX_FILE_NAME 100
1048c2ecf20Sopenharmony_ci#define ENTRIES_PER_BLOCK 50
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_cistruct kvp_record {
1078c2ecf20Sopenharmony_ci	char key[HV_KVP_EXCHANGE_MAX_KEY_SIZE];
1088c2ecf20Sopenharmony_ci	char value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE];
1098c2ecf20Sopenharmony_ci};
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_cistruct kvp_file_state {
1128c2ecf20Sopenharmony_ci	int fd;
1138c2ecf20Sopenharmony_ci	int num_blocks;
1148c2ecf20Sopenharmony_ci	struct kvp_record *records;
1158c2ecf20Sopenharmony_ci	int num_records;
1168c2ecf20Sopenharmony_ci	char fname[MAX_FILE_NAME];
1178c2ecf20Sopenharmony_ci};
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_cistatic struct kvp_file_state kvp_file_info[KVP_POOL_COUNT];
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_cistatic void kvp_acquire_lock(int pool)
1228c2ecf20Sopenharmony_ci{
1238c2ecf20Sopenharmony_ci	struct flock fl = {F_WRLCK, SEEK_SET, 0, 0, 0};
1248c2ecf20Sopenharmony_ci	fl.l_pid = getpid();
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	if (fcntl(kvp_file_info[pool].fd, F_SETLKW, &fl) == -1) {
1278c2ecf20Sopenharmony_ci		syslog(LOG_ERR, "Failed to acquire the lock pool: %d; error: %d %s", pool,
1288c2ecf20Sopenharmony_ci				errno, strerror(errno));
1298c2ecf20Sopenharmony_ci		exit(EXIT_FAILURE);
1308c2ecf20Sopenharmony_ci	}
1318c2ecf20Sopenharmony_ci}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_cistatic void kvp_release_lock(int pool)
1348c2ecf20Sopenharmony_ci{
1358c2ecf20Sopenharmony_ci	struct flock fl = {F_UNLCK, SEEK_SET, 0, 0, 0};
1368c2ecf20Sopenharmony_ci	fl.l_pid = getpid();
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	if (fcntl(kvp_file_info[pool].fd, F_SETLK, &fl) == -1) {
1398c2ecf20Sopenharmony_ci		syslog(LOG_ERR, "Failed to release the lock pool: %d; error: %d %s", pool,
1408c2ecf20Sopenharmony_ci				errno, strerror(errno));
1418c2ecf20Sopenharmony_ci		exit(EXIT_FAILURE);
1428c2ecf20Sopenharmony_ci	}
1438c2ecf20Sopenharmony_ci}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_cistatic void kvp_update_file(int pool)
1468c2ecf20Sopenharmony_ci{
1478c2ecf20Sopenharmony_ci	FILE *filep;
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	/*
1508c2ecf20Sopenharmony_ci	 * We are going to write our in-memory registry out to
1518c2ecf20Sopenharmony_ci	 * disk; acquire the lock first.
1528c2ecf20Sopenharmony_ci	 */
1538c2ecf20Sopenharmony_ci	kvp_acquire_lock(pool);
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	filep = fopen(kvp_file_info[pool].fname, "we");
1568c2ecf20Sopenharmony_ci	if (!filep) {
1578c2ecf20Sopenharmony_ci		syslog(LOG_ERR, "Failed to open file, pool: %d; error: %d %s", pool,
1588c2ecf20Sopenharmony_ci				errno, strerror(errno));
1598c2ecf20Sopenharmony_ci		kvp_release_lock(pool);
1608c2ecf20Sopenharmony_ci		exit(EXIT_FAILURE);
1618c2ecf20Sopenharmony_ci	}
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	fwrite(kvp_file_info[pool].records, sizeof(struct kvp_record),
1648c2ecf20Sopenharmony_ci				kvp_file_info[pool].num_records, filep);
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	if (ferror(filep) || fclose(filep)) {
1678c2ecf20Sopenharmony_ci		kvp_release_lock(pool);
1688c2ecf20Sopenharmony_ci		syslog(LOG_ERR, "Failed to write file, pool: %d", pool);
1698c2ecf20Sopenharmony_ci		exit(EXIT_FAILURE);
1708c2ecf20Sopenharmony_ci	}
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	kvp_release_lock(pool);
1738c2ecf20Sopenharmony_ci}
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_cistatic void kvp_update_mem_state(int pool)
1768c2ecf20Sopenharmony_ci{
1778c2ecf20Sopenharmony_ci	FILE *filep;
1788c2ecf20Sopenharmony_ci	size_t records_read = 0;
1798c2ecf20Sopenharmony_ci	struct kvp_record *record = kvp_file_info[pool].records;
1808c2ecf20Sopenharmony_ci	struct kvp_record *readp;
1818c2ecf20Sopenharmony_ci	int num_blocks = kvp_file_info[pool].num_blocks;
1828c2ecf20Sopenharmony_ci	int alloc_unit = sizeof(struct kvp_record) * ENTRIES_PER_BLOCK;
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	kvp_acquire_lock(pool);
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	filep = fopen(kvp_file_info[pool].fname, "re");
1878c2ecf20Sopenharmony_ci	if (!filep) {
1888c2ecf20Sopenharmony_ci		syslog(LOG_ERR, "Failed to open file, pool: %d; error: %d %s", pool,
1898c2ecf20Sopenharmony_ci				errno, strerror(errno));
1908c2ecf20Sopenharmony_ci		kvp_release_lock(pool);
1918c2ecf20Sopenharmony_ci		exit(EXIT_FAILURE);
1928c2ecf20Sopenharmony_ci	}
1938c2ecf20Sopenharmony_ci	for (;;) {
1948c2ecf20Sopenharmony_ci		readp = &record[records_read];
1958c2ecf20Sopenharmony_ci		records_read += fread(readp, sizeof(struct kvp_record),
1968c2ecf20Sopenharmony_ci				ENTRIES_PER_BLOCK * num_blocks - records_read,
1978c2ecf20Sopenharmony_ci				filep);
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci		if (ferror(filep)) {
2008c2ecf20Sopenharmony_ci			syslog(LOG_ERR,
2018c2ecf20Sopenharmony_ci				"Failed to read file, pool: %d; error: %d %s",
2028c2ecf20Sopenharmony_ci				 pool, errno, strerror(errno));
2038c2ecf20Sopenharmony_ci			kvp_release_lock(pool);
2048c2ecf20Sopenharmony_ci			exit(EXIT_FAILURE);
2058c2ecf20Sopenharmony_ci		}
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci		if (!feof(filep)) {
2088c2ecf20Sopenharmony_ci			/*
2098c2ecf20Sopenharmony_ci			 * We have more data to read.
2108c2ecf20Sopenharmony_ci			 */
2118c2ecf20Sopenharmony_ci			num_blocks++;
2128c2ecf20Sopenharmony_ci			record = realloc(record, alloc_unit * num_blocks);
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci			if (record == NULL) {
2158c2ecf20Sopenharmony_ci				syslog(LOG_ERR, "malloc failed");
2168c2ecf20Sopenharmony_ci				kvp_release_lock(pool);
2178c2ecf20Sopenharmony_ci				exit(EXIT_FAILURE);
2188c2ecf20Sopenharmony_ci			}
2198c2ecf20Sopenharmony_ci			continue;
2208c2ecf20Sopenharmony_ci		}
2218c2ecf20Sopenharmony_ci		break;
2228c2ecf20Sopenharmony_ci	}
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	kvp_file_info[pool].num_blocks = num_blocks;
2258c2ecf20Sopenharmony_ci	kvp_file_info[pool].records = record;
2268c2ecf20Sopenharmony_ci	kvp_file_info[pool].num_records = records_read;
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	fclose(filep);
2298c2ecf20Sopenharmony_ci	kvp_release_lock(pool);
2308c2ecf20Sopenharmony_ci}
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_cistatic int kvp_file_init(void)
2338c2ecf20Sopenharmony_ci{
2348c2ecf20Sopenharmony_ci	int  fd;
2358c2ecf20Sopenharmony_ci	char *fname;
2368c2ecf20Sopenharmony_ci	int i;
2378c2ecf20Sopenharmony_ci	int alloc_unit = sizeof(struct kvp_record) * ENTRIES_PER_BLOCK;
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	if (access(KVP_CONFIG_LOC, F_OK)) {
2408c2ecf20Sopenharmony_ci		if (mkdir(KVP_CONFIG_LOC, 0755 /* rwxr-xr-x */)) {
2418c2ecf20Sopenharmony_ci			syslog(LOG_ERR, "Failed to create '%s'; error: %d %s", KVP_CONFIG_LOC,
2428c2ecf20Sopenharmony_ci					errno, strerror(errno));
2438c2ecf20Sopenharmony_ci			exit(EXIT_FAILURE);
2448c2ecf20Sopenharmony_ci		}
2458c2ecf20Sopenharmony_ci	}
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	for (i = 0; i < KVP_POOL_COUNT; i++) {
2488c2ecf20Sopenharmony_ci		fname = kvp_file_info[i].fname;
2498c2ecf20Sopenharmony_ci		sprintf(fname, "%s/.kvp_pool_%d", KVP_CONFIG_LOC, i);
2508c2ecf20Sopenharmony_ci		fd = open(fname, O_RDWR | O_CREAT | O_CLOEXEC, 0644 /* rw-r--r-- */);
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci		if (fd == -1)
2538c2ecf20Sopenharmony_ci			return 1;
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci		kvp_file_info[i].fd = fd;
2568c2ecf20Sopenharmony_ci		kvp_file_info[i].num_blocks = 1;
2578c2ecf20Sopenharmony_ci		kvp_file_info[i].records = malloc(alloc_unit);
2588c2ecf20Sopenharmony_ci		if (kvp_file_info[i].records == NULL)
2598c2ecf20Sopenharmony_ci			return 1;
2608c2ecf20Sopenharmony_ci		kvp_file_info[i].num_records = 0;
2618c2ecf20Sopenharmony_ci		kvp_update_mem_state(i);
2628c2ecf20Sopenharmony_ci	}
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	return 0;
2658c2ecf20Sopenharmony_ci}
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_cistatic int kvp_key_delete(int pool, const __u8 *key, int key_size)
2688c2ecf20Sopenharmony_ci{
2698c2ecf20Sopenharmony_ci	int i;
2708c2ecf20Sopenharmony_ci	int j, k;
2718c2ecf20Sopenharmony_ci	int num_records;
2728c2ecf20Sopenharmony_ci	struct kvp_record *record;
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	/*
2758c2ecf20Sopenharmony_ci	 * First update the in-memory state.
2768c2ecf20Sopenharmony_ci	 */
2778c2ecf20Sopenharmony_ci	kvp_update_mem_state(pool);
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	num_records = kvp_file_info[pool].num_records;
2808c2ecf20Sopenharmony_ci	record = kvp_file_info[pool].records;
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	for (i = 0; i < num_records; i++) {
2838c2ecf20Sopenharmony_ci		if (memcmp(key, record[i].key, key_size))
2848c2ecf20Sopenharmony_ci			continue;
2858c2ecf20Sopenharmony_ci		/*
2868c2ecf20Sopenharmony_ci		 * Found a match; just move the remaining
2878c2ecf20Sopenharmony_ci		 * entries up.
2888c2ecf20Sopenharmony_ci		 */
2898c2ecf20Sopenharmony_ci		if (i == (num_records - 1)) {
2908c2ecf20Sopenharmony_ci			kvp_file_info[pool].num_records--;
2918c2ecf20Sopenharmony_ci			kvp_update_file(pool);
2928c2ecf20Sopenharmony_ci			return 0;
2938c2ecf20Sopenharmony_ci		}
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci		j = i;
2968c2ecf20Sopenharmony_ci		k = j + 1;
2978c2ecf20Sopenharmony_ci		for (; k < num_records; k++) {
2988c2ecf20Sopenharmony_ci			strcpy(record[j].key, record[k].key);
2998c2ecf20Sopenharmony_ci			strcpy(record[j].value, record[k].value);
3008c2ecf20Sopenharmony_ci			j++;
3018c2ecf20Sopenharmony_ci		}
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci		kvp_file_info[pool].num_records--;
3048c2ecf20Sopenharmony_ci		kvp_update_file(pool);
3058c2ecf20Sopenharmony_ci		return 0;
3068c2ecf20Sopenharmony_ci	}
3078c2ecf20Sopenharmony_ci	return 1;
3088c2ecf20Sopenharmony_ci}
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_cistatic int kvp_key_add_or_modify(int pool, const __u8 *key, int key_size,
3118c2ecf20Sopenharmony_ci				 const __u8 *value, int value_size)
3128c2ecf20Sopenharmony_ci{
3138c2ecf20Sopenharmony_ci	int i;
3148c2ecf20Sopenharmony_ci	int num_records;
3158c2ecf20Sopenharmony_ci	struct kvp_record *record;
3168c2ecf20Sopenharmony_ci	int num_blocks;
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	if ((key_size > HV_KVP_EXCHANGE_MAX_KEY_SIZE) ||
3198c2ecf20Sopenharmony_ci		(value_size > HV_KVP_EXCHANGE_MAX_VALUE_SIZE))
3208c2ecf20Sopenharmony_ci		return 1;
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	/*
3238c2ecf20Sopenharmony_ci	 * First update the in-memory state.
3248c2ecf20Sopenharmony_ci	 */
3258c2ecf20Sopenharmony_ci	kvp_update_mem_state(pool);
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	num_records = kvp_file_info[pool].num_records;
3288c2ecf20Sopenharmony_ci	record = kvp_file_info[pool].records;
3298c2ecf20Sopenharmony_ci	num_blocks = kvp_file_info[pool].num_blocks;
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	for (i = 0; i < num_records; i++) {
3328c2ecf20Sopenharmony_ci		if (memcmp(key, record[i].key, key_size))
3338c2ecf20Sopenharmony_ci			continue;
3348c2ecf20Sopenharmony_ci		/*
3358c2ecf20Sopenharmony_ci		 * Found a match; just update the value -
3368c2ecf20Sopenharmony_ci		 * this is the modify case.
3378c2ecf20Sopenharmony_ci		 */
3388c2ecf20Sopenharmony_ci		memcpy(record[i].value, value, value_size);
3398c2ecf20Sopenharmony_ci		kvp_update_file(pool);
3408c2ecf20Sopenharmony_ci		return 0;
3418c2ecf20Sopenharmony_ci	}
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	/*
3448c2ecf20Sopenharmony_ci	 * Need to add a new entry;
3458c2ecf20Sopenharmony_ci	 */
3468c2ecf20Sopenharmony_ci	if (num_records == (ENTRIES_PER_BLOCK * num_blocks)) {
3478c2ecf20Sopenharmony_ci		/* Need to allocate a larger array for reg entries. */
3488c2ecf20Sopenharmony_ci		record = realloc(record, sizeof(struct kvp_record) *
3498c2ecf20Sopenharmony_ci			 ENTRIES_PER_BLOCK * (num_blocks + 1));
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci		if (record == NULL)
3528c2ecf20Sopenharmony_ci			return 1;
3538c2ecf20Sopenharmony_ci		kvp_file_info[pool].num_blocks++;
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	}
3568c2ecf20Sopenharmony_ci	memcpy(record[i].value, value, value_size);
3578c2ecf20Sopenharmony_ci	memcpy(record[i].key, key, key_size);
3588c2ecf20Sopenharmony_ci	kvp_file_info[pool].records = record;
3598c2ecf20Sopenharmony_ci	kvp_file_info[pool].num_records++;
3608c2ecf20Sopenharmony_ci	kvp_update_file(pool);
3618c2ecf20Sopenharmony_ci	return 0;
3628c2ecf20Sopenharmony_ci}
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_cistatic int kvp_get_value(int pool, const __u8 *key, int key_size, __u8 *value,
3658c2ecf20Sopenharmony_ci			int value_size)
3668c2ecf20Sopenharmony_ci{
3678c2ecf20Sopenharmony_ci	int i;
3688c2ecf20Sopenharmony_ci	int num_records;
3698c2ecf20Sopenharmony_ci	struct kvp_record *record;
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	if ((key_size > HV_KVP_EXCHANGE_MAX_KEY_SIZE) ||
3728c2ecf20Sopenharmony_ci		(value_size > HV_KVP_EXCHANGE_MAX_VALUE_SIZE))
3738c2ecf20Sopenharmony_ci		return 1;
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	/*
3768c2ecf20Sopenharmony_ci	 * First update the in-memory state.
3778c2ecf20Sopenharmony_ci	 */
3788c2ecf20Sopenharmony_ci	kvp_update_mem_state(pool);
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	num_records = kvp_file_info[pool].num_records;
3818c2ecf20Sopenharmony_ci	record = kvp_file_info[pool].records;
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	for (i = 0; i < num_records; i++) {
3848c2ecf20Sopenharmony_ci		if (memcmp(key, record[i].key, key_size))
3858c2ecf20Sopenharmony_ci			continue;
3868c2ecf20Sopenharmony_ci		/*
3878c2ecf20Sopenharmony_ci		 * Found a match; just copy the value out.
3888c2ecf20Sopenharmony_ci		 */
3898c2ecf20Sopenharmony_ci		memcpy(value, record[i].value, value_size);
3908c2ecf20Sopenharmony_ci		return 0;
3918c2ecf20Sopenharmony_ci	}
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	return 1;
3948c2ecf20Sopenharmony_ci}
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_cistatic int kvp_pool_enumerate(int pool, int index, __u8 *key, int key_size,
3978c2ecf20Sopenharmony_ci				__u8 *value, int value_size)
3988c2ecf20Sopenharmony_ci{
3998c2ecf20Sopenharmony_ci	struct kvp_record *record;
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	/*
4028c2ecf20Sopenharmony_ci	 * First update our in-memory database.
4038c2ecf20Sopenharmony_ci	 */
4048c2ecf20Sopenharmony_ci	kvp_update_mem_state(pool);
4058c2ecf20Sopenharmony_ci	record = kvp_file_info[pool].records;
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci	if (index >= kvp_file_info[pool].num_records) {
4088c2ecf20Sopenharmony_ci		return 1;
4098c2ecf20Sopenharmony_ci	}
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	memcpy(key, record[index].key, key_size);
4128c2ecf20Sopenharmony_ci	memcpy(value, record[index].value, value_size);
4138c2ecf20Sopenharmony_ci	return 0;
4148c2ecf20Sopenharmony_ci}
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_civoid kvp_get_os_info(void)
4188c2ecf20Sopenharmony_ci{
4198c2ecf20Sopenharmony_ci	FILE	*file;
4208c2ecf20Sopenharmony_ci	char	*p, buf[512];
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	uname(&uts_buf);
4238c2ecf20Sopenharmony_ci	os_version = uts_buf.release;
4248c2ecf20Sopenharmony_ci	os_build = strdup(uts_buf.release);
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci	os_name = uts_buf.sysname;
4278c2ecf20Sopenharmony_ci	processor_arch = uts_buf.machine;
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	/*
4308c2ecf20Sopenharmony_ci	 * The current windows host (win7) expects the build
4318c2ecf20Sopenharmony_ci	 * string to be of the form: x.y.z
4328c2ecf20Sopenharmony_ci	 * Strip additional information we may have.
4338c2ecf20Sopenharmony_ci	 */
4348c2ecf20Sopenharmony_ci	p = strchr(os_version, '-');
4358c2ecf20Sopenharmony_ci	if (p)
4368c2ecf20Sopenharmony_ci		*p = '\0';
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	/*
4398c2ecf20Sopenharmony_ci	 * Parse the /etc/os-release file if present:
4408c2ecf20Sopenharmony_ci	 * https://www.freedesktop.org/software/systemd/man/os-release.html
4418c2ecf20Sopenharmony_ci	 */
4428c2ecf20Sopenharmony_ci	file = fopen("/etc/os-release", "r");
4438c2ecf20Sopenharmony_ci	if (file != NULL) {
4448c2ecf20Sopenharmony_ci		while (fgets(buf, sizeof(buf), file)) {
4458c2ecf20Sopenharmony_ci			char *value, *q;
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci			/* Ignore comments */
4488c2ecf20Sopenharmony_ci			if (buf[0] == '#')
4498c2ecf20Sopenharmony_ci				continue;
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci			/* Split into name=value */
4528c2ecf20Sopenharmony_ci			p = strchr(buf, '=');
4538c2ecf20Sopenharmony_ci			if (!p)
4548c2ecf20Sopenharmony_ci				continue;
4558c2ecf20Sopenharmony_ci			*p++ = 0;
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci			/* Remove quotes and newline; un-escape */
4588c2ecf20Sopenharmony_ci			value = p;
4598c2ecf20Sopenharmony_ci			q = p;
4608c2ecf20Sopenharmony_ci			while (*p) {
4618c2ecf20Sopenharmony_ci				if (*p == '\\') {
4628c2ecf20Sopenharmony_ci					++p;
4638c2ecf20Sopenharmony_ci					if (!*p)
4648c2ecf20Sopenharmony_ci						break;
4658c2ecf20Sopenharmony_ci					*q++ = *p++;
4668c2ecf20Sopenharmony_ci				} else if (*p == '\'' || *p == '"' ||
4678c2ecf20Sopenharmony_ci					   *p == '\n') {
4688c2ecf20Sopenharmony_ci					++p;
4698c2ecf20Sopenharmony_ci				} else {
4708c2ecf20Sopenharmony_ci					*q++ = *p++;
4718c2ecf20Sopenharmony_ci				}
4728c2ecf20Sopenharmony_ci			}
4738c2ecf20Sopenharmony_ci			*q = 0;
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci			if (!strcmp(buf, "NAME")) {
4768c2ecf20Sopenharmony_ci				p = strdup(value);
4778c2ecf20Sopenharmony_ci				if (!p)
4788c2ecf20Sopenharmony_ci					break;
4798c2ecf20Sopenharmony_ci				os_name = p;
4808c2ecf20Sopenharmony_ci			} else if (!strcmp(buf, "VERSION_ID")) {
4818c2ecf20Sopenharmony_ci				p = strdup(value);
4828c2ecf20Sopenharmony_ci				if (!p)
4838c2ecf20Sopenharmony_ci					break;
4848c2ecf20Sopenharmony_ci				os_major = p;
4858c2ecf20Sopenharmony_ci			}
4868c2ecf20Sopenharmony_ci		}
4878c2ecf20Sopenharmony_ci		fclose(file);
4888c2ecf20Sopenharmony_ci		return;
4898c2ecf20Sopenharmony_ci	}
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	/* Fallback for older RH/SUSE releases */
4928c2ecf20Sopenharmony_ci	file = fopen("/etc/SuSE-release", "r");
4938c2ecf20Sopenharmony_ci	if (file != NULL)
4948c2ecf20Sopenharmony_ci		goto kvp_osinfo_found;
4958c2ecf20Sopenharmony_ci	file  = fopen("/etc/redhat-release", "r");
4968c2ecf20Sopenharmony_ci	if (file != NULL)
4978c2ecf20Sopenharmony_ci		goto kvp_osinfo_found;
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	/*
5008c2ecf20Sopenharmony_ci	 * We don't have information about the os.
5018c2ecf20Sopenharmony_ci	 */
5028c2ecf20Sopenharmony_ci	return;
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_cikvp_osinfo_found:
5058c2ecf20Sopenharmony_ci	/* up to three lines */
5068c2ecf20Sopenharmony_ci	p = fgets(buf, sizeof(buf), file);
5078c2ecf20Sopenharmony_ci	if (p) {
5088c2ecf20Sopenharmony_ci		p = strchr(buf, '\n');
5098c2ecf20Sopenharmony_ci		if (p)
5108c2ecf20Sopenharmony_ci			*p = '\0';
5118c2ecf20Sopenharmony_ci		p = strdup(buf);
5128c2ecf20Sopenharmony_ci		if (!p)
5138c2ecf20Sopenharmony_ci			goto done;
5148c2ecf20Sopenharmony_ci		os_name = p;
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci		/* second line */
5178c2ecf20Sopenharmony_ci		p = fgets(buf, sizeof(buf), file);
5188c2ecf20Sopenharmony_ci		if (p) {
5198c2ecf20Sopenharmony_ci			p = strchr(buf, '\n');
5208c2ecf20Sopenharmony_ci			if (p)
5218c2ecf20Sopenharmony_ci				*p = '\0';
5228c2ecf20Sopenharmony_ci			p = strdup(buf);
5238c2ecf20Sopenharmony_ci			if (!p)
5248c2ecf20Sopenharmony_ci				goto done;
5258c2ecf20Sopenharmony_ci			os_major = p;
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ci			/* third line */
5288c2ecf20Sopenharmony_ci			p = fgets(buf, sizeof(buf), file);
5298c2ecf20Sopenharmony_ci			if (p)  {
5308c2ecf20Sopenharmony_ci				p = strchr(buf, '\n');
5318c2ecf20Sopenharmony_ci				if (p)
5328c2ecf20Sopenharmony_ci					*p = '\0';
5338c2ecf20Sopenharmony_ci				p = strdup(buf);
5348c2ecf20Sopenharmony_ci				if (p)
5358c2ecf20Sopenharmony_ci					os_minor = p;
5368c2ecf20Sopenharmony_ci			}
5378c2ecf20Sopenharmony_ci		}
5388c2ecf20Sopenharmony_ci	}
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_cidone:
5418c2ecf20Sopenharmony_ci	fclose(file);
5428c2ecf20Sopenharmony_ci	return;
5438c2ecf20Sopenharmony_ci}
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci/*
5488c2ecf20Sopenharmony_ci * Retrieve an interface name corresponding to the specified guid.
5498c2ecf20Sopenharmony_ci * If there is a match, the function returns a pointer
5508c2ecf20Sopenharmony_ci * to the interface name and if not, a NULL is returned.
5518c2ecf20Sopenharmony_ci * If a match is found, the caller is responsible for
5528c2ecf20Sopenharmony_ci * freeing the memory.
5538c2ecf20Sopenharmony_ci */
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_cistatic char *kvp_get_if_name(char *guid)
5568c2ecf20Sopenharmony_ci{
5578c2ecf20Sopenharmony_ci	DIR *dir;
5588c2ecf20Sopenharmony_ci	struct dirent *entry;
5598c2ecf20Sopenharmony_ci	FILE    *file;
5608c2ecf20Sopenharmony_ci	char    *p, *x;
5618c2ecf20Sopenharmony_ci	char    *if_name = NULL;
5628c2ecf20Sopenharmony_ci	char    buf[256];
5638c2ecf20Sopenharmony_ci	char dev_id[PATH_MAX];
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci	dir = opendir(KVP_NET_DIR);
5668c2ecf20Sopenharmony_ci	if (dir == NULL)
5678c2ecf20Sopenharmony_ci		return NULL;
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci	while ((entry = readdir(dir)) != NULL) {
5708c2ecf20Sopenharmony_ci		/*
5718c2ecf20Sopenharmony_ci		 * Set the state for the next pass.
5728c2ecf20Sopenharmony_ci		 */
5738c2ecf20Sopenharmony_ci		snprintf(dev_id, sizeof(dev_id), "%s%s/device/device_id",
5748c2ecf20Sopenharmony_ci			 KVP_NET_DIR, entry->d_name);
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci		file = fopen(dev_id, "r");
5778c2ecf20Sopenharmony_ci		if (file == NULL)
5788c2ecf20Sopenharmony_ci			continue;
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci		p = fgets(buf, sizeof(buf), file);
5818c2ecf20Sopenharmony_ci		if (p) {
5828c2ecf20Sopenharmony_ci			x = strchr(p, '\n');
5838c2ecf20Sopenharmony_ci			if (x)
5848c2ecf20Sopenharmony_ci				*x = '\0';
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci			if (!strcmp(p, guid)) {
5878c2ecf20Sopenharmony_ci				/*
5888c2ecf20Sopenharmony_ci				 * Found the guid match; return the interface
5898c2ecf20Sopenharmony_ci				 * name. The caller will free the memory.
5908c2ecf20Sopenharmony_ci				 */
5918c2ecf20Sopenharmony_ci				if_name = strdup(entry->d_name);
5928c2ecf20Sopenharmony_ci				fclose(file);
5938c2ecf20Sopenharmony_ci				break;
5948c2ecf20Sopenharmony_ci			}
5958c2ecf20Sopenharmony_ci		}
5968c2ecf20Sopenharmony_ci		fclose(file);
5978c2ecf20Sopenharmony_ci	}
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci	closedir(dir);
6008c2ecf20Sopenharmony_ci	return if_name;
6018c2ecf20Sopenharmony_ci}
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_ci/*
6048c2ecf20Sopenharmony_ci * Retrieve the MAC address given the interface name.
6058c2ecf20Sopenharmony_ci */
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_cistatic char *kvp_if_name_to_mac(char *if_name)
6088c2ecf20Sopenharmony_ci{
6098c2ecf20Sopenharmony_ci	FILE    *file;
6108c2ecf20Sopenharmony_ci	char    *p, *x;
6118c2ecf20Sopenharmony_ci	char    buf[256];
6128c2ecf20Sopenharmony_ci	char addr_file[PATH_MAX];
6138c2ecf20Sopenharmony_ci	unsigned int i;
6148c2ecf20Sopenharmony_ci	char *mac_addr = NULL;
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_ci	snprintf(addr_file, sizeof(addr_file), "%s%s%s", KVP_NET_DIR,
6178c2ecf20Sopenharmony_ci		 if_name, "/address");
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci	file = fopen(addr_file, "r");
6208c2ecf20Sopenharmony_ci	if (file == NULL)
6218c2ecf20Sopenharmony_ci		return NULL;
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_ci	p = fgets(buf, sizeof(buf), file);
6248c2ecf20Sopenharmony_ci	if (p) {
6258c2ecf20Sopenharmony_ci		x = strchr(p, '\n');
6268c2ecf20Sopenharmony_ci		if (x)
6278c2ecf20Sopenharmony_ci			*x = '\0';
6288c2ecf20Sopenharmony_ci		for (i = 0; i < strlen(p); i++)
6298c2ecf20Sopenharmony_ci			p[i] = toupper(p[i]);
6308c2ecf20Sopenharmony_ci		mac_addr = strdup(p);
6318c2ecf20Sopenharmony_ci	}
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci	fclose(file);
6348c2ecf20Sopenharmony_ci	return mac_addr;
6358c2ecf20Sopenharmony_ci}
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_cistatic void kvp_process_ipconfig_file(char *cmd,
6388c2ecf20Sopenharmony_ci					char *config_buf, unsigned int len,
6398c2ecf20Sopenharmony_ci					int element_size, int offset)
6408c2ecf20Sopenharmony_ci{
6418c2ecf20Sopenharmony_ci	char buf[256];
6428c2ecf20Sopenharmony_ci	char *p;
6438c2ecf20Sopenharmony_ci	char *x;
6448c2ecf20Sopenharmony_ci	FILE *file;
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_ci	/*
6478c2ecf20Sopenharmony_ci	 * First execute the command.
6488c2ecf20Sopenharmony_ci	 */
6498c2ecf20Sopenharmony_ci	file = popen(cmd, "r");
6508c2ecf20Sopenharmony_ci	if (file == NULL)
6518c2ecf20Sopenharmony_ci		return;
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_ci	if (offset == 0)
6548c2ecf20Sopenharmony_ci		memset(config_buf, 0, len);
6558c2ecf20Sopenharmony_ci	while ((p = fgets(buf, sizeof(buf), file)) != NULL) {
6568c2ecf20Sopenharmony_ci		if (len < strlen(config_buf) + element_size + 1)
6578c2ecf20Sopenharmony_ci			break;
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_ci		x = strchr(p, '\n');
6608c2ecf20Sopenharmony_ci		if (x)
6618c2ecf20Sopenharmony_ci			*x = '\0';
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_ci		strcat(config_buf, p);
6648c2ecf20Sopenharmony_ci		strcat(config_buf, ";");
6658c2ecf20Sopenharmony_ci	}
6668c2ecf20Sopenharmony_ci	pclose(file);
6678c2ecf20Sopenharmony_ci}
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_cistatic void kvp_get_ipconfig_info(char *if_name,
6708c2ecf20Sopenharmony_ci				 struct hv_kvp_ipaddr_value *buffer)
6718c2ecf20Sopenharmony_ci{
6728c2ecf20Sopenharmony_ci	char cmd[512];
6738c2ecf20Sopenharmony_ci	char dhcp_info[128];
6748c2ecf20Sopenharmony_ci	char *p;
6758c2ecf20Sopenharmony_ci	FILE *file;
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_ci	/*
6788c2ecf20Sopenharmony_ci	 * Get the address of default gateway (ipv4).
6798c2ecf20Sopenharmony_ci	 */
6808c2ecf20Sopenharmony_ci	sprintf(cmd, "%s %s", "ip route show dev", if_name);
6818c2ecf20Sopenharmony_ci	strcat(cmd, " | awk '/default/ {print $3 }'");
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_ci	/*
6848c2ecf20Sopenharmony_ci	 * Execute the command to gather gateway info.
6858c2ecf20Sopenharmony_ci	 */
6868c2ecf20Sopenharmony_ci	kvp_process_ipconfig_file(cmd, (char *)buffer->gate_way,
6878c2ecf20Sopenharmony_ci				(MAX_GATEWAY_SIZE * 2), INET_ADDRSTRLEN, 0);
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_ci	/*
6908c2ecf20Sopenharmony_ci	 * Get the address of default gateway (ipv6).
6918c2ecf20Sopenharmony_ci	 */
6928c2ecf20Sopenharmony_ci	sprintf(cmd, "%s %s", "ip -f inet6  route show dev", if_name);
6938c2ecf20Sopenharmony_ci	strcat(cmd, " | awk '/default/ {print $3 }'");
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_ci	/*
6968c2ecf20Sopenharmony_ci	 * Execute the command to gather gateway info (ipv6).
6978c2ecf20Sopenharmony_ci	 */
6988c2ecf20Sopenharmony_ci	kvp_process_ipconfig_file(cmd, (char *)buffer->gate_way,
6998c2ecf20Sopenharmony_ci				(MAX_GATEWAY_SIZE * 2), INET6_ADDRSTRLEN, 1);
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_ci	/*
7038c2ecf20Sopenharmony_ci	 * Gather the DNS state.
7048c2ecf20Sopenharmony_ci	 * Since there is no standard way to get this information
7058c2ecf20Sopenharmony_ci	 * across various distributions of interest; we just invoke
7068c2ecf20Sopenharmony_ci	 * an external script that needs to be ported across distros
7078c2ecf20Sopenharmony_ci	 * of interest.
7088c2ecf20Sopenharmony_ci	 *
7098c2ecf20Sopenharmony_ci	 * Following is the expected format of the information from the script:
7108c2ecf20Sopenharmony_ci	 *
7118c2ecf20Sopenharmony_ci	 * ipaddr1 (nameserver1)
7128c2ecf20Sopenharmony_ci	 * ipaddr2 (nameserver2)
7138c2ecf20Sopenharmony_ci	 * .
7148c2ecf20Sopenharmony_ci	 * .
7158c2ecf20Sopenharmony_ci	 */
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_ci	sprintf(cmd, KVP_SCRIPTS_PATH "%s",  "hv_get_dns_info");
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_ci	/*
7208c2ecf20Sopenharmony_ci	 * Execute the command to gather DNS info.
7218c2ecf20Sopenharmony_ci	 */
7228c2ecf20Sopenharmony_ci	kvp_process_ipconfig_file(cmd, (char *)buffer->dns_addr,
7238c2ecf20Sopenharmony_ci				(MAX_IP_ADDR_SIZE * 2), INET_ADDRSTRLEN, 0);
7248c2ecf20Sopenharmony_ci
7258c2ecf20Sopenharmony_ci	/*
7268c2ecf20Sopenharmony_ci	 * Gather the DHCP state.
7278c2ecf20Sopenharmony_ci	 * We will gather this state by invoking an external script.
7288c2ecf20Sopenharmony_ci	 * The parameter to the script is the interface name.
7298c2ecf20Sopenharmony_ci	 * Here is the expected output:
7308c2ecf20Sopenharmony_ci	 *
7318c2ecf20Sopenharmony_ci	 * Enabled: DHCP enabled.
7328c2ecf20Sopenharmony_ci	 */
7338c2ecf20Sopenharmony_ci
7348c2ecf20Sopenharmony_ci	sprintf(cmd, KVP_SCRIPTS_PATH "%s %s", "hv_get_dhcp_info", if_name);
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_ci	file = popen(cmd, "r");
7378c2ecf20Sopenharmony_ci	if (file == NULL)
7388c2ecf20Sopenharmony_ci		return;
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_ci	p = fgets(dhcp_info, sizeof(dhcp_info), file);
7418c2ecf20Sopenharmony_ci	if (p == NULL) {
7428c2ecf20Sopenharmony_ci		pclose(file);
7438c2ecf20Sopenharmony_ci		return;
7448c2ecf20Sopenharmony_ci	}
7458c2ecf20Sopenharmony_ci
7468c2ecf20Sopenharmony_ci	if (!strncmp(p, "Enabled", 7))
7478c2ecf20Sopenharmony_ci		buffer->dhcp_enabled = 1;
7488c2ecf20Sopenharmony_ci	else
7498c2ecf20Sopenharmony_ci		buffer->dhcp_enabled = 0;
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_ci	pclose(file);
7528c2ecf20Sopenharmony_ci}
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_cistatic unsigned int hweight32(unsigned int *w)
7568c2ecf20Sopenharmony_ci{
7578c2ecf20Sopenharmony_ci	unsigned int res = *w - ((*w >> 1) & 0x55555555);
7588c2ecf20Sopenharmony_ci	res = (res & 0x33333333) + ((res >> 2) & 0x33333333);
7598c2ecf20Sopenharmony_ci	res = (res + (res >> 4)) & 0x0F0F0F0F;
7608c2ecf20Sopenharmony_ci	res = res + (res >> 8);
7618c2ecf20Sopenharmony_ci	return (res + (res >> 16)) & 0x000000FF;
7628c2ecf20Sopenharmony_ci}
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_cistatic int kvp_process_ip_address(void *addrp,
7658c2ecf20Sopenharmony_ci				int family, char *buffer,
7668c2ecf20Sopenharmony_ci				int length,  int *offset)
7678c2ecf20Sopenharmony_ci{
7688c2ecf20Sopenharmony_ci	struct sockaddr_in *addr;
7698c2ecf20Sopenharmony_ci	struct sockaddr_in6 *addr6;
7708c2ecf20Sopenharmony_ci	int addr_length;
7718c2ecf20Sopenharmony_ci	char tmp[50];
7728c2ecf20Sopenharmony_ci	const char *str;
7738c2ecf20Sopenharmony_ci
7748c2ecf20Sopenharmony_ci	if (family == AF_INET) {
7758c2ecf20Sopenharmony_ci		addr = (struct sockaddr_in *)addrp;
7768c2ecf20Sopenharmony_ci		str = inet_ntop(family, &addr->sin_addr, tmp, 50);
7778c2ecf20Sopenharmony_ci		addr_length = INET_ADDRSTRLEN;
7788c2ecf20Sopenharmony_ci	} else {
7798c2ecf20Sopenharmony_ci		addr6 = (struct sockaddr_in6 *)addrp;
7808c2ecf20Sopenharmony_ci		str = inet_ntop(family, &addr6->sin6_addr.s6_addr, tmp, 50);
7818c2ecf20Sopenharmony_ci		addr_length = INET6_ADDRSTRLEN;
7828c2ecf20Sopenharmony_ci	}
7838c2ecf20Sopenharmony_ci
7848c2ecf20Sopenharmony_ci	if ((length - *offset) < addr_length + 2)
7858c2ecf20Sopenharmony_ci		return HV_E_FAIL;
7868c2ecf20Sopenharmony_ci	if (str == NULL) {
7878c2ecf20Sopenharmony_ci		strcpy(buffer, "inet_ntop failed\n");
7888c2ecf20Sopenharmony_ci		return HV_E_FAIL;
7898c2ecf20Sopenharmony_ci	}
7908c2ecf20Sopenharmony_ci	if (*offset == 0)
7918c2ecf20Sopenharmony_ci		strcpy(buffer, tmp);
7928c2ecf20Sopenharmony_ci	else {
7938c2ecf20Sopenharmony_ci		strcat(buffer, ";");
7948c2ecf20Sopenharmony_ci		strcat(buffer, tmp);
7958c2ecf20Sopenharmony_ci	}
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_ci	*offset += strlen(str) + 1;
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_ci	return 0;
8008c2ecf20Sopenharmony_ci}
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_cistatic int
8038c2ecf20Sopenharmony_cikvp_get_ip_info(int family, char *if_name, int op,
8048c2ecf20Sopenharmony_ci		 void  *out_buffer, unsigned int length)
8058c2ecf20Sopenharmony_ci{
8068c2ecf20Sopenharmony_ci	struct ifaddrs *ifap;
8078c2ecf20Sopenharmony_ci	struct ifaddrs *curp;
8088c2ecf20Sopenharmony_ci	int offset = 0;
8098c2ecf20Sopenharmony_ci	int sn_offset = 0;
8108c2ecf20Sopenharmony_ci	int error = 0;
8118c2ecf20Sopenharmony_ci	char *buffer;
8128c2ecf20Sopenharmony_ci	struct hv_kvp_ipaddr_value *ip_buffer = NULL;
8138c2ecf20Sopenharmony_ci	char cidr_mask[5]; /* /xyz */
8148c2ecf20Sopenharmony_ci	int weight;
8158c2ecf20Sopenharmony_ci	int i;
8168c2ecf20Sopenharmony_ci	unsigned int *w;
8178c2ecf20Sopenharmony_ci	char *sn_str;
8188c2ecf20Sopenharmony_ci	struct sockaddr_in6 *addr6;
8198c2ecf20Sopenharmony_ci
8208c2ecf20Sopenharmony_ci	if (op == KVP_OP_ENUMERATE) {
8218c2ecf20Sopenharmony_ci		buffer = out_buffer;
8228c2ecf20Sopenharmony_ci	} else {
8238c2ecf20Sopenharmony_ci		ip_buffer = out_buffer;
8248c2ecf20Sopenharmony_ci		buffer = (char *)ip_buffer->ip_addr;
8258c2ecf20Sopenharmony_ci		ip_buffer->addr_family = 0;
8268c2ecf20Sopenharmony_ci	}
8278c2ecf20Sopenharmony_ci	/*
8288c2ecf20Sopenharmony_ci	 * On entry into this function, the buffer is capable of holding the
8298c2ecf20Sopenharmony_ci	 * maximum key value.
8308c2ecf20Sopenharmony_ci	 */
8318c2ecf20Sopenharmony_ci
8328c2ecf20Sopenharmony_ci	if (getifaddrs(&ifap)) {
8338c2ecf20Sopenharmony_ci		strcpy(buffer, "getifaddrs failed\n");
8348c2ecf20Sopenharmony_ci		return HV_E_FAIL;
8358c2ecf20Sopenharmony_ci	}
8368c2ecf20Sopenharmony_ci
8378c2ecf20Sopenharmony_ci	curp = ifap;
8388c2ecf20Sopenharmony_ci	while (curp != NULL) {
8398c2ecf20Sopenharmony_ci		if (curp->ifa_addr == NULL) {
8408c2ecf20Sopenharmony_ci			curp = curp->ifa_next;
8418c2ecf20Sopenharmony_ci			continue;
8428c2ecf20Sopenharmony_ci		}
8438c2ecf20Sopenharmony_ci
8448c2ecf20Sopenharmony_ci		if ((if_name != NULL) &&
8458c2ecf20Sopenharmony_ci			(strncmp(curp->ifa_name, if_name, strlen(if_name)))) {
8468c2ecf20Sopenharmony_ci			/*
8478c2ecf20Sopenharmony_ci			 * We want info about a specific interface;
8488c2ecf20Sopenharmony_ci			 * just continue.
8498c2ecf20Sopenharmony_ci			 */
8508c2ecf20Sopenharmony_ci			curp = curp->ifa_next;
8518c2ecf20Sopenharmony_ci			continue;
8528c2ecf20Sopenharmony_ci		}
8538c2ecf20Sopenharmony_ci
8548c2ecf20Sopenharmony_ci		/*
8558c2ecf20Sopenharmony_ci		 * We only support two address families: AF_INET and AF_INET6.
8568c2ecf20Sopenharmony_ci		 * If a family value of 0 is specified, we collect both
8578c2ecf20Sopenharmony_ci		 * supported address families; if not we gather info on
8588c2ecf20Sopenharmony_ci		 * the specified address family.
8598c2ecf20Sopenharmony_ci		 */
8608c2ecf20Sopenharmony_ci		if ((((family != 0) &&
8618c2ecf20Sopenharmony_ci			 (curp->ifa_addr->sa_family != family))) ||
8628c2ecf20Sopenharmony_ci			 (curp->ifa_flags & IFF_LOOPBACK)) {
8638c2ecf20Sopenharmony_ci			curp = curp->ifa_next;
8648c2ecf20Sopenharmony_ci			continue;
8658c2ecf20Sopenharmony_ci		}
8668c2ecf20Sopenharmony_ci		if ((curp->ifa_addr->sa_family != AF_INET) &&
8678c2ecf20Sopenharmony_ci			(curp->ifa_addr->sa_family != AF_INET6)) {
8688c2ecf20Sopenharmony_ci			curp = curp->ifa_next;
8698c2ecf20Sopenharmony_ci			continue;
8708c2ecf20Sopenharmony_ci		}
8718c2ecf20Sopenharmony_ci
8728c2ecf20Sopenharmony_ci		if (op == KVP_OP_GET_IP_INFO) {
8738c2ecf20Sopenharmony_ci			/*
8748c2ecf20Sopenharmony_ci			 * Gather info other than the IP address.
8758c2ecf20Sopenharmony_ci			 * IP address info will be gathered later.
8768c2ecf20Sopenharmony_ci			 */
8778c2ecf20Sopenharmony_ci			if (curp->ifa_addr->sa_family == AF_INET) {
8788c2ecf20Sopenharmony_ci				ip_buffer->addr_family |= ADDR_FAMILY_IPV4;
8798c2ecf20Sopenharmony_ci				/*
8808c2ecf20Sopenharmony_ci				 * Get subnet info.
8818c2ecf20Sopenharmony_ci				 */
8828c2ecf20Sopenharmony_ci				error = kvp_process_ip_address(
8838c2ecf20Sopenharmony_ci							     curp->ifa_netmask,
8848c2ecf20Sopenharmony_ci							     AF_INET,
8858c2ecf20Sopenharmony_ci							     (char *)
8868c2ecf20Sopenharmony_ci							     ip_buffer->sub_net,
8878c2ecf20Sopenharmony_ci							     length,
8888c2ecf20Sopenharmony_ci							     &sn_offset);
8898c2ecf20Sopenharmony_ci				if (error)
8908c2ecf20Sopenharmony_ci					goto gather_ipaddr;
8918c2ecf20Sopenharmony_ci			} else {
8928c2ecf20Sopenharmony_ci				ip_buffer->addr_family |= ADDR_FAMILY_IPV6;
8938c2ecf20Sopenharmony_ci
8948c2ecf20Sopenharmony_ci				/*
8958c2ecf20Sopenharmony_ci				 * Get subnet info in CIDR format.
8968c2ecf20Sopenharmony_ci				 */
8978c2ecf20Sopenharmony_ci				weight = 0;
8988c2ecf20Sopenharmony_ci				sn_str = (char *)ip_buffer->sub_net;
8998c2ecf20Sopenharmony_ci				addr6 = (struct sockaddr_in6 *)
9008c2ecf20Sopenharmony_ci					curp->ifa_netmask;
9018c2ecf20Sopenharmony_ci				w = addr6->sin6_addr.s6_addr32;
9028c2ecf20Sopenharmony_ci
9038c2ecf20Sopenharmony_ci				for (i = 0; i < 4; i++)
9048c2ecf20Sopenharmony_ci					weight += hweight32(&w[i]);
9058c2ecf20Sopenharmony_ci
9068c2ecf20Sopenharmony_ci				sprintf(cidr_mask, "/%d", weight);
9078c2ecf20Sopenharmony_ci				if (length < sn_offset + strlen(cidr_mask) + 1)
9088c2ecf20Sopenharmony_ci					goto gather_ipaddr;
9098c2ecf20Sopenharmony_ci
9108c2ecf20Sopenharmony_ci				if (sn_offset == 0)
9118c2ecf20Sopenharmony_ci					strcpy(sn_str, cidr_mask);
9128c2ecf20Sopenharmony_ci				else {
9138c2ecf20Sopenharmony_ci					strcat((char *)ip_buffer->sub_net, ";");
9148c2ecf20Sopenharmony_ci					strcat(sn_str, cidr_mask);
9158c2ecf20Sopenharmony_ci				}
9168c2ecf20Sopenharmony_ci				sn_offset += strlen(sn_str) + 1;
9178c2ecf20Sopenharmony_ci			}
9188c2ecf20Sopenharmony_ci
9198c2ecf20Sopenharmony_ci			/*
9208c2ecf20Sopenharmony_ci			 * Collect other ip related configuration info.
9218c2ecf20Sopenharmony_ci			 */
9228c2ecf20Sopenharmony_ci
9238c2ecf20Sopenharmony_ci			kvp_get_ipconfig_info(if_name, ip_buffer);
9248c2ecf20Sopenharmony_ci		}
9258c2ecf20Sopenharmony_ci
9268c2ecf20Sopenharmony_cigather_ipaddr:
9278c2ecf20Sopenharmony_ci		error = kvp_process_ip_address(curp->ifa_addr,
9288c2ecf20Sopenharmony_ci						curp->ifa_addr->sa_family,
9298c2ecf20Sopenharmony_ci						buffer,
9308c2ecf20Sopenharmony_ci						length, &offset);
9318c2ecf20Sopenharmony_ci		if (error)
9328c2ecf20Sopenharmony_ci			goto getaddr_done;
9338c2ecf20Sopenharmony_ci
9348c2ecf20Sopenharmony_ci		curp = curp->ifa_next;
9358c2ecf20Sopenharmony_ci	}
9368c2ecf20Sopenharmony_ci
9378c2ecf20Sopenharmony_cigetaddr_done:
9388c2ecf20Sopenharmony_ci	freeifaddrs(ifap);
9398c2ecf20Sopenharmony_ci	return error;
9408c2ecf20Sopenharmony_ci}
9418c2ecf20Sopenharmony_ci
9428c2ecf20Sopenharmony_ci/*
9438c2ecf20Sopenharmony_ci * Retrieve the IP given the MAC address.
9448c2ecf20Sopenharmony_ci */
9458c2ecf20Sopenharmony_cistatic int kvp_mac_to_ip(struct hv_kvp_ipaddr_value *kvp_ip_val)
9468c2ecf20Sopenharmony_ci{
9478c2ecf20Sopenharmony_ci	char *mac = (char *)kvp_ip_val->adapter_id;
9488c2ecf20Sopenharmony_ci	DIR *dir;
9498c2ecf20Sopenharmony_ci	struct dirent *entry;
9508c2ecf20Sopenharmony_ci	FILE    *file;
9518c2ecf20Sopenharmony_ci	char    *p, *x;
9528c2ecf20Sopenharmony_ci	char    *if_name = NULL;
9538c2ecf20Sopenharmony_ci	char    buf[256];
9548c2ecf20Sopenharmony_ci	char dev_id[PATH_MAX];
9558c2ecf20Sopenharmony_ci	unsigned int i;
9568c2ecf20Sopenharmony_ci	int error = HV_E_FAIL;
9578c2ecf20Sopenharmony_ci
9588c2ecf20Sopenharmony_ci	dir = opendir(KVP_NET_DIR);
9598c2ecf20Sopenharmony_ci	if (dir == NULL)
9608c2ecf20Sopenharmony_ci		return HV_E_FAIL;
9618c2ecf20Sopenharmony_ci
9628c2ecf20Sopenharmony_ci	while ((entry = readdir(dir)) != NULL) {
9638c2ecf20Sopenharmony_ci		/*
9648c2ecf20Sopenharmony_ci		 * Set the state for the next pass.
9658c2ecf20Sopenharmony_ci		 */
9668c2ecf20Sopenharmony_ci		snprintf(dev_id, sizeof(dev_id), "%s%s/address", KVP_NET_DIR,
9678c2ecf20Sopenharmony_ci			 entry->d_name);
9688c2ecf20Sopenharmony_ci
9698c2ecf20Sopenharmony_ci		file = fopen(dev_id, "r");
9708c2ecf20Sopenharmony_ci		if (file == NULL)
9718c2ecf20Sopenharmony_ci			continue;
9728c2ecf20Sopenharmony_ci
9738c2ecf20Sopenharmony_ci		p = fgets(buf, sizeof(buf), file);
9748c2ecf20Sopenharmony_ci		fclose(file);
9758c2ecf20Sopenharmony_ci		if (!p)
9768c2ecf20Sopenharmony_ci			continue;
9778c2ecf20Sopenharmony_ci
9788c2ecf20Sopenharmony_ci		x = strchr(p, '\n');
9798c2ecf20Sopenharmony_ci		if (x)
9808c2ecf20Sopenharmony_ci			*x = '\0';
9818c2ecf20Sopenharmony_ci
9828c2ecf20Sopenharmony_ci		for (i = 0; i < strlen(p); i++)
9838c2ecf20Sopenharmony_ci			p[i] = toupper(p[i]);
9848c2ecf20Sopenharmony_ci
9858c2ecf20Sopenharmony_ci		if (strcmp(p, mac))
9868c2ecf20Sopenharmony_ci			continue;
9878c2ecf20Sopenharmony_ci
9888c2ecf20Sopenharmony_ci		/*
9898c2ecf20Sopenharmony_ci		 * Found the MAC match.
9908c2ecf20Sopenharmony_ci		 * A NIC (e.g. VF) matching the MAC, but without IP, is skipped.
9918c2ecf20Sopenharmony_ci		 */
9928c2ecf20Sopenharmony_ci		if_name = entry->d_name;
9938c2ecf20Sopenharmony_ci		if (!if_name)
9948c2ecf20Sopenharmony_ci			continue;
9958c2ecf20Sopenharmony_ci
9968c2ecf20Sopenharmony_ci		error = kvp_get_ip_info(0, if_name, KVP_OP_GET_IP_INFO,
9978c2ecf20Sopenharmony_ci					kvp_ip_val, MAX_IP_ADDR_SIZE * 2);
9988c2ecf20Sopenharmony_ci
9998c2ecf20Sopenharmony_ci		if (!error && strlen((char *)kvp_ip_val->ip_addr))
10008c2ecf20Sopenharmony_ci			break;
10018c2ecf20Sopenharmony_ci	}
10028c2ecf20Sopenharmony_ci
10038c2ecf20Sopenharmony_ci	closedir(dir);
10048c2ecf20Sopenharmony_ci	return error;
10058c2ecf20Sopenharmony_ci}
10068c2ecf20Sopenharmony_ci
10078c2ecf20Sopenharmony_cistatic int expand_ipv6(char *addr, int type)
10088c2ecf20Sopenharmony_ci{
10098c2ecf20Sopenharmony_ci	int ret;
10108c2ecf20Sopenharmony_ci	struct in6_addr v6_addr;
10118c2ecf20Sopenharmony_ci
10128c2ecf20Sopenharmony_ci	ret = inet_pton(AF_INET6, addr, &v6_addr);
10138c2ecf20Sopenharmony_ci
10148c2ecf20Sopenharmony_ci	if (ret != 1) {
10158c2ecf20Sopenharmony_ci		if (type == NETMASK)
10168c2ecf20Sopenharmony_ci			return 1;
10178c2ecf20Sopenharmony_ci		return 0;
10188c2ecf20Sopenharmony_ci	}
10198c2ecf20Sopenharmony_ci
10208c2ecf20Sopenharmony_ci	sprintf(addr, "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:"
10218c2ecf20Sopenharmony_ci		"%02x%02x:%02x%02x:%02x%02x",
10228c2ecf20Sopenharmony_ci		(int)v6_addr.s6_addr[0], (int)v6_addr.s6_addr[1],
10238c2ecf20Sopenharmony_ci		(int)v6_addr.s6_addr[2], (int)v6_addr.s6_addr[3],
10248c2ecf20Sopenharmony_ci		(int)v6_addr.s6_addr[4], (int)v6_addr.s6_addr[5],
10258c2ecf20Sopenharmony_ci		(int)v6_addr.s6_addr[6], (int)v6_addr.s6_addr[7],
10268c2ecf20Sopenharmony_ci		(int)v6_addr.s6_addr[8], (int)v6_addr.s6_addr[9],
10278c2ecf20Sopenharmony_ci		(int)v6_addr.s6_addr[10], (int)v6_addr.s6_addr[11],
10288c2ecf20Sopenharmony_ci		(int)v6_addr.s6_addr[12], (int)v6_addr.s6_addr[13],
10298c2ecf20Sopenharmony_ci		(int)v6_addr.s6_addr[14], (int)v6_addr.s6_addr[15]);
10308c2ecf20Sopenharmony_ci
10318c2ecf20Sopenharmony_ci	return 1;
10328c2ecf20Sopenharmony_ci
10338c2ecf20Sopenharmony_ci}
10348c2ecf20Sopenharmony_ci
10358c2ecf20Sopenharmony_cistatic int is_ipv4(char *addr)
10368c2ecf20Sopenharmony_ci{
10378c2ecf20Sopenharmony_ci	int ret;
10388c2ecf20Sopenharmony_ci	struct in_addr ipv4_addr;
10398c2ecf20Sopenharmony_ci
10408c2ecf20Sopenharmony_ci	ret = inet_pton(AF_INET, addr, &ipv4_addr);
10418c2ecf20Sopenharmony_ci
10428c2ecf20Sopenharmony_ci	if (ret == 1)
10438c2ecf20Sopenharmony_ci		return 1;
10448c2ecf20Sopenharmony_ci	return 0;
10458c2ecf20Sopenharmony_ci}
10468c2ecf20Sopenharmony_ci
10478c2ecf20Sopenharmony_cistatic int parse_ip_val_buffer(char *in_buf, int *offset,
10488c2ecf20Sopenharmony_ci				char *out_buf, int out_len)
10498c2ecf20Sopenharmony_ci{
10508c2ecf20Sopenharmony_ci	char *x;
10518c2ecf20Sopenharmony_ci	char *start;
10528c2ecf20Sopenharmony_ci
10538c2ecf20Sopenharmony_ci	/*
10548c2ecf20Sopenharmony_ci	 * in_buf has sequence of characters that are separated by
10558c2ecf20Sopenharmony_ci	 * the character ';'. The last sequence does not have the
10568c2ecf20Sopenharmony_ci	 * terminating ";" character.
10578c2ecf20Sopenharmony_ci	 */
10588c2ecf20Sopenharmony_ci	start = in_buf + *offset;
10598c2ecf20Sopenharmony_ci
10608c2ecf20Sopenharmony_ci	x = strchr(start, ';');
10618c2ecf20Sopenharmony_ci	if (x)
10628c2ecf20Sopenharmony_ci		*x = 0;
10638c2ecf20Sopenharmony_ci	else
10648c2ecf20Sopenharmony_ci		x = start + strlen(start);
10658c2ecf20Sopenharmony_ci
10668c2ecf20Sopenharmony_ci	if (strlen(start) != 0) {
10678c2ecf20Sopenharmony_ci		int i = 0;
10688c2ecf20Sopenharmony_ci		/*
10698c2ecf20Sopenharmony_ci		 * Get rid of leading spaces.
10708c2ecf20Sopenharmony_ci		 */
10718c2ecf20Sopenharmony_ci		while (start[i] == ' ')
10728c2ecf20Sopenharmony_ci			i++;
10738c2ecf20Sopenharmony_ci
10748c2ecf20Sopenharmony_ci		if ((x - start) <= out_len) {
10758c2ecf20Sopenharmony_ci			strcpy(out_buf, (start + i));
10768c2ecf20Sopenharmony_ci			*offset += (x - start) + 1;
10778c2ecf20Sopenharmony_ci			return 1;
10788c2ecf20Sopenharmony_ci		}
10798c2ecf20Sopenharmony_ci	}
10808c2ecf20Sopenharmony_ci	return 0;
10818c2ecf20Sopenharmony_ci}
10828c2ecf20Sopenharmony_ci
10838c2ecf20Sopenharmony_cistatic int kvp_write_file(FILE *f, char *s1, char *s2, char *s3)
10848c2ecf20Sopenharmony_ci{
10858c2ecf20Sopenharmony_ci	int ret;
10868c2ecf20Sopenharmony_ci
10878c2ecf20Sopenharmony_ci	ret = fprintf(f, "%s%s%s%s\n", s1, s2, "=", s3);
10888c2ecf20Sopenharmony_ci
10898c2ecf20Sopenharmony_ci	if (ret < 0)
10908c2ecf20Sopenharmony_ci		return HV_E_FAIL;
10918c2ecf20Sopenharmony_ci
10928c2ecf20Sopenharmony_ci	return 0;
10938c2ecf20Sopenharmony_ci}
10948c2ecf20Sopenharmony_ci
10958c2ecf20Sopenharmony_ci
10968c2ecf20Sopenharmony_cistatic int process_ip_string(FILE *f, char *ip_string, int type)
10978c2ecf20Sopenharmony_ci{
10988c2ecf20Sopenharmony_ci	int error = 0;
10998c2ecf20Sopenharmony_ci	char addr[INET6_ADDRSTRLEN];
11008c2ecf20Sopenharmony_ci	int i = 0;
11018c2ecf20Sopenharmony_ci	int j = 0;
11028c2ecf20Sopenharmony_ci	char str[256];
11038c2ecf20Sopenharmony_ci	char sub_str[13];
11048c2ecf20Sopenharmony_ci	int offset = 0;
11058c2ecf20Sopenharmony_ci
11068c2ecf20Sopenharmony_ci	memset(addr, 0, sizeof(addr));
11078c2ecf20Sopenharmony_ci
11088c2ecf20Sopenharmony_ci	while (parse_ip_val_buffer(ip_string, &offset, addr,
11098c2ecf20Sopenharmony_ci					(MAX_IP_ADDR_SIZE * 2))) {
11108c2ecf20Sopenharmony_ci
11118c2ecf20Sopenharmony_ci		sub_str[0] = 0;
11128c2ecf20Sopenharmony_ci		if (is_ipv4(addr)) {
11138c2ecf20Sopenharmony_ci			switch (type) {
11148c2ecf20Sopenharmony_ci			case IPADDR:
11158c2ecf20Sopenharmony_ci				snprintf(str, sizeof(str), "%s", "IPADDR");
11168c2ecf20Sopenharmony_ci				break;
11178c2ecf20Sopenharmony_ci			case NETMASK:
11188c2ecf20Sopenharmony_ci				snprintf(str, sizeof(str), "%s", "NETMASK");
11198c2ecf20Sopenharmony_ci				break;
11208c2ecf20Sopenharmony_ci			case GATEWAY:
11218c2ecf20Sopenharmony_ci				snprintf(str, sizeof(str), "%s", "GATEWAY");
11228c2ecf20Sopenharmony_ci				break;
11238c2ecf20Sopenharmony_ci			case DNS:
11248c2ecf20Sopenharmony_ci				snprintf(str, sizeof(str), "%s", "DNS");
11258c2ecf20Sopenharmony_ci				break;
11268c2ecf20Sopenharmony_ci			}
11278c2ecf20Sopenharmony_ci
11288c2ecf20Sopenharmony_ci			if (type == DNS) {
11298c2ecf20Sopenharmony_ci				snprintf(sub_str, sizeof(sub_str), "%d", ++i);
11308c2ecf20Sopenharmony_ci			} else if (type == GATEWAY && i == 0) {
11318c2ecf20Sopenharmony_ci				++i;
11328c2ecf20Sopenharmony_ci			} else {
11338c2ecf20Sopenharmony_ci				snprintf(sub_str, sizeof(sub_str), "%d", i++);
11348c2ecf20Sopenharmony_ci			}
11358c2ecf20Sopenharmony_ci
11368c2ecf20Sopenharmony_ci
11378c2ecf20Sopenharmony_ci		} else if (expand_ipv6(addr, type)) {
11388c2ecf20Sopenharmony_ci			switch (type) {
11398c2ecf20Sopenharmony_ci			case IPADDR:
11408c2ecf20Sopenharmony_ci				snprintf(str, sizeof(str), "%s", "IPV6ADDR");
11418c2ecf20Sopenharmony_ci				break;
11428c2ecf20Sopenharmony_ci			case NETMASK:
11438c2ecf20Sopenharmony_ci				snprintf(str, sizeof(str), "%s", "IPV6NETMASK");
11448c2ecf20Sopenharmony_ci				break;
11458c2ecf20Sopenharmony_ci			case GATEWAY:
11468c2ecf20Sopenharmony_ci				snprintf(str, sizeof(str), "%s",
11478c2ecf20Sopenharmony_ci					"IPV6_DEFAULTGW");
11488c2ecf20Sopenharmony_ci				break;
11498c2ecf20Sopenharmony_ci			case DNS:
11508c2ecf20Sopenharmony_ci				snprintf(str, sizeof(str), "%s",  "DNS");
11518c2ecf20Sopenharmony_ci				break;
11528c2ecf20Sopenharmony_ci			}
11538c2ecf20Sopenharmony_ci
11548c2ecf20Sopenharmony_ci			if (type == DNS) {
11558c2ecf20Sopenharmony_ci				snprintf(sub_str, sizeof(sub_str), "%d", ++i);
11568c2ecf20Sopenharmony_ci			} else if (j == 0) {
11578c2ecf20Sopenharmony_ci				++j;
11588c2ecf20Sopenharmony_ci			} else {
11598c2ecf20Sopenharmony_ci				snprintf(sub_str, sizeof(sub_str), "_%d", j++);
11608c2ecf20Sopenharmony_ci			}
11618c2ecf20Sopenharmony_ci		} else {
11628c2ecf20Sopenharmony_ci			return  HV_INVALIDARG;
11638c2ecf20Sopenharmony_ci		}
11648c2ecf20Sopenharmony_ci
11658c2ecf20Sopenharmony_ci		error = kvp_write_file(f, str, sub_str, addr);
11668c2ecf20Sopenharmony_ci		if (error)
11678c2ecf20Sopenharmony_ci			return error;
11688c2ecf20Sopenharmony_ci		memset(addr, 0, sizeof(addr));
11698c2ecf20Sopenharmony_ci	}
11708c2ecf20Sopenharmony_ci
11718c2ecf20Sopenharmony_ci	return 0;
11728c2ecf20Sopenharmony_ci}
11738c2ecf20Sopenharmony_ci
11748c2ecf20Sopenharmony_cistatic int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val)
11758c2ecf20Sopenharmony_ci{
11768c2ecf20Sopenharmony_ci	int error = 0;
11778c2ecf20Sopenharmony_ci	char if_file[PATH_MAX];
11788c2ecf20Sopenharmony_ci	FILE *file;
11798c2ecf20Sopenharmony_ci	char cmd[PATH_MAX];
11808c2ecf20Sopenharmony_ci	char *mac_addr;
11818c2ecf20Sopenharmony_ci	int str_len;
11828c2ecf20Sopenharmony_ci
11838c2ecf20Sopenharmony_ci	/*
11848c2ecf20Sopenharmony_ci	 * Set the configuration for the specified interface with
11858c2ecf20Sopenharmony_ci	 * the information provided. Since there is no standard
11868c2ecf20Sopenharmony_ci	 * way to configure an interface, we will have an external
11878c2ecf20Sopenharmony_ci	 * script that does the job of configuring the interface and
11888c2ecf20Sopenharmony_ci	 * flushing the configuration.
11898c2ecf20Sopenharmony_ci	 *
11908c2ecf20Sopenharmony_ci	 * The parameters passed to this external script are:
11918c2ecf20Sopenharmony_ci	 * 1. A configuration file that has the specified configuration.
11928c2ecf20Sopenharmony_ci	 *
11938c2ecf20Sopenharmony_ci	 * We will embed the name of the interface in the configuration
11948c2ecf20Sopenharmony_ci	 * file: ifcfg-ethx (where ethx is the interface name).
11958c2ecf20Sopenharmony_ci	 *
11968c2ecf20Sopenharmony_ci	 * The information provided here may be more than what is needed
11978c2ecf20Sopenharmony_ci	 * in a given distro to configure the interface and so are free
11988c2ecf20Sopenharmony_ci	 * ignore information that may not be relevant.
11998c2ecf20Sopenharmony_ci	 *
12008c2ecf20Sopenharmony_ci	 * Here is the format of the ip configuration file:
12018c2ecf20Sopenharmony_ci	 *
12028c2ecf20Sopenharmony_ci	 * HWADDR=macaddr
12038c2ecf20Sopenharmony_ci	 * DEVICE=interface name
12048c2ecf20Sopenharmony_ci	 * BOOTPROTO=<protocol> (where <protocol> is "dhcp" if DHCP is configured
12058c2ecf20Sopenharmony_ci	 *                       or "none" if no boot-time protocol should be used)
12068c2ecf20Sopenharmony_ci	 *
12078c2ecf20Sopenharmony_ci	 * IPADDR0=ipaddr1
12088c2ecf20Sopenharmony_ci	 * IPADDR1=ipaddr2
12098c2ecf20Sopenharmony_ci	 * IPADDRx=ipaddry (where y = x + 1)
12108c2ecf20Sopenharmony_ci	 *
12118c2ecf20Sopenharmony_ci	 * NETMASK0=netmask1
12128c2ecf20Sopenharmony_ci	 * NETMASKx=netmasky (where y = x + 1)
12138c2ecf20Sopenharmony_ci	 *
12148c2ecf20Sopenharmony_ci	 * GATEWAY=ipaddr1
12158c2ecf20Sopenharmony_ci	 * GATEWAYx=ipaddry (where y = x + 1)
12168c2ecf20Sopenharmony_ci	 *
12178c2ecf20Sopenharmony_ci	 * DNSx=ipaddrx (where first DNS address is tagged as DNS1 etc)
12188c2ecf20Sopenharmony_ci	 *
12198c2ecf20Sopenharmony_ci	 * IPV6 addresses will be tagged as IPV6ADDR, IPV6 gateway will be
12208c2ecf20Sopenharmony_ci	 * tagged as IPV6_DEFAULTGW and IPV6 NETMASK will be tagged as
12218c2ecf20Sopenharmony_ci	 * IPV6NETMASK.
12228c2ecf20Sopenharmony_ci	 *
12238c2ecf20Sopenharmony_ci	 * The host can specify multiple ipv4 and ipv6 addresses to be
12248c2ecf20Sopenharmony_ci	 * configured for the interface. Furthermore, the configuration
12258c2ecf20Sopenharmony_ci	 * needs to be persistent. A subsequent GET call on the interface
12268c2ecf20Sopenharmony_ci	 * is expected to return the configuration that is set via the SET
12278c2ecf20Sopenharmony_ci	 * call.
12288c2ecf20Sopenharmony_ci	 */
12298c2ecf20Sopenharmony_ci
12308c2ecf20Sopenharmony_ci	snprintf(if_file, sizeof(if_file), "%s%s%s", KVP_CONFIG_LOC,
12318c2ecf20Sopenharmony_ci		"/ifcfg-", if_name);
12328c2ecf20Sopenharmony_ci
12338c2ecf20Sopenharmony_ci	file = fopen(if_file, "w");
12348c2ecf20Sopenharmony_ci
12358c2ecf20Sopenharmony_ci	if (file == NULL) {
12368c2ecf20Sopenharmony_ci		syslog(LOG_ERR, "Failed to open config file; error: %d %s",
12378c2ecf20Sopenharmony_ci				errno, strerror(errno));
12388c2ecf20Sopenharmony_ci		return HV_E_FAIL;
12398c2ecf20Sopenharmony_ci	}
12408c2ecf20Sopenharmony_ci
12418c2ecf20Sopenharmony_ci	/*
12428c2ecf20Sopenharmony_ci	 * First write out the MAC address.
12438c2ecf20Sopenharmony_ci	 */
12448c2ecf20Sopenharmony_ci
12458c2ecf20Sopenharmony_ci	mac_addr = kvp_if_name_to_mac(if_name);
12468c2ecf20Sopenharmony_ci	if (mac_addr == NULL) {
12478c2ecf20Sopenharmony_ci		error = HV_E_FAIL;
12488c2ecf20Sopenharmony_ci		goto setval_error;
12498c2ecf20Sopenharmony_ci	}
12508c2ecf20Sopenharmony_ci
12518c2ecf20Sopenharmony_ci	error = kvp_write_file(file, "HWADDR", "", mac_addr);
12528c2ecf20Sopenharmony_ci	free(mac_addr);
12538c2ecf20Sopenharmony_ci	if (error)
12548c2ecf20Sopenharmony_ci		goto setval_error;
12558c2ecf20Sopenharmony_ci
12568c2ecf20Sopenharmony_ci	error = kvp_write_file(file, "DEVICE", "", if_name);
12578c2ecf20Sopenharmony_ci	if (error)
12588c2ecf20Sopenharmony_ci		goto setval_error;
12598c2ecf20Sopenharmony_ci
12608c2ecf20Sopenharmony_ci	/*
12618c2ecf20Sopenharmony_ci	 * The dhcp_enabled flag is only for IPv4. In the case the host only
12628c2ecf20Sopenharmony_ci	 * injects an IPv6 address, the flag is true, but we still need to
12638c2ecf20Sopenharmony_ci	 * proceed to parse and pass the IPv6 information to the
12648c2ecf20Sopenharmony_ci	 * disto-specific script hv_set_ifconfig.
12658c2ecf20Sopenharmony_ci	 */
12668c2ecf20Sopenharmony_ci	if (new_val->dhcp_enabled) {
12678c2ecf20Sopenharmony_ci		error = kvp_write_file(file, "BOOTPROTO", "", "dhcp");
12688c2ecf20Sopenharmony_ci		if (error)
12698c2ecf20Sopenharmony_ci			goto setval_error;
12708c2ecf20Sopenharmony_ci
12718c2ecf20Sopenharmony_ci	} else {
12728c2ecf20Sopenharmony_ci		error = kvp_write_file(file, "BOOTPROTO", "", "none");
12738c2ecf20Sopenharmony_ci		if (error)
12748c2ecf20Sopenharmony_ci			goto setval_error;
12758c2ecf20Sopenharmony_ci	}
12768c2ecf20Sopenharmony_ci
12778c2ecf20Sopenharmony_ci	/*
12788c2ecf20Sopenharmony_ci	 * Write the configuration for ipaddress, netmask, gateway and
12798c2ecf20Sopenharmony_ci	 * name servers.
12808c2ecf20Sopenharmony_ci	 */
12818c2ecf20Sopenharmony_ci
12828c2ecf20Sopenharmony_ci	error = process_ip_string(file, (char *)new_val->ip_addr, IPADDR);
12838c2ecf20Sopenharmony_ci	if (error)
12848c2ecf20Sopenharmony_ci		goto setval_error;
12858c2ecf20Sopenharmony_ci
12868c2ecf20Sopenharmony_ci	error = process_ip_string(file, (char *)new_val->sub_net, NETMASK);
12878c2ecf20Sopenharmony_ci	if (error)
12888c2ecf20Sopenharmony_ci		goto setval_error;
12898c2ecf20Sopenharmony_ci
12908c2ecf20Sopenharmony_ci	error = process_ip_string(file, (char *)new_val->gate_way, GATEWAY);
12918c2ecf20Sopenharmony_ci	if (error)
12928c2ecf20Sopenharmony_ci		goto setval_error;
12938c2ecf20Sopenharmony_ci
12948c2ecf20Sopenharmony_ci	error = process_ip_string(file, (char *)new_val->dns_addr, DNS);
12958c2ecf20Sopenharmony_ci	if (error)
12968c2ecf20Sopenharmony_ci		goto setval_error;
12978c2ecf20Sopenharmony_ci
12988c2ecf20Sopenharmony_ci	fclose(file);
12998c2ecf20Sopenharmony_ci
13008c2ecf20Sopenharmony_ci	/*
13018c2ecf20Sopenharmony_ci	 * Now that we have populated the configuration file,
13028c2ecf20Sopenharmony_ci	 * invoke the external script to do its magic.
13038c2ecf20Sopenharmony_ci	 */
13048c2ecf20Sopenharmony_ci
13058c2ecf20Sopenharmony_ci	str_len = snprintf(cmd, sizeof(cmd), KVP_SCRIPTS_PATH "%s %s",
13068c2ecf20Sopenharmony_ci			   "hv_set_ifconfig", if_file);
13078c2ecf20Sopenharmony_ci	/*
13088c2ecf20Sopenharmony_ci	 * This is a little overcautious, but it's necessary to suppress some
13098c2ecf20Sopenharmony_ci	 * false warnings from gcc 8.0.1.
13108c2ecf20Sopenharmony_ci	 */
13118c2ecf20Sopenharmony_ci	if (str_len <= 0 || (unsigned int)str_len >= sizeof(cmd)) {
13128c2ecf20Sopenharmony_ci		syslog(LOG_ERR, "Cmd '%s' (len=%d) may be too long",
13138c2ecf20Sopenharmony_ci		       cmd, str_len);
13148c2ecf20Sopenharmony_ci		return HV_E_FAIL;
13158c2ecf20Sopenharmony_ci	}
13168c2ecf20Sopenharmony_ci
13178c2ecf20Sopenharmony_ci	if (system(cmd)) {
13188c2ecf20Sopenharmony_ci		syslog(LOG_ERR, "Failed to execute cmd '%s'; error: %d %s",
13198c2ecf20Sopenharmony_ci				cmd, errno, strerror(errno));
13208c2ecf20Sopenharmony_ci		return HV_E_FAIL;
13218c2ecf20Sopenharmony_ci	}
13228c2ecf20Sopenharmony_ci	return 0;
13238c2ecf20Sopenharmony_ci
13248c2ecf20Sopenharmony_cisetval_error:
13258c2ecf20Sopenharmony_ci	syslog(LOG_ERR, "Failed to write config file");
13268c2ecf20Sopenharmony_ci	fclose(file);
13278c2ecf20Sopenharmony_ci	return error;
13288c2ecf20Sopenharmony_ci}
13298c2ecf20Sopenharmony_ci
13308c2ecf20Sopenharmony_ci
13318c2ecf20Sopenharmony_cistatic void
13328c2ecf20Sopenharmony_cikvp_get_domain_name(char *buffer, int length)
13338c2ecf20Sopenharmony_ci{
13348c2ecf20Sopenharmony_ci	struct addrinfo	hints, *info ;
13358c2ecf20Sopenharmony_ci	int error = 0;
13368c2ecf20Sopenharmony_ci
13378c2ecf20Sopenharmony_ci	gethostname(buffer, length);
13388c2ecf20Sopenharmony_ci	memset(&hints, 0, sizeof(hints));
13398c2ecf20Sopenharmony_ci	hints.ai_family = AF_INET; /*Get only ipv4 addrinfo. */
13408c2ecf20Sopenharmony_ci	hints.ai_socktype = SOCK_STREAM;
13418c2ecf20Sopenharmony_ci	hints.ai_flags = AI_CANONNAME;
13428c2ecf20Sopenharmony_ci
13438c2ecf20Sopenharmony_ci	error = getaddrinfo(buffer, NULL, &hints, &info);
13448c2ecf20Sopenharmony_ci	if (error != 0) {
13458c2ecf20Sopenharmony_ci		snprintf(buffer, length, "getaddrinfo failed: 0x%x %s",
13468c2ecf20Sopenharmony_ci			error, gai_strerror(error));
13478c2ecf20Sopenharmony_ci		return;
13488c2ecf20Sopenharmony_ci	}
13498c2ecf20Sopenharmony_ci	snprintf(buffer, length, "%s", info->ai_canonname);
13508c2ecf20Sopenharmony_ci	freeaddrinfo(info);
13518c2ecf20Sopenharmony_ci}
13528c2ecf20Sopenharmony_ci
13538c2ecf20Sopenharmony_civoid print_usage(char *argv[])
13548c2ecf20Sopenharmony_ci{
13558c2ecf20Sopenharmony_ci	fprintf(stderr, "Usage: %s [options]\n"
13568c2ecf20Sopenharmony_ci		"Options are:\n"
13578c2ecf20Sopenharmony_ci		"  -n, --no-daemon        stay in foreground, don't daemonize\n"
13588c2ecf20Sopenharmony_ci		"  -h, --help             print this help\n", argv[0]);
13598c2ecf20Sopenharmony_ci}
13608c2ecf20Sopenharmony_ci
13618c2ecf20Sopenharmony_ciint main(int argc, char *argv[])
13628c2ecf20Sopenharmony_ci{
13638c2ecf20Sopenharmony_ci	int kvp_fd = -1, len;
13648c2ecf20Sopenharmony_ci	int error;
13658c2ecf20Sopenharmony_ci	struct pollfd pfd;
13668c2ecf20Sopenharmony_ci	char    *p;
13678c2ecf20Sopenharmony_ci	struct hv_kvp_msg hv_msg[1];
13688c2ecf20Sopenharmony_ci	char	*key_value;
13698c2ecf20Sopenharmony_ci	char	*key_name;
13708c2ecf20Sopenharmony_ci	int	op;
13718c2ecf20Sopenharmony_ci	int	pool;
13728c2ecf20Sopenharmony_ci	char	*if_name;
13738c2ecf20Sopenharmony_ci	struct hv_kvp_ipaddr_value *kvp_ip_val;
13748c2ecf20Sopenharmony_ci	int daemonize = 1, long_index = 0, opt;
13758c2ecf20Sopenharmony_ci
13768c2ecf20Sopenharmony_ci	static struct option long_options[] = {
13778c2ecf20Sopenharmony_ci		{"help",	no_argument,	   0,  'h' },
13788c2ecf20Sopenharmony_ci		{"no-daemon",	no_argument,	   0,  'n' },
13798c2ecf20Sopenharmony_ci		{0,		0,		   0,  0   }
13808c2ecf20Sopenharmony_ci	};
13818c2ecf20Sopenharmony_ci
13828c2ecf20Sopenharmony_ci	while ((opt = getopt_long(argc, argv, "hn", long_options,
13838c2ecf20Sopenharmony_ci				  &long_index)) != -1) {
13848c2ecf20Sopenharmony_ci		switch (opt) {
13858c2ecf20Sopenharmony_ci		case 'n':
13868c2ecf20Sopenharmony_ci			daemonize = 0;
13878c2ecf20Sopenharmony_ci			break;
13888c2ecf20Sopenharmony_ci		case 'h':
13898c2ecf20Sopenharmony_ci			print_usage(argv);
13908c2ecf20Sopenharmony_ci			exit(0);
13918c2ecf20Sopenharmony_ci		default:
13928c2ecf20Sopenharmony_ci			print_usage(argv);
13938c2ecf20Sopenharmony_ci			exit(EXIT_FAILURE);
13948c2ecf20Sopenharmony_ci		}
13958c2ecf20Sopenharmony_ci	}
13968c2ecf20Sopenharmony_ci
13978c2ecf20Sopenharmony_ci	if (daemonize && daemon(1, 0))
13988c2ecf20Sopenharmony_ci		return 1;
13998c2ecf20Sopenharmony_ci
14008c2ecf20Sopenharmony_ci	openlog("KVP", 0, LOG_USER);
14018c2ecf20Sopenharmony_ci	syslog(LOG_INFO, "KVP starting; pid is:%d", getpid());
14028c2ecf20Sopenharmony_ci
14038c2ecf20Sopenharmony_ci	/*
14048c2ecf20Sopenharmony_ci	 * Retrieve OS release information.
14058c2ecf20Sopenharmony_ci	 */
14068c2ecf20Sopenharmony_ci	kvp_get_os_info();
14078c2ecf20Sopenharmony_ci	/*
14088c2ecf20Sopenharmony_ci	 * Cache Fully Qualified Domain Name because getaddrinfo takes an
14098c2ecf20Sopenharmony_ci	 * unpredictable amount of time to finish.
14108c2ecf20Sopenharmony_ci	 */
14118c2ecf20Sopenharmony_ci	kvp_get_domain_name(full_domain_name, sizeof(full_domain_name));
14128c2ecf20Sopenharmony_ci
14138c2ecf20Sopenharmony_ci	if (kvp_file_init()) {
14148c2ecf20Sopenharmony_ci		syslog(LOG_ERR, "Failed to initialize the pools");
14158c2ecf20Sopenharmony_ci		exit(EXIT_FAILURE);
14168c2ecf20Sopenharmony_ci	}
14178c2ecf20Sopenharmony_ci
14188c2ecf20Sopenharmony_cireopen_kvp_fd:
14198c2ecf20Sopenharmony_ci	if (kvp_fd != -1)
14208c2ecf20Sopenharmony_ci		close(kvp_fd);
14218c2ecf20Sopenharmony_ci	in_hand_shake = 1;
14228c2ecf20Sopenharmony_ci	kvp_fd = open("/dev/vmbus/hv_kvp", O_RDWR | O_CLOEXEC);
14238c2ecf20Sopenharmony_ci
14248c2ecf20Sopenharmony_ci	if (kvp_fd < 0) {
14258c2ecf20Sopenharmony_ci		syslog(LOG_ERR, "open /dev/vmbus/hv_kvp failed; error: %d %s",
14268c2ecf20Sopenharmony_ci		       errno, strerror(errno));
14278c2ecf20Sopenharmony_ci		exit(EXIT_FAILURE);
14288c2ecf20Sopenharmony_ci	}
14298c2ecf20Sopenharmony_ci
14308c2ecf20Sopenharmony_ci	/*
14318c2ecf20Sopenharmony_ci	 * Register ourselves with the kernel.
14328c2ecf20Sopenharmony_ci	 */
14338c2ecf20Sopenharmony_ci	hv_msg->kvp_hdr.operation = KVP_OP_REGISTER1;
14348c2ecf20Sopenharmony_ci	len = write(kvp_fd, hv_msg, sizeof(struct hv_kvp_msg));
14358c2ecf20Sopenharmony_ci	if (len != sizeof(struct hv_kvp_msg)) {
14368c2ecf20Sopenharmony_ci		syslog(LOG_ERR, "registration to kernel failed; error: %d %s",
14378c2ecf20Sopenharmony_ci		       errno, strerror(errno));
14388c2ecf20Sopenharmony_ci		close(kvp_fd);
14398c2ecf20Sopenharmony_ci		exit(EXIT_FAILURE);
14408c2ecf20Sopenharmony_ci	}
14418c2ecf20Sopenharmony_ci
14428c2ecf20Sopenharmony_ci	pfd.fd = kvp_fd;
14438c2ecf20Sopenharmony_ci
14448c2ecf20Sopenharmony_ci	while (1) {
14458c2ecf20Sopenharmony_ci		pfd.events = POLLIN;
14468c2ecf20Sopenharmony_ci		pfd.revents = 0;
14478c2ecf20Sopenharmony_ci
14488c2ecf20Sopenharmony_ci		if (poll(&pfd, 1, -1) < 0) {
14498c2ecf20Sopenharmony_ci			syslog(LOG_ERR, "poll failed; error: %d %s", errno, strerror(errno));
14508c2ecf20Sopenharmony_ci			if (errno == EINVAL) {
14518c2ecf20Sopenharmony_ci				close(kvp_fd);
14528c2ecf20Sopenharmony_ci				exit(EXIT_FAILURE);
14538c2ecf20Sopenharmony_ci			}
14548c2ecf20Sopenharmony_ci			else
14558c2ecf20Sopenharmony_ci				continue;
14568c2ecf20Sopenharmony_ci		}
14578c2ecf20Sopenharmony_ci
14588c2ecf20Sopenharmony_ci		len = read(kvp_fd, hv_msg, sizeof(struct hv_kvp_msg));
14598c2ecf20Sopenharmony_ci
14608c2ecf20Sopenharmony_ci		if (len != sizeof(struct hv_kvp_msg)) {
14618c2ecf20Sopenharmony_ci			syslog(LOG_ERR, "read failed; error:%d %s",
14628c2ecf20Sopenharmony_ci			       errno, strerror(errno));
14638c2ecf20Sopenharmony_ci			goto reopen_kvp_fd;
14648c2ecf20Sopenharmony_ci		}
14658c2ecf20Sopenharmony_ci
14668c2ecf20Sopenharmony_ci		/*
14678c2ecf20Sopenharmony_ci		 * We will use the KVP header information to pass back
14688c2ecf20Sopenharmony_ci		 * the error from this daemon. So, first copy the state
14698c2ecf20Sopenharmony_ci		 * and set the error code to success.
14708c2ecf20Sopenharmony_ci		 */
14718c2ecf20Sopenharmony_ci		op = hv_msg->kvp_hdr.operation;
14728c2ecf20Sopenharmony_ci		pool = hv_msg->kvp_hdr.pool;
14738c2ecf20Sopenharmony_ci		hv_msg->error = HV_S_OK;
14748c2ecf20Sopenharmony_ci
14758c2ecf20Sopenharmony_ci		if ((in_hand_shake) && (op == KVP_OP_REGISTER1)) {
14768c2ecf20Sopenharmony_ci			/*
14778c2ecf20Sopenharmony_ci			 * Driver is registering with us; stash away the version
14788c2ecf20Sopenharmony_ci			 * information.
14798c2ecf20Sopenharmony_ci			 */
14808c2ecf20Sopenharmony_ci			in_hand_shake = 0;
14818c2ecf20Sopenharmony_ci			p = (char *)hv_msg->body.kvp_register.version;
14828c2ecf20Sopenharmony_ci			lic_version = malloc(strlen(p) + 1);
14838c2ecf20Sopenharmony_ci			if (lic_version) {
14848c2ecf20Sopenharmony_ci				strcpy(lic_version, p);
14858c2ecf20Sopenharmony_ci				syslog(LOG_INFO, "KVP LIC Version: %s",
14868c2ecf20Sopenharmony_ci				       lic_version);
14878c2ecf20Sopenharmony_ci			} else {
14888c2ecf20Sopenharmony_ci				syslog(LOG_ERR, "malloc failed");
14898c2ecf20Sopenharmony_ci			}
14908c2ecf20Sopenharmony_ci			continue;
14918c2ecf20Sopenharmony_ci		}
14928c2ecf20Sopenharmony_ci
14938c2ecf20Sopenharmony_ci		switch (op) {
14948c2ecf20Sopenharmony_ci		case KVP_OP_GET_IP_INFO:
14958c2ecf20Sopenharmony_ci			kvp_ip_val = &hv_msg->body.kvp_ip_val;
14968c2ecf20Sopenharmony_ci
14978c2ecf20Sopenharmony_ci			error = kvp_mac_to_ip(kvp_ip_val);
14988c2ecf20Sopenharmony_ci
14998c2ecf20Sopenharmony_ci			if (error)
15008c2ecf20Sopenharmony_ci				hv_msg->error = error;
15018c2ecf20Sopenharmony_ci
15028c2ecf20Sopenharmony_ci			break;
15038c2ecf20Sopenharmony_ci
15048c2ecf20Sopenharmony_ci		case KVP_OP_SET_IP_INFO:
15058c2ecf20Sopenharmony_ci			kvp_ip_val = &hv_msg->body.kvp_ip_val;
15068c2ecf20Sopenharmony_ci			if_name = kvp_get_if_name(
15078c2ecf20Sopenharmony_ci					(char *)kvp_ip_val->adapter_id);
15088c2ecf20Sopenharmony_ci			if (if_name == NULL) {
15098c2ecf20Sopenharmony_ci				/*
15108c2ecf20Sopenharmony_ci				 * We could not map the guid to an
15118c2ecf20Sopenharmony_ci				 * interface name; return error.
15128c2ecf20Sopenharmony_ci				 */
15138c2ecf20Sopenharmony_ci				hv_msg->error = HV_GUID_NOTFOUND;
15148c2ecf20Sopenharmony_ci				break;
15158c2ecf20Sopenharmony_ci			}
15168c2ecf20Sopenharmony_ci			error = kvp_set_ip_info(if_name, kvp_ip_val);
15178c2ecf20Sopenharmony_ci			if (error)
15188c2ecf20Sopenharmony_ci				hv_msg->error = error;
15198c2ecf20Sopenharmony_ci
15208c2ecf20Sopenharmony_ci			free(if_name);
15218c2ecf20Sopenharmony_ci			break;
15228c2ecf20Sopenharmony_ci
15238c2ecf20Sopenharmony_ci		case KVP_OP_SET:
15248c2ecf20Sopenharmony_ci			if (kvp_key_add_or_modify(pool,
15258c2ecf20Sopenharmony_ci					hv_msg->body.kvp_set.data.key,
15268c2ecf20Sopenharmony_ci					hv_msg->body.kvp_set.data.key_size,
15278c2ecf20Sopenharmony_ci					hv_msg->body.kvp_set.data.value,
15288c2ecf20Sopenharmony_ci					hv_msg->body.kvp_set.data.value_size))
15298c2ecf20Sopenharmony_ci					hv_msg->error = HV_S_CONT;
15308c2ecf20Sopenharmony_ci			break;
15318c2ecf20Sopenharmony_ci
15328c2ecf20Sopenharmony_ci		case KVP_OP_GET:
15338c2ecf20Sopenharmony_ci			if (kvp_get_value(pool,
15348c2ecf20Sopenharmony_ci					hv_msg->body.kvp_set.data.key,
15358c2ecf20Sopenharmony_ci					hv_msg->body.kvp_set.data.key_size,
15368c2ecf20Sopenharmony_ci					hv_msg->body.kvp_set.data.value,
15378c2ecf20Sopenharmony_ci					hv_msg->body.kvp_set.data.value_size))
15388c2ecf20Sopenharmony_ci					hv_msg->error = HV_S_CONT;
15398c2ecf20Sopenharmony_ci			break;
15408c2ecf20Sopenharmony_ci
15418c2ecf20Sopenharmony_ci		case KVP_OP_DELETE:
15428c2ecf20Sopenharmony_ci			if (kvp_key_delete(pool,
15438c2ecf20Sopenharmony_ci					hv_msg->body.kvp_delete.key,
15448c2ecf20Sopenharmony_ci					hv_msg->body.kvp_delete.key_size))
15458c2ecf20Sopenharmony_ci					hv_msg->error = HV_S_CONT;
15468c2ecf20Sopenharmony_ci			break;
15478c2ecf20Sopenharmony_ci
15488c2ecf20Sopenharmony_ci		default:
15498c2ecf20Sopenharmony_ci			break;
15508c2ecf20Sopenharmony_ci		}
15518c2ecf20Sopenharmony_ci
15528c2ecf20Sopenharmony_ci		if (op != KVP_OP_ENUMERATE)
15538c2ecf20Sopenharmony_ci			goto kvp_done;
15548c2ecf20Sopenharmony_ci
15558c2ecf20Sopenharmony_ci		/*
15568c2ecf20Sopenharmony_ci		 * If the pool is KVP_POOL_AUTO, dynamically generate
15578c2ecf20Sopenharmony_ci		 * both the key and the value; if not read from the
15588c2ecf20Sopenharmony_ci		 * appropriate pool.
15598c2ecf20Sopenharmony_ci		 */
15608c2ecf20Sopenharmony_ci		if (pool != KVP_POOL_AUTO) {
15618c2ecf20Sopenharmony_ci			if (kvp_pool_enumerate(pool,
15628c2ecf20Sopenharmony_ci					hv_msg->body.kvp_enum_data.index,
15638c2ecf20Sopenharmony_ci					hv_msg->body.kvp_enum_data.data.key,
15648c2ecf20Sopenharmony_ci					HV_KVP_EXCHANGE_MAX_KEY_SIZE,
15658c2ecf20Sopenharmony_ci					hv_msg->body.kvp_enum_data.data.value,
15668c2ecf20Sopenharmony_ci					HV_KVP_EXCHANGE_MAX_VALUE_SIZE))
15678c2ecf20Sopenharmony_ci					hv_msg->error = HV_S_CONT;
15688c2ecf20Sopenharmony_ci			goto kvp_done;
15698c2ecf20Sopenharmony_ci		}
15708c2ecf20Sopenharmony_ci
15718c2ecf20Sopenharmony_ci		key_name = (char *)hv_msg->body.kvp_enum_data.data.key;
15728c2ecf20Sopenharmony_ci		key_value = (char *)hv_msg->body.kvp_enum_data.data.value;
15738c2ecf20Sopenharmony_ci
15748c2ecf20Sopenharmony_ci		switch (hv_msg->body.kvp_enum_data.index) {
15758c2ecf20Sopenharmony_ci		case FullyQualifiedDomainName:
15768c2ecf20Sopenharmony_ci			strcpy(key_value, full_domain_name);
15778c2ecf20Sopenharmony_ci			strcpy(key_name, "FullyQualifiedDomainName");
15788c2ecf20Sopenharmony_ci			break;
15798c2ecf20Sopenharmony_ci		case IntegrationServicesVersion:
15808c2ecf20Sopenharmony_ci			strcpy(key_name, "IntegrationServicesVersion");
15818c2ecf20Sopenharmony_ci			strcpy(key_value, lic_version);
15828c2ecf20Sopenharmony_ci			break;
15838c2ecf20Sopenharmony_ci		case NetworkAddressIPv4:
15848c2ecf20Sopenharmony_ci			kvp_get_ip_info(AF_INET, NULL, KVP_OP_ENUMERATE,
15858c2ecf20Sopenharmony_ci				key_value, HV_KVP_EXCHANGE_MAX_VALUE_SIZE);
15868c2ecf20Sopenharmony_ci			strcpy(key_name, "NetworkAddressIPv4");
15878c2ecf20Sopenharmony_ci			break;
15888c2ecf20Sopenharmony_ci		case NetworkAddressIPv6:
15898c2ecf20Sopenharmony_ci			kvp_get_ip_info(AF_INET6, NULL, KVP_OP_ENUMERATE,
15908c2ecf20Sopenharmony_ci				key_value, HV_KVP_EXCHANGE_MAX_VALUE_SIZE);
15918c2ecf20Sopenharmony_ci			strcpy(key_name, "NetworkAddressIPv6");
15928c2ecf20Sopenharmony_ci			break;
15938c2ecf20Sopenharmony_ci		case OSBuildNumber:
15948c2ecf20Sopenharmony_ci			strcpy(key_value, os_build);
15958c2ecf20Sopenharmony_ci			strcpy(key_name, "OSBuildNumber");
15968c2ecf20Sopenharmony_ci			break;
15978c2ecf20Sopenharmony_ci		case OSName:
15988c2ecf20Sopenharmony_ci			strcpy(key_value, os_name);
15998c2ecf20Sopenharmony_ci			strcpy(key_name, "OSName");
16008c2ecf20Sopenharmony_ci			break;
16018c2ecf20Sopenharmony_ci		case OSMajorVersion:
16028c2ecf20Sopenharmony_ci			strcpy(key_value, os_major);
16038c2ecf20Sopenharmony_ci			strcpy(key_name, "OSMajorVersion");
16048c2ecf20Sopenharmony_ci			break;
16058c2ecf20Sopenharmony_ci		case OSMinorVersion:
16068c2ecf20Sopenharmony_ci			strcpy(key_value, os_minor);
16078c2ecf20Sopenharmony_ci			strcpy(key_name, "OSMinorVersion");
16088c2ecf20Sopenharmony_ci			break;
16098c2ecf20Sopenharmony_ci		case OSVersion:
16108c2ecf20Sopenharmony_ci			strcpy(key_value, os_version);
16118c2ecf20Sopenharmony_ci			strcpy(key_name, "OSVersion");
16128c2ecf20Sopenharmony_ci			break;
16138c2ecf20Sopenharmony_ci		case ProcessorArchitecture:
16148c2ecf20Sopenharmony_ci			strcpy(key_value, processor_arch);
16158c2ecf20Sopenharmony_ci			strcpy(key_name, "ProcessorArchitecture");
16168c2ecf20Sopenharmony_ci			break;
16178c2ecf20Sopenharmony_ci		default:
16188c2ecf20Sopenharmony_ci			hv_msg->error = HV_S_CONT;
16198c2ecf20Sopenharmony_ci			break;
16208c2ecf20Sopenharmony_ci		}
16218c2ecf20Sopenharmony_ci
16228c2ecf20Sopenharmony_ci		/*
16238c2ecf20Sopenharmony_ci		 * Send the value back to the kernel. Note: the write() may
16248c2ecf20Sopenharmony_ci		 * return an error due to hibernation; we can ignore the error
16258c2ecf20Sopenharmony_ci		 * by resetting the dev file, i.e. closing and re-opening it.
16268c2ecf20Sopenharmony_ci		 */
16278c2ecf20Sopenharmony_cikvp_done:
16288c2ecf20Sopenharmony_ci		len = write(kvp_fd, hv_msg, sizeof(struct hv_kvp_msg));
16298c2ecf20Sopenharmony_ci		if (len != sizeof(struct hv_kvp_msg)) {
16308c2ecf20Sopenharmony_ci			syslog(LOG_ERR, "write failed; error: %d %s", errno,
16318c2ecf20Sopenharmony_ci			       strerror(errno));
16328c2ecf20Sopenharmony_ci			goto reopen_kvp_fd;
16338c2ecf20Sopenharmony_ci		}
16348c2ecf20Sopenharmony_ci	}
16358c2ecf20Sopenharmony_ci
16368c2ecf20Sopenharmony_ci	close(kvp_fd);
16378c2ecf20Sopenharmony_ci	exit(0);
16388c2ecf20Sopenharmony_ci}
1639