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