xref: /openbmc/linux/tools/hv/hv_kvp_daemon.c (revision 63dc02bd)
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 it
9  * under the terms of the GNU General Public License version 2 as published
10  * by the Free Software Foundation.
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
15  * NON INFRINGEMENT.  See the GNU General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
21  *
22  */
23 
24 
25 #include <sys/types.h>
26 #include <sys/socket.h>
27 #include <sys/poll.h>
28 #include <sys/utsname.h>
29 #include <linux/types.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <unistd.h>
33 #include <string.h>
34 #include <errno.h>
35 #include <arpa/inet.h>
36 #include <linux/connector.h>
37 #include <linux/hyperv.h>
38 #include <linux/netlink.h>
39 #include <ifaddrs.h>
40 #include <netdb.h>
41 #include <syslog.h>
42 #include <sys/stat.h>
43 #include <fcntl.h>
44 
45 /*
46  * KVP protocol: The user mode component first registers with the
47  * the kernel component. Subsequently, the kernel component requests, data
48  * for the specified keys. In response to this message the user mode component
49  * fills in the value corresponding to the specified key. We overload the
50  * 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 mode
53  * application for state that may be maintained in the KVP kernel component.
54  *
55  */
56 
57 
58 enum 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 	ProcessorArchitecture
69 };
70 
71 static char kvp_send_buffer[4096];
72 static char kvp_recv_buffer[4096];
73 static struct sockaddr_nl addr;
74 
75 static char *os_name = "";
76 static char *os_major = "";
77 static char *os_minor = "";
78 static char *processor_arch;
79 static char *os_build;
80 static char *lic_version;
81 static struct utsname uts_buf;
82 
83 
84 #define MAX_FILE_NAME 100
85 #define ENTRIES_PER_BLOCK 50
86 
87 struct kvp_record {
88 	__u8 key[HV_KVP_EXCHANGE_MAX_KEY_SIZE];
89 	__u8 value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE];
90 };
91 
92 struct kvp_file_state {
93 	int fd;
94 	int num_blocks;
95 	struct kvp_record *records;
96 	int num_records;
97 	__u8 fname[MAX_FILE_NAME];
98 };
99 
100 static struct kvp_file_state kvp_file_info[KVP_POOL_COUNT];
101 
102 static void kvp_acquire_lock(int pool)
103 {
104 	struct flock fl = {F_WRLCK, SEEK_SET, 0, 0, 0};
105 	fl.l_pid = getpid();
106 
107 	if (fcntl(kvp_file_info[pool].fd, F_SETLKW, &fl) == -1) {
108 		syslog(LOG_ERR, "Failed to acquire the lock pool: %d", pool);
109 		exit(-1);
110 	}
111 }
112 
113 static void kvp_release_lock(int pool)
114 {
115 	struct flock fl = {F_UNLCK, SEEK_SET, 0, 0, 0};
116 	fl.l_pid = getpid();
117 
118 	if (fcntl(kvp_file_info[pool].fd, F_SETLK, &fl) == -1) {
119 		perror("fcntl");
120 		syslog(LOG_ERR, "Failed to release the lock pool: %d", pool);
121 		exit(-1);
122 	}
123 }
124 
125 static void kvp_update_file(int pool)
126 {
127 	FILE *filep;
128 	size_t bytes_written;
129 
130 	/*
131 	 * We are going to write our in-memory registry out to
132 	 * disk; acquire the lock first.
133 	 */
134 	kvp_acquire_lock(pool);
135 
136 	filep = fopen(kvp_file_info[pool].fname, "w");
137 	if (!filep) {
138 		kvp_release_lock(pool);
139 		syslog(LOG_ERR, "Failed to open file, pool: %d", pool);
140 		exit(-1);
141 	}
142 
143 	bytes_written = fwrite(kvp_file_info[pool].records,
144 				sizeof(struct kvp_record),
145 				kvp_file_info[pool].num_records, filep);
146 
147 	fflush(filep);
148 	kvp_release_lock(pool);
149 }
150 
151 static void kvp_update_mem_state(int pool)
152 {
153 	FILE *filep;
154 	size_t records_read = 0;
155 	struct kvp_record *record = kvp_file_info[pool].records;
156 	struct kvp_record *readp;
157 	int num_blocks = kvp_file_info[pool].num_blocks;
158 	int alloc_unit = sizeof(struct kvp_record) * ENTRIES_PER_BLOCK;
159 
160 	kvp_acquire_lock(pool);
161 
162 	filep = fopen(kvp_file_info[pool].fname, "r");
163 	if (!filep) {
164 		kvp_release_lock(pool);
165 		syslog(LOG_ERR, "Failed to open file, pool: %d", pool);
166 		exit(-1);
167 	}
168 	while (!feof(filep)) {
169 		readp = &record[records_read];
170 		records_read += fread(readp, sizeof(struct kvp_record),
171 					ENTRIES_PER_BLOCK * num_blocks,
172 					filep);
173 
174 		if (!feof(filep)) {
175 			/*
176 			 * We have more data to read.
177 			 */
178 			num_blocks++;
179 			record = realloc(record, alloc_unit * num_blocks);
180 
181 			if (record == NULL) {
182 				syslog(LOG_ERR, "malloc failed");
183 				exit(-1);
184 			}
185 			continue;
186 		}
187 		break;
188 	}
189 
190 	kvp_file_info[pool].num_blocks = num_blocks;
191 	kvp_file_info[pool].records = record;
192 	kvp_file_info[pool].num_records = records_read;
193 
194 	kvp_release_lock(pool);
195 }
196 static int kvp_file_init(void)
197 {
198 	int ret, fd;
199 	FILE *filep;
200 	size_t records_read;
201 	__u8 *fname;
202 	struct kvp_record *record;
203 	struct kvp_record *readp;
204 	int num_blocks;
205 	int i;
206 	int alloc_unit = sizeof(struct kvp_record) * ENTRIES_PER_BLOCK;
207 
208 	if (access("/var/opt/hyperv", F_OK)) {
209 		if (mkdir("/var/opt/hyperv", S_IRUSR | S_IWUSR | S_IROTH)) {
210 			syslog(LOG_ERR, " Failed to create /var/opt/hyperv");
211 			exit(-1);
212 		}
213 	}
214 
215 	for (i = 0; i < KVP_POOL_COUNT; i++) {
216 		fname = kvp_file_info[i].fname;
217 		records_read = 0;
218 		num_blocks = 1;
219 		sprintf(fname, "/var/opt/hyperv/.kvp_pool_%d", i);
220 		fd = open(fname, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IROTH);
221 
222 		if (fd == -1)
223 			return 1;
224 
225 
226 		filep = fopen(fname, "r");
227 		if (!filep)
228 			return 1;
229 
230 		record = malloc(alloc_unit * num_blocks);
231 		if (record == NULL) {
232 			fclose(filep);
233 			return 1;
234 		}
235 		while (!feof(filep)) {
236 			readp = &record[records_read];
237 			records_read += fread(readp, sizeof(struct kvp_record),
238 					ENTRIES_PER_BLOCK,
239 					filep);
240 
241 			if (!feof(filep)) {
242 				/*
243 				 * We have more data to read.
244 				 */
245 				num_blocks++;
246 				record = realloc(record, alloc_unit *
247 						num_blocks);
248 				if (record == NULL) {
249 					fclose(filep);
250 					return 1;
251 				}
252 				continue;
253 			}
254 			break;
255 		}
256 		kvp_file_info[i].fd = fd;
257 		kvp_file_info[i].num_blocks = num_blocks;
258 		kvp_file_info[i].records = record;
259 		kvp_file_info[i].num_records = records_read;
260 		fclose(filep);
261 
262 	}
263 
264 	return 0;
265 }
266 
267 static int kvp_key_delete(int pool, __u8 *key, int key_size)
268 {
269 	int i;
270 	int j, k;
271 	int num_records;
272 	struct kvp_record *record;
273 
274 	/*
275 	 * First update the in-memory state.
276 	 */
277 	kvp_update_mem_state(pool);
278 
279 	num_records = kvp_file_info[pool].num_records;
280 	record = kvp_file_info[pool].records;
281 
282 	for (i = 0; i < num_records; i++) {
283 		if (memcmp(key, record[i].key, key_size))
284 			continue;
285 		/*
286 		 * Found a match; just move the remaining
287 		 * entries up.
288 		 */
289 		if (i == num_records) {
290 			kvp_file_info[pool].num_records--;
291 			kvp_update_file(pool);
292 			return 0;
293 		}
294 
295 		j = i;
296 		k = j + 1;
297 		for (; k < num_records; k++) {
298 			strcpy(record[j].key, record[k].key);
299 			strcpy(record[j].value, record[k].value);
300 			j++;
301 		}
302 
303 		kvp_file_info[pool].num_records--;
304 		kvp_update_file(pool);
305 		return 0;
306 	}
307 	return 1;
308 }
309 
310 static int kvp_key_add_or_modify(int pool, __u8 *key, int key_size, __u8 *value,
311 			int value_size)
312 {
313 	int i;
314 	int j, k;
315 	int num_records;
316 	struct kvp_record *record;
317 	int num_blocks;
318 
319 	if ((key_size > HV_KVP_EXCHANGE_MAX_KEY_SIZE) ||
320 		(value_size > HV_KVP_EXCHANGE_MAX_VALUE_SIZE))
321 		return 1;
322 
323 	/*
324 	 * First update the in-memory state.
325 	 */
326 	kvp_update_mem_state(pool);
327 
328 	num_records = kvp_file_info[pool].num_records;
329 	record = kvp_file_info[pool].records;
330 	num_blocks = kvp_file_info[pool].num_blocks;
331 
332 	for (i = 0; i < num_records; i++) {
333 		if (memcmp(key, record[i].key, key_size))
334 			continue;
335 		/*
336 		 * Found a match; just update the value -
337 		 * this is the modify case.
338 		 */
339 		memcpy(record[i].value, value, value_size);
340 		kvp_update_file(pool);
341 		return 0;
342 	}
343 
344 	/*
345 	 * Need to add a new entry;
346 	 */
347 	if (num_records == (ENTRIES_PER_BLOCK * num_blocks)) {
348 		/* Need to allocate a larger array for reg entries. */
349 		record = realloc(record, sizeof(struct kvp_record) *
350 			 ENTRIES_PER_BLOCK * (num_blocks + 1));
351 
352 		if (record == NULL)
353 			return 1;
354 		kvp_file_info[pool].num_blocks++;
355 
356 	}
357 	memcpy(record[i].value, value, value_size);
358 	memcpy(record[i].key, key, key_size);
359 	kvp_file_info[pool].records = record;
360 	kvp_file_info[pool].num_records++;
361 	kvp_update_file(pool);
362 	return 0;
363 }
364 
365 static int kvp_get_value(int pool, __u8 *key, int key_size, __u8 *value,
366 			int value_size)
367 {
368 	int i;
369 	int num_records;
370 	struct kvp_record *record;
371 
372 	if ((key_size > HV_KVP_EXCHANGE_MAX_KEY_SIZE) ||
373 		(value_size > HV_KVP_EXCHANGE_MAX_VALUE_SIZE))
374 		return 1;
375 
376 	/*
377 	 * First update the in-memory state.
378 	 */
379 	kvp_update_mem_state(pool);
380 
381 	num_records = kvp_file_info[pool].num_records;
382 	record = kvp_file_info[pool].records;
383 
384 	for (i = 0; i < num_records; i++) {
385 		if (memcmp(key, record[i].key, key_size))
386 			continue;
387 		/*
388 		 * Found a match; just copy the value out.
389 		 */
390 		memcpy(value, record[i].value, value_size);
391 		return 0;
392 	}
393 
394 	return 1;
395 }
396 
397 static void kvp_pool_enumerate(int pool, int index, __u8 *key, int key_size,
398 				__u8 *value, int value_size)
399 {
400 	struct kvp_record *record;
401 
402 	/*
403 	 * First update our in-memory database.
404 	 */
405 	kvp_update_mem_state(pool);
406 	record = kvp_file_info[pool].records;
407 
408 	if (index >= kvp_file_info[pool].num_records) {
409 		/*
410 		 * This is an invalid index; terminate enumeration;
411 		 * - a NULL value will do the trick.
412 		 */
413 		strcpy(value, "");
414 		return;
415 	}
416 
417 	memcpy(key, record[index].key, key_size);
418 	memcpy(value, record[index].value, value_size);
419 }
420 
421 
422 void kvp_get_os_info(void)
423 {
424 	FILE	*file;
425 	char	*p, buf[512];
426 
427 	uname(&uts_buf);
428 	os_build = uts_buf.release;
429 	processor_arch = uts_buf.machine;
430 
431 	/*
432 	 * The current windows host (win7) expects the build
433 	 * string to be of the form: x.y.z
434 	 * Strip additional information we may have.
435 	 */
436 	p = strchr(os_build, '-');
437 	if (p)
438 		*p = '\0';
439 
440 	file = fopen("/etc/SuSE-release", "r");
441 	if (file != NULL)
442 		goto kvp_osinfo_found;
443 	file  = fopen("/etc/redhat-release", "r");
444 	if (file != NULL)
445 		goto kvp_osinfo_found;
446 	/*
447 	 * Add code for other supported platforms.
448 	 */
449 
450 	/*
451 	 * We don't have information about the os.
452 	 */
453 	os_name = uts_buf.sysname;
454 	return;
455 
456 kvp_osinfo_found:
457 	/* up to three lines */
458 	p = fgets(buf, sizeof(buf), file);
459 	if (p) {
460 		p = strchr(buf, '\n');
461 		if (p)
462 			*p = '\0';
463 		p = strdup(buf);
464 		if (!p)
465 			goto done;
466 		os_name = p;
467 
468 		/* second line */
469 		p = fgets(buf, sizeof(buf), file);
470 		if (p) {
471 			p = strchr(buf, '\n');
472 			if (p)
473 				*p = '\0';
474 			p = strdup(buf);
475 			if (!p)
476 				goto done;
477 			os_major = p;
478 
479 			/* third line */
480 			p = fgets(buf, sizeof(buf), file);
481 			if (p)  {
482 				p = strchr(buf, '\n');
483 				if (p)
484 					*p = '\0';
485 				p = strdup(buf);
486 				if (p)
487 					os_minor = p;
488 			}
489 		}
490 	}
491 
492 done:
493 	fclose(file);
494 	return;
495 }
496 
497 static int
498 kvp_get_ip_address(int family, char *buffer, int length)
499 {
500 	struct ifaddrs *ifap;
501 	struct ifaddrs *curp;
502 	int ipv4_len = strlen("255.255.255.255") + 1;
503 	int ipv6_len = strlen("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")+1;
504 	int offset = 0;
505 	const char *str;
506 	char tmp[50];
507 	int error = 0;
508 
509 	/*
510 	 * On entry into this function, the buffer is capable of holding the
511 	 * maximum key value (2048 bytes).
512 	 */
513 
514 	if (getifaddrs(&ifap)) {
515 		strcpy(buffer, "getifaddrs failed\n");
516 		return 1;
517 	}
518 
519 	curp = ifap;
520 	while (curp != NULL) {
521 		if ((curp->ifa_addr != NULL) &&
522 		   (curp->ifa_addr->sa_family == family)) {
523 			if (family == AF_INET) {
524 				struct sockaddr_in *addr =
525 				(struct sockaddr_in *) curp->ifa_addr;
526 
527 				str = inet_ntop(family, &addr->sin_addr,
528 						tmp, 50);
529 				if (str == NULL) {
530 					strcpy(buffer, "inet_ntop failed\n");
531 					error = 1;
532 					goto getaddr_done;
533 				}
534 				if (offset == 0)
535 					strcpy(buffer, tmp);
536 				else
537 					strcat(buffer, tmp);
538 				strcat(buffer, ";");
539 
540 				offset += strlen(str) + 1;
541 				if ((length - offset) < (ipv4_len + 1))
542 					goto getaddr_done;
543 
544 			} else {
545 
546 			/*
547 			 * We only support AF_INET and AF_INET6
548 			 * and the list of addresses is separated by a ";".
549 			 */
550 				struct sockaddr_in6 *addr =
551 				(struct sockaddr_in6 *) curp->ifa_addr;
552 
553 				str = inet_ntop(family,
554 					&addr->sin6_addr.s6_addr,
555 					tmp, 50);
556 				if (str == NULL) {
557 					strcpy(buffer, "inet_ntop failed\n");
558 					error = 1;
559 					goto getaddr_done;
560 				}
561 				if (offset == 0)
562 					strcpy(buffer, tmp);
563 				else
564 					strcat(buffer, tmp);
565 				strcat(buffer, ";");
566 				offset += strlen(str) + 1;
567 				if ((length - offset) < (ipv6_len + 1))
568 					goto getaddr_done;
569 
570 			}
571 
572 		}
573 		curp = curp->ifa_next;
574 	}
575 
576 getaddr_done:
577 	freeifaddrs(ifap);
578 	return error;
579 }
580 
581 
582 static int
583 kvp_get_domain_name(char *buffer, int length)
584 {
585 	struct addrinfo	hints, *info ;
586 	int error = 0;
587 
588 	gethostname(buffer, length);
589 	memset(&hints, 0, sizeof(hints));
590 	hints.ai_family = AF_INET; /*Get only ipv4 addrinfo. */
591 	hints.ai_socktype = SOCK_STREAM;
592 	hints.ai_flags = AI_CANONNAME;
593 
594 	error = getaddrinfo(buffer, NULL, &hints, &info);
595 	if (error != 0) {
596 		strcpy(buffer, "getaddrinfo failed\n");
597 		return error;
598 	}
599 	strcpy(buffer, info->ai_canonname);
600 	freeaddrinfo(info);
601 	return error;
602 }
603 
604 static int
605 netlink_send(int fd, struct cn_msg *msg)
606 {
607 	struct nlmsghdr *nlh;
608 	unsigned int size;
609 	struct msghdr message;
610 	char buffer[64];
611 	struct iovec iov[2];
612 
613 	size = NLMSG_SPACE(sizeof(struct cn_msg) + msg->len);
614 
615 	nlh = (struct nlmsghdr *)buffer;
616 	nlh->nlmsg_seq = 0;
617 	nlh->nlmsg_pid = getpid();
618 	nlh->nlmsg_type = NLMSG_DONE;
619 	nlh->nlmsg_len = NLMSG_LENGTH(size - sizeof(*nlh));
620 	nlh->nlmsg_flags = 0;
621 
622 	iov[0].iov_base = nlh;
623 	iov[0].iov_len = sizeof(*nlh);
624 
625 	iov[1].iov_base = msg;
626 	iov[1].iov_len = size;
627 
628 	memset(&message, 0, sizeof(message));
629 	message.msg_name = &addr;
630 	message.msg_namelen = sizeof(addr);
631 	message.msg_iov = iov;
632 	message.msg_iovlen = 2;
633 
634 	return sendmsg(fd, &message, 0);
635 }
636 
637 int main(void)
638 {
639 	int fd, len, sock_opt;
640 	int error;
641 	struct cn_msg *message;
642 	struct pollfd pfd;
643 	struct nlmsghdr *incoming_msg;
644 	struct cn_msg	*incoming_cn_msg;
645 	struct hv_kvp_msg *hv_msg;
646 	char	*p;
647 	char	*key_value;
648 	char	*key_name;
649 
650 	daemon(1, 0);
651 	openlog("KVP", 0, LOG_USER);
652 	syslog(LOG_INFO, "KVP starting; pid is:%d", getpid());
653 	/*
654 	 * Retrieve OS release information.
655 	 */
656 	kvp_get_os_info();
657 
658 	if (kvp_file_init()) {
659 		syslog(LOG_ERR, "Failed to initialize the pools");
660 		exit(-1);
661 	}
662 
663 	fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR);
664 	if (fd < 0) {
665 		syslog(LOG_ERR, "netlink socket creation failed; error:%d", fd);
666 		exit(-1);
667 	}
668 	addr.nl_family = AF_NETLINK;
669 	addr.nl_pad = 0;
670 	addr.nl_pid = 0;
671 	addr.nl_groups = CN_KVP_IDX;
672 
673 
674 	error = bind(fd, (struct sockaddr *)&addr, sizeof(addr));
675 	if (error < 0) {
676 		syslog(LOG_ERR, "bind failed; error:%d", error);
677 		close(fd);
678 		exit(-1);
679 	}
680 	sock_opt = addr.nl_groups;
681 	setsockopt(fd, 270, 1, &sock_opt, sizeof(sock_opt));
682 	/*
683 	 * Register ourselves with the kernel.
684 	 */
685 	message = (struct cn_msg *)kvp_send_buffer;
686 	message->id.idx = CN_KVP_IDX;
687 	message->id.val = CN_KVP_VAL;
688 
689 	hv_msg = (struct hv_kvp_msg *)message->data;
690 	hv_msg->kvp_hdr.operation = KVP_OP_REGISTER;
691 	message->ack = 0;
692 	message->len = sizeof(struct hv_kvp_msg);
693 
694 	len = netlink_send(fd, message);
695 	if (len < 0) {
696 		syslog(LOG_ERR, "netlink_send failed; error:%d", len);
697 		close(fd);
698 		exit(-1);
699 	}
700 
701 	pfd.fd = fd;
702 
703 	while (1) {
704 		pfd.events = POLLIN;
705 		pfd.revents = 0;
706 		poll(&pfd, 1, -1);
707 
708 		len = recv(fd, kvp_recv_buffer, sizeof(kvp_recv_buffer), 0);
709 
710 		if (len < 0) {
711 			syslog(LOG_ERR, "recv failed; error:%d", len);
712 			close(fd);
713 			return -1;
714 		}
715 
716 		incoming_msg = (struct nlmsghdr *)kvp_recv_buffer;
717 		incoming_cn_msg = (struct cn_msg *)NLMSG_DATA(incoming_msg);
718 		hv_msg = (struct hv_kvp_msg *)incoming_cn_msg->data;
719 
720 		switch (hv_msg->kvp_hdr.operation) {
721 		case KVP_OP_REGISTER:
722 			/*
723 			 * Driver is registering with us; stash away the version
724 			 * information.
725 			 */
726 			p = (char *)hv_msg->body.kvp_register.version;
727 			lic_version = malloc(strlen(p) + 1);
728 			if (lic_version) {
729 				strcpy(lic_version, p);
730 				syslog(LOG_INFO, "KVP LIC Version: %s",
731 					lic_version);
732 			} else {
733 				syslog(LOG_ERR, "malloc failed");
734 			}
735 			continue;
736 
737 		/*
738 		 * The current protocol with the kernel component uses a
739 		 * NULL key name to pass an error condition.
740 		 * For the SET, GET and DELETE operations,
741 		 * use the existing protocol to pass back error.
742 		 */
743 
744 		case KVP_OP_SET:
745 			if (kvp_key_add_or_modify(hv_msg->kvp_hdr.pool,
746 					hv_msg->body.kvp_set.data.key,
747 					hv_msg->body.kvp_set.data.key_size,
748 					hv_msg->body.kvp_set.data.value,
749 					hv_msg->body.kvp_set.data.value_size))
750 				strcpy(hv_msg->body.kvp_set.data.key, "");
751 			break;
752 
753 		case KVP_OP_GET:
754 			if (kvp_get_value(hv_msg->kvp_hdr.pool,
755 					hv_msg->body.kvp_set.data.key,
756 					hv_msg->body.kvp_set.data.key_size,
757 					hv_msg->body.kvp_set.data.value,
758 					hv_msg->body.kvp_set.data.value_size))
759 				strcpy(hv_msg->body.kvp_set.data.key, "");
760 			break;
761 
762 		case KVP_OP_DELETE:
763 			if (kvp_key_delete(hv_msg->kvp_hdr.pool,
764 					hv_msg->body.kvp_delete.key,
765 					hv_msg->body.kvp_delete.key_size))
766 				strcpy(hv_msg->body.kvp_delete.key, "");
767 			break;
768 
769 		default:
770 			break;
771 		}
772 
773 		if (hv_msg->kvp_hdr.operation != KVP_OP_ENUMERATE)
774 			goto kvp_done;
775 
776 		/*
777 		 * If the pool is KVP_POOL_AUTO, dynamically generate
778 		 * both the key and the value; if not read from the
779 		 * appropriate pool.
780 		 */
781 		if (hv_msg->kvp_hdr.pool != KVP_POOL_AUTO) {
782 			kvp_pool_enumerate(hv_msg->kvp_hdr.pool,
783 					hv_msg->body.kvp_enum_data.index,
784 					hv_msg->body.kvp_enum_data.data.key,
785 					HV_KVP_EXCHANGE_MAX_KEY_SIZE,
786 					hv_msg->body.kvp_enum_data.data.value,
787 					HV_KVP_EXCHANGE_MAX_VALUE_SIZE);
788 			goto kvp_done;
789 		}
790 
791 		hv_msg = (struct hv_kvp_msg *)incoming_cn_msg->data;
792 		key_name = (char *)hv_msg->body.kvp_enum_data.data.key;
793 		key_value = (char *)hv_msg->body.kvp_enum_data.data.value;
794 
795 		switch (hv_msg->body.kvp_enum_data.index) {
796 		case FullyQualifiedDomainName:
797 			kvp_get_domain_name(key_value,
798 					HV_KVP_EXCHANGE_MAX_VALUE_SIZE);
799 			strcpy(key_name, "FullyQualifiedDomainName");
800 			break;
801 		case IntegrationServicesVersion:
802 			strcpy(key_name, "IntegrationServicesVersion");
803 			strcpy(key_value, lic_version);
804 			break;
805 		case NetworkAddressIPv4:
806 			kvp_get_ip_address(AF_INET, key_value,
807 					HV_KVP_EXCHANGE_MAX_VALUE_SIZE);
808 			strcpy(key_name, "NetworkAddressIPv4");
809 			break;
810 		case NetworkAddressIPv6:
811 			kvp_get_ip_address(AF_INET6, key_value,
812 					HV_KVP_EXCHANGE_MAX_VALUE_SIZE);
813 			strcpy(key_name, "NetworkAddressIPv6");
814 			break;
815 		case OSBuildNumber:
816 			strcpy(key_value, os_build);
817 			strcpy(key_name, "OSBuildNumber");
818 			break;
819 		case OSName:
820 			strcpy(key_value, os_name);
821 			strcpy(key_name, "OSName");
822 			break;
823 		case OSMajorVersion:
824 			strcpy(key_value, os_major);
825 			strcpy(key_name, "OSMajorVersion");
826 			break;
827 		case OSMinorVersion:
828 			strcpy(key_value, os_minor);
829 			strcpy(key_name, "OSMinorVersion");
830 			break;
831 		case OSVersion:
832 			strcpy(key_value, os_build);
833 			strcpy(key_name, "OSVersion");
834 			break;
835 		case ProcessorArchitecture:
836 			strcpy(key_value, processor_arch);
837 			strcpy(key_name, "ProcessorArchitecture");
838 			break;
839 		default:
840 			strcpy(key_value, "Unknown Key");
841 			/*
842 			 * We use a null key name to terminate enumeration.
843 			 */
844 			strcpy(key_name, "");
845 			break;
846 		}
847 		/*
848 		 * Send the value back to the kernel. The response is
849 		 * already in the receive buffer. Update the cn_msg header to
850 		 * reflect the key value that has been added to the message
851 		 */
852 kvp_done:
853 
854 		incoming_cn_msg->id.idx = CN_KVP_IDX;
855 		incoming_cn_msg->id.val = CN_KVP_VAL;
856 		incoming_cn_msg->ack = 0;
857 		incoming_cn_msg->len = sizeof(struct hv_kvp_msg);
858 
859 		len = netlink_send(fd, incoming_cn_msg);
860 		if (len < 0) {
861 			syslog(LOG_ERR, "net_link send failed; error:%d", len);
862 			exit(-1);
863 		}
864 	}
865 
866 }
867