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/poll.h> 26 #include <sys/utsname.h> 27 #include <stdio.h> 28 #include <stdlib.h> 29 #include <unistd.h> 30 #include <string.h> 31 #include <ctype.h> 32 #include <errno.h> 33 #include <arpa/inet.h> 34 #include <linux/hyperv.h> 35 #include <ifaddrs.h> 36 #include <netdb.h> 37 #include <syslog.h> 38 #include <sys/stat.h> 39 #include <fcntl.h> 40 #include <dirent.h> 41 #include <net/if.h> 42 #include <getopt.h> 43 44 /* 45 * KVP protocol: The user mode component first registers with the 46 * the kernel component. Subsequently, the kernel component requests, data 47 * for the specified keys. In response to this message the user mode component 48 * fills in the value corresponding to the specified key. We overload the 49 * sequence field in the cn_msg header to define our KVP message types. 50 * 51 * We use this infrastructure for also supporting queries from user mode 52 * application for state that may be maintained in the KVP kernel component. 53 * 54 */ 55 56 57 enum key_index { 58 FullyQualifiedDomainName = 0, 59 IntegrationServicesVersion, /*This key is serviced in the kernel*/ 60 NetworkAddressIPv4, 61 NetworkAddressIPv6, 62 OSBuildNumber, 63 OSName, 64 OSMajorVersion, 65 OSMinorVersion, 66 OSVersion, 67 ProcessorArchitecture 68 }; 69 70 71 enum { 72 IPADDR = 0, 73 NETMASK, 74 GATEWAY, 75 DNS 76 }; 77 78 static int in_hand_shake = 1; 79 80 static char *os_name = ""; 81 static char *os_major = ""; 82 static char *os_minor = ""; 83 static char *processor_arch; 84 static char *os_build; 85 static char *os_version; 86 static char *lic_version = "Unknown version"; 87 static char full_domain_name[HV_KVP_EXCHANGE_MAX_VALUE_SIZE]; 88 static struct utsname uts_buf; 89 90 /* 91 * The location of the interface configuration file. 92 */ 93 94 #define KVP_CONFIG_LOC "/var/lib/hyperv" 95 96 #ifndef KVP_SCRIPTS_PATH 97 #define KVP_SCRIPTS_PATH "/usr/libexec/hypervkvpd/" 98 #endif 99 100 #define MAX_FILE_NAME 100 101 #define ENTRIES_PER_BLOCK 50 102 103 struct kvp_record { 104 char key[HV_KVP_EXCHANGE_MAX_KEY_SIZE]; 105 char value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE]; 106 }; 107 108 struct kvp_file_state { 109 int fd; 110 int num_blocks; 111 struct kvp_record *records; 112 int num_records; 113 char fname[MAX_FILE_NAME]; 114 }; 115 116 static struct kvp_file_state kvp_file_info[KVP_POOL_COUNT]; 117 118 static void kvp_acquire_lock(int pool) 119 { 120 struct flock fl = {F_WRLCK, SEEK_SET, 0, 0, 0}; 121 fl.l_pid = getpid(); 122 123 if (fcntl(kvp_file_info[pool].fd, F_SETLKW, &fl) == -1) { 124 syslog(LOG_ERR, "Failed to acquire the lock pool: %d; error: %d %s", pool, 125 errno, strerror(errno)); 126 exit(EXIT_FAILURE); 127 } 128 } 129 130 static void kvp_release_lock(int pool) 131 { 132 struct flock fl = {F_UNLCK, SEEK_SET, 0, 0, 0}; 133 fl.l_pid = getpid(); 134 135 if (fcntl(kvp_file_info[pool].fd, F_SETLK, &fl) == -1) { 136 syslog(LOG_ERR, "Failed to release the lock pool: %d; error: %d %s", pool, 137 errno, strerror(errno)); 138 exit(EXIT_FAILURE); 139 } 140 } 141 142 static void kvp_update_file(int pool) 143 { 144 FILE *filep; 145 146 /* 147 * We are going to write our in-memory registry out to 148 * disk; acquire the lock first. 149 */ 150 kvp_acquire_lock(pool); 151 152 filep = fopen(kvp_file_info[pool].fname, "we"); 153 if (!filep) { 154 syslog(LOG_ERR, "Failed to open file, pool: %d; error: %d %s", pool, 155 errno, strerror(errno)); 156 kvp_release_lock(pool); 157 exit(EXIT_FAILURE); 158 } 159 160 fwrite(kvp_file_info[pool].records, sizeof(struct kvp_record), 161 kvp_file_info[pool].num_records, filep); 162 163 if (ferror(filep) || fclose(filep)) { 164 kvp_release_lock(pool); 165 syslog(LOG_ERR, "Failed to write file, pool: %d", pool); 166 exit(EXIT_FAILURE); 167 } 168 169 kvp_release_lock(pool); 170 } 171 172 static void kvp_update_mem_state(int pool) 173 { 174 FILE *filep; 175 size_t records_read = 0; 176 struct kvp_record *record = kvp_file_info[pool].records; 177 struct kvp_record *readp; 178 int num_blocks = kvp_file_info[pool].num_blocks; 179 int alloc_unit = sizeof(struct kvp_record) * ENTRIES_PER_BLOCK; 180 181 kvp_acquire_lock(pool); 182 183 filep = fopen(kvp_file_info[pool].fname, "re"); 184 if (!filep) { 185 syslog(LOG_ERR, "Failed to open file, pool: %d; error: %d %s", pool, 186 errno, strerror(errno)); 187 kvp_release_lock(pool); 188 exit(EXIT_FAILURE); 189 } 190 for (;;) { 191 readp = &record[records_read]; 192 records_read += fread(readp, sizeof(struct kvp_record), 193 ENTRIES_PER_BLOCK * num_blocks, 194 filep); 195 196 if (ferror(filep)) { 197 syslog(LOG_ERR, "Failed to read file, pool: %d", pool); 198 exit(EXIT_FAILURE); 199 } 200 201 if (!feof(filep)) { 202 /* 203 * We have more data to read. 204 */ 205 num_blocks++; 206 record = realloc(record, alloc_unit * num_blocks); 207 208 if (record == NULL) { 209 syslog(LOG_ERR, "malloc failed"); 210 exit(EXIT_FAILURE); 211 } 212 continue; 213 } 214 break; 215 } 216 217 kvp_file_info[pool].num_blocks = num_blocks; 218 kvp_file_info[pool].records = record; 219 kvp_file_info[pool].num_records = records_read; 220 221 fclose(filep); 222 kvp_release_lock(pool); 223 } 224 static int kvp_file_init(void) 225 { 226 int fd; 227 FILE *filep; 228 size_t records_read; 229 char *fname; 230 struct kvp_record *record; 231 struct kvp_record *readp; 232 int num_blocks; 233 int i; 234 int alloc_unit = sizeof(struct kvp_record) * ENTRIES_PER_BLOCK; 235 236 if (access(KVP_CONFIG_LOC, F_OK)) { 237 if (mkdir(KVP_CONFIG_LOC, 0755 /* rwxr-xr-x */)) { 238 syslog(LOG_ERR, "Failed to create '%s'; error: %d %s", KVP_CONFIG_LOC, 239 errno, strerror(errno)); 240 exit(EXIT_FAILURE); 241 } 242 } 243 244 for (i = 0; i < KVP_POOL_COUNT; i++) { 245 fname = kvp_file_info[i].fname; 246 records_read = 0; 247 num_blocks = 1; 248 sprintf(fname, "%s/.kvp_pool_%d", KVP_CONFIG_LOC, i); 249 fd = open(fname, O_RDWR | O_CREAT | O_CLOEXEC, 0644 /* rw-r--r-- */); 250 251 if (fd == -1) 252 return 1; 253 254 255 filep = fopen(fname, "re"); 256 if (!filep) { 257 close(fd); 258 return 1; 259 } 260 261 record = malloc(alloc_unit * num_blocks); 262 if (record == NULL) { 263 fclose(filep); 264 close(fd); 265 return 1; 266 } 267 for (;;) { 268 readp = &record[records_read]; 269 records_read += fread(readp, sizeof(struct kvp_record), 270 ENTRIES_PER_BLOCK, 271 filep); 272 273 if (ferror(filep)) { 274 syslog(LOG_ERR, "Failed to read file, pool: %d", 275 i); 276 exit(EXIT_FAILURE); 277 } 278 279 if (!feof(filep)) { 280 /* 281 * We have more data to read. 282 */ 283 num_blocks++; 284 record = realloc(record, alloc_unit * 285 num_blocks); 286 if (record == NULL) { 287 fclose(filep); 288 close(fd); 289 return 1; 290 } 291 continue; 292 } 293 break; 294 } 295 kvp_file_info[i].fd = fd; 296 kvp_file_info[i].num_blocks = num_blocks; 297 kvp_file_info[i].records = record; 298 kvp_file_info[i].num_records = records_read; 299 fclose(filep); 300 301 } 302 303 return 0; 304 } 305 306 static int kvp_key_delete(int pool, const __u8 *key, int key_size) 307 { 308 int i; 309 int j, k; 310 int num_records; 311 struct kvp_record *record; 312 313 /* 314 * First update the in-memory state. 315 */ 316 kvp_update_mem_state(pool); 317 318 num_records = kvp_file_info[pool].num_records; 319 record = kvp_file_info[pool].records; 320 321 for (i = 0; i < num_records; i++) { 322 if (memcmp(key, record[i].key, key_size)) 323 continue; 324 /* 325 * Found a match; just move the remaining 326 * entries up. 327 */ 328 if (i == num_records) { 329 kvp_file_info[pool].num_records--; 330 kvp_update_file(pool); 331 return 0; 332 } 333 334 j = i; 335 k = j + 1; 336 for (; k < num_records; k++) { 337 strcpy(record[j].key, record[k].key); 338 strcpy(record[j].value, record[k].value); 339 j++; 340 } 341 342 kvp_file_info[pool].num_records--; 343 kvp_update_file(pool); 344 return 0; 345 } 346 return 1; 347 } 348 349 static int kvp_key_add_or_modify(int pool, const __u8 *key, int key_size, 350 const __u8 *value, int value_size) 351 { 352 int i; 353 int num_records; 354 struct kvp_record *record; 355 int num_blocks; 356 357 if ((key_size > HV_KVP_EXCHANGE_MAX_KEY_SIZE) || 358 (value_size > HV_KVP_EXCHANGE_MAX_VALUE_SIZE)) 359 return 1; 360 361 /* 362 * First update the in-memory state. 363 */ 364 kvp_update_mem_state(pool); 365 366 num_records = kvp_file_info[pool].num_records; 367 record = kvp_file_info[pool].records; 368 num_blocks = kvp_file_info[pool].num_blocks; 369 370 for (i = 0; i < num_records; i++) { 371 if (memcmp(key, record[i].key, key_size)) 372 continue; 373 /* 374 * Found a match; just update the value - 375 * this is the modify case. 376 */ 377 memcpy(record[i].value, value, value_size); 378 kvp_update_file(pool); 379 return 0; 380 } 381 382 /* 383 * Need to add a new entry; 384 */ 385 if (num_records == (ENTRIES_PER_BLOCK * num_blocks)) { 386 /* Need to allocate a larger array for reg entries. */ 387 record = realloc(record, sizeof(struct kvp_record) * 388 ENTRIES_PER_BLOCK * (num_blocks + 1)); 389 390 if (record == NULL) 391 return 1; 392 kvp_file_info[pool].num_blocks++; 393 394 } 395 memcpy(record[i].value, value, value_size); 396 memcpy(record[i].key, key, key_size); 397 kvp_file_info[pool].records = record; 398 kvp_file_info[pool].num_records++; 399 kvp_update_file(pool); 400 return 0; 401 } 402 403 static int kvp_get_value(int pool, const __u8 *key, int key_size, __u8 *value, 404 int value_size) 405 { 406 int i; 407 int num_records; 408 struct kvp_record *record; 409 410 if ((key_size > HV_KVP_EXCHANGE_MAX_KEY_SIZE) || 411 (value_size > HV_KVP_EXCHANGE_MAX_VALUE_SIZE)) 412 return 1; 413 414 /* 415 * First update the in-memory state. 416 */ 417 kvp_update_mem_state(pool); 418 419 num_records = kvp_file_info[pool].num_records; 420 record = kvp_file_info[pool].records; 421 422 for (i = 0; i < num_records; i++) { 423 if (memcmp(key, record[i].key, key_size)) 424 continue; 425 /* 426 * Found a match; just copy the value out. 427 */ 428 memcpy(value, record[i].value, value_size); 429 return 0; 430 } 431 432 return 1; 433 } 434 435 static int kvp_pool_enumerate(int pool, int index, __u8 *key, int key_size, 436 __u8 *value, int value_size) 437 { 438 struct kvp_record *record; 439 440 /* 441 * First update our in-memory database. 442 */ 443 kvp_update_mem_state(pool); 444 record = kvp_file_info[pool].records; 445 446 if (index >= kvp_file_info[pool].num_records) { 447 return 1; 448 } 449 450 memcpy(key, record[index].key, key_size); 451 memcpy(value, record[index].value, value_size); 452 return 0; 453 } 454 455 456 void kvp_get_os_info(void) 457 { 458 FILE *file; 459 char *p, buf[512]; 460 461 uname(&uts_buf); 462 os_version = uts_buf.release; 463 os_build = strdup(uts_buf.release); 464 465 os_name = uts_buf.sysname; 466 processor_arch = uts_buf.machine; 467 468 /* 469 * The current windows host (win7) expects the build 470 * string to be of the form: x.y.z 471 * Strip additional information we may have. 472 */ 473 p = strchr(os_version, '-'); 474 if (p) 475 *p = '\0'; 476 477 /* 478 * Parse the /etc/os-release file if present: 479 * http://www.freedesktop.org/software/systemd/man/os-release.html 480 */ 481 file = fopen("/etc/os-release", "r"); 482 if (file != NULL) { 483 while (fgets(buf, sizeof(buf), file)) { 484 char *value, *q; 485 486 /* Ignore comments */ 487 if (buf[0] == '#') 488 continue; 489 490 /* Split into name=value */ 491 p = strchr(buf, '='); 492 if (!p) 493 continue; 494 *p++ = 0; 495 496 /* Remove quotes and newline; un-escape */ 497 value = p; 498 q = p; 499 while (*p) { 500 if (*p == '\\') { 501 ++p; 502 if (!*p) 503 break; 504 *q++ = *p++; 505 } else if (*p == '\'' || *p == '"' || 506 *p == '\n') { 507 ++p; 508 } else { 509 *q++ = *p++; 510 } 511 } 512 *q = 0; 513 514 if (!strcmp(buf, "NAME")) { 515 p = strdup(value); 516 if (!p) 517 break; 518 os_name = p; 519 } else if (!strcmp(buf, "VERSION_ID")) { 520 p = strdup(value); 521 if (!p) 522 break; 523 os_major = p; 524 } 525 } 526 fclose(file); 527 return; 528 } 529 530 /* Fallback for older RH/SUSE releases */ 531 file = fopen("/etc/SuSE-release", "r"); 532 if (file != NULL) 533 goto kvp_osinfo_found; 534 file = fopen("/etc/redhat-release", "r"); 535 if (file != NULL) 536 goto kvp_osinfo_found; 537 538 /* 539 * We don't have information about the os. 540 */ 541 return; 542 543 kvp_osinfo_found: 544 /* up to three lines */ 545 p = fgets(buf, sizeof(buf), file); 546 if (p) { 547 p = strchr(buf, '\n'); 548 if (p) 549 *p = '\0'; 550 p = strdup(buf); 551 if (!p) 552 goto done; 553 os_name = p; 554 555 /* second line */ 556 p = fgets(buf, sizeof(buf), file); 557 if (p) { 558 p = strchr(buf, '\n'); 559 if (p) 560 *p = '\0'; 561 p = strdup(buf); 562 if (!p) 563 goto done; 564 os_major = p; 565 566 /* third line */ 567 p = fgets(buf, sizeof(buf), file); 568 if (p) { 569 p = strchr(buf, '\n'); 570 if (p) 571 *p = '\0'; 572 p = strdup(buf); 573 if (p) 574 os_minor = p; 575 } 576 } 577 } 578 579 done: 580 fclose(file); 581 return; 582 } 583 584 585 586 /* 587 * Retrieve an interface name corresponding to the specified guid. 588 * If there is a match, the function returns a pointer 589 * to the interface name and if not, a NULL is returned. 590 * If a match is found, the caller is responsible for 591 * freeing the memory. 592 */ 593 594 static char *kvp_get_if_name(char *guid) 595 { 596 DIR *dir; 597 struct dirent *entry; 598 FILE *file; 599 char *p, *q, *x; 600 char *if_name = NULL; 601 char buf[256]; 602 char *kvp_net_dir = "/sys/class/net/"; 603 char dev_id[256]; 604 605 dir = opendir(kvp_net_dir); 606 if (dir == NULL) 607 return NULL; 608 609 snprintf(dev_id, sizeof(dev_id), "%s", kvp_net_dir); 610 q = dev_id + strlen(kvp_net_dir); 611 612 while ((entry = readdir(dir)) != NULL) { 613 /* 614 * Set the state for the next pass. 615 */ 616 *q = '\0'; 617 strcat(dev_id, entry->d_name); 618 strcat(dev_id, "/device/device_id"); 619 620 file = fopen(dev_id, "r"); 621 if (file == NULL) 622 continue; 623 624 p = fgets(buf, sizeof(buf), file); 625 if (p) { 626 x = strchr(p, '\n'); 627 if (x) 628 *x = '\0'; 629 630 if (!strcmp(p, guid)) { 631 /* 632 * Found the guid match; return the interface 633 * name. The caller will free the memory. 634 */ 635 if_name = strdup(entry->d_name); 636 fclose(file); 637 break; 638 } 639 } 640 fclose(file); 641 } 642 643 closedir(dir); 644 return if_name; 645 } 646 647 /* 648 * Retrieve the MAC address given the interface name. 649 */ 650 651 static char *kvp_if_name_to_mac(char *if_name) 652 { 653 FILE *file; 654 char *p, *x; 655 char buf[256]; 656 char addr_file[256]; 657 unsigned int i; 658 char *mac_addr = NULL; 659 660 snprintf(addr_file, sizeof(addr_file), "%s%s%s", "/sys/class/net/", 661 if_name, "/address"); 662 663 file = fopen(addr_file, "r"); 664 if (file == NULL) 665 return NULL; 666 667 p = fgets(buf, sizeof(buf), file); 668 if (p) { 669 x = strchr(p, '\n'); 670 if (x) 671 *x = '\0'; 672 for (i = 0; i < strlen(p); i++) 673 p[i] = toupper(p[i]); 674 mac_addr = strdup(p); 675 } 676 677 fclose(file); 678 return mac_addr; 679 } 680 681 682 /* 683 * Retrieve the interface name given tha MAC address. 684 */ 685 686 static char *kvp_mac_to_if_name(char *mac) 687 { 688 DIR *dir; 689 struct dirent *entry; 690 FILE *file; 691 char *p, *q, *x; 692 char *if_name = NULL; 693 char buf[256]; 694 char *kvp_net_dir = "/sys/class/net/"; 695 char dev_id[256]; 696 unsigned int i; 697 698 dir = opendir(kvp_net_dir); 699 if (dir == NULL) 700 return NULL; 701 702 snprintf(dev_id, sizeof(dev_id), "%s", kvp_net_dir); 703 q = dev_id + strlen(kvp_net_dir); 704 705 while ((entry = readdir(dir)) != NULL) { 706 /* 707 * Set the state for the next pass. 708 */ 709 *q = '\0'; 710 711 strcat(dev_id, entry->d_name); 712 strcat(dev_id, "/address"); 713 714 file = fopen(dev_id, "r"); 715 if (file == NULL) 716 continue; 717 718 p = fgets(buf, sizeof(buf), file); 719 if (p) { 720 x = strchr(p, '\n'); 721 if (x) 722 *x = '\0'; 723 724 for (i = 0; i < strlen(p); i++) 725 p[i] = toupper(p[i]); 726 727 if (!strcmp(p, mac)) { 728 /* 729 * Found the MAC match; return the interface 730 * name. The caller will free the memory. 731 */ 732 if_name = strdup(entry->d_name); 733 fclose(file); 734 break; 735 } 736 } 737 fclose(file); 738 } 739 740 closedir(dir); 741 return if_name; 742 } 743 744 745 static void kvp_process_ipconfig_file(char *cmd, 746 char *config_buf, unsigned int len, 747 int element_size, int offset) 748 { 749 char buf[256]; 750 char *p; 751 char *x; 752 FILE *file; 753 754 /* 755 * First execute the command. 756 */ 757 file = popen(cmd, "r"); 758 if (file == NULL) 759 return; 760 761 if (offset == 0) 762 memset(config_buf, 0, len); 763 while ((p = fgets(buf, sizeof(buf), file)) != NULL) { 764 if (len < strlen(config_buf) + element_size + 1) 765 break; 766 767 x = strchr(p, '\n'); 768 if (x) 769 *x = '\0'; 770 771 strcat(config_buf, p); 772 strcat(config_buf, ";"); 773 } 774 pclose(file); 775 } 776 777 static void kvp_get_ipconfig_info(char *if_name, 778 struct hv_kvp_ipaddr_value *buffer) 779 { 780 char cmd[512]; 781 char dhcp_info[128]; 782 char *p; 783 FILE *file; 784 785 /* 786 * Get the address of default gateway (ipv4). 787 */ 788 sprintf(cmd, "%s %s", "ip route show dev", if_name); 789 strcat(cmd, " | awk '/default/ {print $3 }'"); 790 791 /* 792 * Execute the command to gather gateway info. 793 */ 794 kvp_process_ipconfig_file(cmd, (char *)buffer->gate_way, 795 (MAX_GATEWAY_SIZE * 2), INET_ADDRSTRLEN, 0); 796 797 /* 798 * Get the address of default gateway (ipv6). 799 */ 800 sprintf(cmd, "%s %s", "ip -f inet6 route show dev", if_name); 801 strcat(cmd, " | awk '/default/ {print $3 }'"); 802 803 /* 804 * Execute the command to gather gateway info (ipv6). 805 */ 806 kvp_process_ipconfig_file(cmd, (char *)buffer->gate_way, 807 (MAX_GATEWAY_SIZE * 2), INET6_ADDRSTRLEN, 1); 808 809 810 /* 811 * Gather the DNS state. 812 * Since there is no standard way to get this information 813 * across various distributions of interest; we just invoke 814 * an external script that needs to be ported across distros 815 * of interest. 816 * 817 * Following is the expected format of the information from the script: 818 * 819 * ipaddr1 (nameserver1) 820 * ipaddr2 (nameserver2) 821 * . 822 * . 823 */ 824 825 sprintf(cmd, KVP_SCRIPTS_PATH "%s", "hv_get_dns_info"); 826 827 /* 828 * Execute the command to gather DNS info. 829 */ 830 kvp_process_ipconfig_file(cmd, (char *)buffer->dns_addr, 831 (MAX_IP_ADDR_SIZE * 2), INET_ADDRSTRLEN, 0); 832 833 /* 834 * Gather the DHCP state. 835 * We will gather this state by invoking an external script. 836 * The parameter to the script is the interface name. 837 * Here is the expected output: 838 * 839 * Enabled: DHCP enabled. 840 */ 841 842 sprintf(cmd, KVP_SCRIPTS_PATH "%s %s", "hv_get_dhcp_info", if_name); 843 844 file = popen(cmd, "r"); 845 if (file == NULL) 846 return; 847 848 p = fgets(dhcp_info, sizeof(dhcp_info), file); 849 if (p == NULL) { 850 pclose(file); 851 return; 852 } 853 854 if (!strncmp(p, "Enabled", 7)) 855 buffer->dhcp_enabled = 1; 856 else 857 buffer->dhcp_enabled = 0; 858 859 pclose(file); 860 } 861 862 863 static unsigned int hweight32(unsigned int *w) 864 { 865 unsigned int res = *w - ((*w >> 1) & 0x55555555); 866 res = (res & 0x33333333) + ((res >> 2) & 0x33333333); 867 res = (res + (res >> 4)) & 0x0F0F0F0F; 868 res = res + (res >> 8); 869 return (res + (res >> 16)) & 0x000000FF; 870 } 871 872 static int kvp_process_ip_address(void *addrp, 873 int family, char *buffer, 874 int length, int *offset) 875 { 876 struct sockaddr_in *addr; 877 struct sockaddr_in6 *addr6; 878 int addr_length; 879 char tmp[50]; 880 const char *str; 881 882 if (family == AF_INET) { 883 addr = (struct sockaddr_in *)addrp; 884 str = inet_ntop(family, &addr->sin_addr, tmp, 50); 885 addr_length = INET_ADDRSTRLEN; 886 } else { 887 addr6 = (struct sockaddr_in6 *)addrp; 888 str = inet_ntop(family, &addr6->sin6_addr.s6_addr, tmp, 50); 889 addr_length = INET6_ADDRSTRLEN; 890 } 891 892 if ((length - *offset) < addr_length + 2) 893 return HV_E_FAIL; 894 if (str == NULL) { 895 strcpy(buffer, "inet_ntop failed\n"); 896 return HV_E_FAIL; 897 } 898 if (*offset == 0) 899 strcpy(buffer, tmp); 900 else { 901 strcat(buffer, ";"); 902 strcat(buffer, tmp); 903 } 904 905 *offset += strlen(str) + 1; 906 907 return 0; 908 } 909 910 static int 911 kvp_get_ip_info(int family, char *if_name, int op, 912 void *out_buffer, unsigned int length) 913 { 914 struct ifaddrs *ifap; 915 struct ifaddrs *curp; 916 int offset = 0; 917 int sn_offset = 0; 918 int error = 0; 919 char *buffer; 920 struct hv_kvp_ipaddr_value *ip_buffer; 921 char cidr_mask[5]; /* /xyz */ 922 int weight; 923 int i; 924 unsigned int *w; 925 char *sn_str; 926 struct sockaddr_in6 *addr6; 927 928 if (op == KVP_OP_ENUMERATE) { 929 buffer = out_buffer; 930 } else { 931 ip_buffer = out_buffer; 932 buffer = (char *)ip_buffer->ip_addr; 933 ip_buffer->addr_family = 0; 934 } 935 /* 936 * On entry into this function, the buffer is capable of holding the 937 * maximum key value. 938 */ 939 940 if (getifaddrs(&ifap)) { 941 strcpy(buffer, "getifaddrs failed\n"); 942 return HV_E_FAIL; 943 } 944 945 curp = ifap; 946 while (curp != NULL) { 947 if (curp->ifa_addr == NULL) { 948 curp = curp->ifa_next; 949 continue; 950 } 951 952 if ((if_name != NULL) && 953 (strncmp(curp->ifa_name, if_name, strlen(if_name)))) { 954 /* 955 * We want info about a specific interface; 956 * just continue. 957 */ 958 curp = curp->ifa_next; 959 continue; 960 } 961 962 /* 963 * We only support two address families: AF_INET and AF_INET6. 964 * If a family value of 0 is specified, we collect both 965 * supported address families; if not we gather info on 966 * the specified address family. 967 */ 968 if ((((family != 0) && 969 (curp->ifa_addr->sa_family != family))) || 970 (curp->ifa_flags & IFF_LOOPBACK)) { 971 curp = curp->ifa_next; 972 continue; 973 } 974 if ((curp->ifa_addr->sa_family != AF_INET) && 975 (curp->ifa_addr->sa_family != AF_INET6)) { 976 curp = curp->ifa_next; 977 continue; 978 } 979 980 if (op == KVP_OP_GET_IP_INFO) { 981 /* 982 * Gather info other than the IP address. 983 * IP address info will be gathered later. 984 */ 985 if (curp->ifa_addr->sa_family == AF_INET) { 986 ip_buffer->addr_family |= ADDR_FAMILY_IPV4; 987 /* 988 * Get subnet info. 989 */ 990 error = kvp_process_ip_address( 991 curp->ifa_netmask, 992 AF_INET, 993 (char *) 994 ip_buffer->sub_net, 995 length, 996 &sn_offset); 997 if (error) 998 goto gather_ipaddr; 999 } else { 1000 ip_buffer->addr_family |= ADDR_FAMILY_IPV6; 1001 1002 /* 1003 * Get subnet info in CIDR format. 1004 */ 1005 weight = 0; 1006 sn_str = (char *)ip_buffer->sub_net; 1007 addr6 = (struct sockaddr_in6 *) 1008 curp->ifa_netmask; 1009 w = addr6->sin6_addr.s6_addr32; 1010 1011 for (i = 0; i < 4; i++) 1012 weight += hweight32(&w[i]); 1013 1014 sprintf(cidr_mask, "/%d", weight); 1015 if (length < sn_offset + strlen(cidr_mask) + 1) 1016 goto gather_ipaddr; 1017 1018 if (sn_offset == 0) 1019 strcpy(sn_str, cidr_mask); 1020 else { 1021 strcat((char *)ip_buffer->sub_net, ";"); 1022 strcat(sn_str, cidr_mask); 1023 } 1024 sn_offset += strlen(sn_str) + 1; 1025 } 1026 1027 /* 1028 * Collect other ip related configuration info. 1029 */ 1030 1031 kvp_get_ipconfig_info(if_name, ip_buffer); 1032 } 1033 1034 gather_ipaddr: 1035 error = kvp_process_ip_address(curp->ifa_addr, 1036 curp->ifa_addr->sa_family, 1037 buffer, 1038 length, &offset); 1039 if (error) 1040 goto getaddr_done; 1041 1042 curp = curp->ifa_next; 1043 } 1044 1045 getaddr_done: 1046 freeifaddrs(ifap); 1047 return error; 1048 } 1049 1050 1051 static int expand_ipv6(char *addr, int type) 1052 { 1053 int ret; 1054 struct in6_addr v6_addr; 1055 1056 ret = inet_pton(AF_INET6, addr, &v6_addr); 1057 1058 if (ret != 1) { 1059 if (type == NETMASK) 1060 return 1; 1061 return 0; 1062 } 1063 1064 sprintf(addr, "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:" 1065 "%02x%02x:%02x%02x:%02x%02x", 1066 (int)v6_addr.s6_addr[0], (int)v6_addr.s6_addr[1], 1067 (int)v6_addr.s6_addr[2], (int)v6_addr.s6_addr[3], 1068 (int)v6_addr.s6_addr[4], (int)v6_addr.s6_addr[5], 1069 (int)v6_addr.s6_addr[6], (int)v6_addr.s6_addr[7], 1070 (int)v6_addr.s6_addr[8], (int)v6_addr.s6_addr[9], 1071 (int)v6_addr.s6_addr[10], (int)v6_addr.s6_addr[11], 1072 (int)v6_addr.s6_addr[12], (int)v6_addr.s6_addr[13], 1073 (int)v6_addr.s6_addr[14], (int)v6_addr.s6_addr[15]); 1074 1075 return 1; 1076 1077 } 1078 1079 static int is_ipv4(char *addr) 1080 { 1081 int ret; 1082 struct in_addr ipv4_addr; 1083 1084 ret = inet_pton(AF_INET, addr, &ipv4_addr); 1085 1086 if (ret == 1) 1087 return 1; 1088 return 0; 1089 } 1090 1091 static int parse_ip_val_buffer(char *in_buf, int *offset, 1092 char *out_buf, int out_len) 1093 { 1094 char *x; 1095 char *start; 1096 1097 /* 1098 * in_buf has sequence of characters that are seperated by 1099 * the character ';'. The last sequence does not have the 1100 * terminating ";" character. 1101 */ 1102 start = in_buf + *offset; 1103 1104 x = strchr(start, ';'); 1105 if (x) 1106 *x = 0; 1107 else 1108 x = start + strlen(start); 1109 1110 if (strlen(start) != 0) { 1111 int i = 0; 1112 /* 1113 * Get rid of leading spaces. 1114 */ 1115 while (start[i] == ' ') 1116 i++; 1117 1118 if ((x - start) <= out_len) { 1119 strcpy(out_buf, (start + i)); 1120 *offset += (x - start) + 1; 1121 return 1; 1122 } 1123 } 1124 return 0; 1125 } 1126 1127 static int kvp_write_file(FILE *f, char *s1, char *s2, char *s3) 1128 { 1129 int ret; 1130 1131 ret = fprintf(f, "%s%s%s%s\n", s1, s2, "=", s3); 1132 1133 if (ret < 0) 1134 return HV_E_FAIL; 1135 1136 return 0; 1137 } 1138 1139 1140 static int process_ip_string(FILE *f, char *ip_string, int type) 1141 { 1142 int error = 0; 1143 char addr[INET6_ADDRSTRLEN]; 1144 int i = 0; 1145 int j = 0; 1146 char str[256]; 1147 char sub_str[10]; 1148 int offset = 0; 1149 1150 memset(addr, 0, sizeof(addr)); 1151 1152 while (parse_ip_val_buffer(ip_string, &offset, addr, 1153 (MAX_IP_ADDR_SIZE * 2))) { 1154 1155 sub_str[0] = 0; 1156 if (is_ipv4(addr)) { 1157 switch (type) { 1158 case IPADDR: 1159 snprintf(str, sizeof(str), "%s", "IPADDR"); 1160 break; 1161 case NETMASK: 1162 snprintf(str, sizeof(str), "%s", "NETMASK"); 1163 break; 1164 case GATEWAY: 1165 snprintf(str, sizeof(str), "%s", "GATEWAY"); 1166 break; 1167 case DNS: 1168 snprintf(str, sizeof(str), "%s", "DNS"); 1169 break; 1170 } 1171 1172 if (type == DNS) { 1173 snprintf(sub_str, sizeof(sub_str), "%d", ++i); 1174 } else if (type == GATEWAY && i == 0) { 1175 ++i; 1176 } else { 1177 snprintf(sub_str, sizeof(sub_str), "%d", i++); 1178 } 1179 1180 1181 } else if (expand_ipv6(addr, type)) { 1182 switch (type) { 1183 case IPADDR: 1184 snprintf(str, sizeof(str), "%s", "IPV6ADDR"); 1185 break; 1186 case NETMASK: 1187 snprintf(str, sizeof(str), "%s", "IPV6NETMASK"); 1188 break; 1189 case GATEWAY: 1190 snprintf(str, sizeof(str), "%s", 1191 "IPV6_DEFAULTGW"); 1192 break; 1193 case DNS: 1194 snprintf(str, sizeof(str), "%s", "DNS"); 1195 break; 1196 } 1197 1198 if (type == DNS) { 1199 snprintf(sub_str, sizeof(sub_str), "%d", ++i); 1200 } else if (j == 0) { 1201 ++j; 1202 } else { 1203 snprintf(sub_str, sizeof(sub_str), "_%d", j++); 1204 } 1205 } else { 1206 return HV_INVALIDARG; 1207 } 1208 1209 error = kvp_write_file(f, str, sub_str, addr); 1210 if (error) 1211 return error; 1212 memset(addr, 0, sizeof(addr)); 1213 } 1214 1215 return 0; 1216 } 1217 1218 static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val) 1219 { 1220 int error = 0; 1221 char if_file[128]; 1222 FILE *file; 1223 char cmd[512]; 1224 char *mac_addr; 1225 1226 /* 1227 * Set the configuration for the specified interface with 1228 * the information provided. Since there is no standard 1229 * way to configure an interface, we will have an external 1230 * script that does the job of configuring the interface and 1231 * flushing the configuration. 1232 * 1233 * The parameters passed to this external script are: 1234 * 1. A configuration file that has the specified configuration. 1235 * 1236 * We will embed the name of the interface in the configuration 1237 * file: ifcfg-ethx (where ethx is the interface name). 1238 * 1239 * The information provided here may be more than what is needed 1240 * in a given distro to configure the interface and so are free 1241 * ignore information that may not be relevant. 1242 * 1243 * Here is the format of the ip configuration file: 1244 * 1245 * HWADDR=macaddr 1246 * DEVICE=interface name 1247 * BOOTPROTO=<protocol> (where <protocol> is "dhcp" if DHCP is configured 1248 * or "none" if no boot-time protocol should be used) 1249 * 1250 * IPADDR0=ipaddr1 1251 * IPADDR1=ipaddr2 1252 * IPADDRx=ipaddry (where y = x + 1) 1253 * 1254 * NETMASK0=netmask1 1255 * NETMASKx=netmasky (where y = x + 1) 1256 * 1257 * GATEWAY=ipaddr1 1258 * GATEWAYx=ipaddry (where y = x + 1) 1259 * 1260 * DNSx=ipaddrx (where first DNS address is tagged as DNS1 etc) 1261 * 1262 * IPV6 addresses will be tagged as IPV6ADDR, IPV6 gateway will be 1263 * tagged as IPV6_DEFAULTGW and IPV6 NETMASK will be tagged as 1264 * IPV6NETMASK. 1265 * 1266 * The host can specify multiple ipv4 and ipv6 addresses to be 1267 * configured for the interface. Furthermore, the configuration 1268 * needs to be persistent. A subsequent GET call on the interface 1269 * is expected to return the configuration that is set via the SET 1270 * call. 1271 */ 1272 1273 snprintf(if_file, sizeof(if_file), "%s%s%s", KVP_CONFIG_LOC, 1274 "/ifcfg-", if_name); 1275 1276 file = fopen(if_file, "w"); 1277 1278 if (file == NULL) { 1279 syslog(LOG_ERR, "Failed to open config file; error: %d %s", 1280 errno, strerror(errno)); 1281 return HV_E_FAIL; 1282 } 1283 1284 /* 1285 * First write out the MAC address. 1286 */ 1287 1288 mac_addr = kvp_if_name_to_mac(if_name); 1289 if (mac_addr == NULL) { 1290 error = HV_E_FAIL; 1291 goto setval_error; 1292 } 1293 1294 error = kvp_write_file(file, "HWADDR", "", mac_addr); 1295 free(mac_addr); 1296 if (error) 1297 goto setval_error; 1298 1299 error = kvp_write_file(file, "DEVICE", "", if_name); 1300 if (error) 1301 goto setval_error; 1302 1303 /* 1304 * The dhcp_enabled flag is only for IPv4. In the case the host only 1305 * injects an IPv6 address, the flag is true, but we still need to 1306 * proceed to parse and pass the IPv6 information to the 1307 * disto-specific script hv_set_ifconfig. 1308 */ 1309 if (new_val->dhcp_enabled) { 1310 error = kvp_write_file(file, "BOOTPROTO", "", "dhcp"); 1311 if (error) 1312 goto setval_error; 1313 1314 } else { 1315 error = kvp_write_file(file, "BOOTPROTO", "", "none"); 1316 if (error) 1317 goto setval_error; 1318 } 1319 1320 /* 1321 * Write the configuration for ipaddress, netmask, gateway and 1322 * name servers. 1323 */ 1324 1325 error = process_ip_string(file, (char *)new_val->ip_addr, IPADDR); 1326 if (error) 1327 goto setval_error; 1328 1329 error = process_ip_string(file, (char *)new_val->sub_net, NETMASK); 1330 if (error) 1331 goto setval_error; 1332 1333 error = process_ip_string(file, (char *)new_val->gate_way, GATEWAY); 1334 if (error) 1335 goto setval_error; 1336 1337 error = process_ip_string(file, (char *)new_val->dns_addr, DNS); 1338 if (error) 1339 goto setval_error; 1340 1341 fclose(file); 1342 1343 /* 1344 * Now that we have populated the configuration file, 1345 * invoke the external script to do its magic. 1346 */ 1347 1348 snprintf(cmd, sizeof(cmd), KVP_SCRIPTS_PATH "%s %s", 1349 "hv_set_ifconfig", if_file); 1350 if (system(cmd)) { 1351 syslog(LOG_ERR, "Failed to execute cmd '%s'; error: %d %s", 1352 cmd, errno, strerror(errno)); 1353 return HV_E_FAIL; 1354 } 1355 return 0; 1356 1357 setval_error: 1358 syslog(LOG_ERR, "Failed to write config file"); 1359 fclose(file); 1360 return error; 1361 } 1362 1363 1364 static void 1365 kvp_get_domain_name(char *buffer, int length) 1366 { 1367 struct addrinfo hints, *info ; 1368 int error = 0; 1369 1370 gethostname(buffer, length); 1371 memset(&hints, 0, sizeof(hints)); 1372 hints.ai_family = AF_INET; /*Get only ipv4 addrinfo. */ 1373 hints.ai_socktype = SOCK_STREAM; 1374 hints.ai_flags = AI_CANONNAME; 1375 1376 error = getaddrinfo(buffer, NULL, &hints, &info); 1377 if (error != 0) { 1378 snprintf(buffer, length, "getaddrinfo failed: 0x%x %s", 1379 error, gai_strerror(error)); 1380 return; 1381 } 1382 snprintf(buffer, length, "%s", info->ai_canonname); 1383 freeaddrinfo(info); 1384 } 1385 1386 void print_usage(char *argv[]) 1387 { 1388 fprintf(stderr, "Usage: %s [options]\n" 1389 "Options are:\n" 1390 " -n, --no-daemon stay in foreground, don't daemonize\n" 1391 " -h, --help print this help\n", argv[0]); 1392 } 1393 1394 int main(int argc, char *argv[]) 1395 { 1396 int kvp_fd, len; 1397 int error; 1398 struct pollfd pfd; 1399 char *p; 1400 struct hv_kvp_msg hv_msg[1]; 1401 char *key_value; 1402 char *key_name; 1403 int op; 1404 int pool; 1405 char *if_name; 1406 struct hv_kvp_ipaddr_value *kvp_ip_val; 1407 int daemonize = 1, long_index = 0, opt; 1408 1409 static struct option long_options[] = { 1410 {"help", no_argument, 0, 'h' }, 1411 {"no-daemon", no_argument, 0, 'n' }, 1412 {0, 0, 0, 0 } 1413 }; 1414 1415 while ((opt = getopt_long(argc, argv, "hn", long_options, 1416 &long_index)) != -1) { 1417 switch (opt) { 1418 case 'n': 1419 daemonize = 0; 1420 break; 1421 case 'h': 1422 default: 1423 print_usage(argv); 1424 exit(EXIT_FAILURE); 1425 } 1426 } 1427 1428 if (daemonize && daemon(1, 0)) 1429 return 1; 1430 1431 openlog("KVP", 0, LOG_USER); 1432 syslog(LOG_INFO, "KVP starting; pid is:%d", getpid()); 1433 1434 kvp_fd = open("/dev/vmbus/hv_kvp", O_RDWR | O_CLOEXEC); 1435 1436 if (kvp_fd < 0) { 1437 syslog(LOG_ERR, "open /dev/vmbus/hv_kvp failed; error: %d %s", 1438 errno, strerror(errno)); 1439 exit(EXIT_FAILURE); 1440 } 1441 1442 /* 1443 * Retrieve OS release information. 1444 */ 1445 kvp_get_os_info(); 1446 /* 1447 * Cache Fully Qualified Domain Name because getaddrinfo takes an 1448 * unpredictable amount of time to finish. 1449 */ 1450 kvp_get_domain_name(full_domain_name, sizeof(full_domain_name)); 1451 1452 if (kvp_file_init()) { 1453 syslog(LOG_ERR, "Failed to initialize the pools"); 1454 exit(EXIT_FAILURE); 1455 } 1456 1457 /* 1458 * Register ourselves with the kernel. 1459 */ 1460 hv_msg->kvp_hdr.operation = KVP_OP_REGISTER1; 1461 len = write(kvp_fd, hv_msg, sizeof(struct hv_kvp_msg)); 1462 if (len != sizeof(struct hv_kvp_msg)) { 1463 syslog(LOG_ERR, "registration to kernel failed; error: %d %s", 1464 errno, strerror(errno)); 1465 close(kvp_fd); 1466 exit(EXIT_FAILURE); 1467 } 1468 1469 pfd.fd = kvp_fd; 1470 1471 while (1) { 1472 pfd.events = POLLIN; 1473 pfd.revents = 0; 1474 1475 if (poll(&pfd, 1, -1) < 0) { 1476 syslog(LOG_ERR, "poll failed; error: %d %s", errno, strerror(errno)); 1477 if (errno == EINVAL) { 1478 close(kvp_fd); 1479 exit(EXIT_FAILURE); 1480 } 1481 else 1482 continue; 1483 } 1484 1485 len = read(kvp_fd, hv_msg, sizeof(struct hv_kvp_msg)); 1486 1487 if (len != sizeof(struct hv_kvp_msg)) { 1488 syslog(LOG_ERR, "read failed; error:%d %s", 1489 errno, strerror(errno)); 1490 1491 close(kvp_fd); 1492 return EXIT_FAILURE; 1493 } 1494 1495 /* 1496 * We will use the KVP header information to pass back 1497 * the error from this daemon. So, first copy the state 1498 * and set the error code to success. 1499 */ 1500 op = hv_msg->kvp_hdr.operation; 1501 pool = hv_msg->kvp_hdr.pool; 1502 hv_msg->error = HV_S_OK; 1503 1504 if ((in_hand_shake) && (op == KVP_OP_REGISTER1)) { 1505 /* 1506 * Driver is registering with us; stash away the version 1507 * information. 1508 */ 1509 in_hand_shake = 0; 1510 p = (char *)hv_msg->body.kvp_register.version; 1511 lic_version = malloc(strlen(p) + 1); 1512 if (lic_version) { 1513 strcpy(lic_version, p); 1514 syslog(LOG_INFO, "KVP LIC Version: %s", 1515 lic_version); 1516 } else { 1517 syslog(LOG_ERR, "malloc failed"); 1518 } 1519 continue; 1520 } 1521 1522 switch (op) { 1523 case KVP_OP_GET_IP_INFO: 1524 kvp_ip_val = &hv_msg->body.kvp_ip_val; 1525 if_name = 1526 kvp_mac_to_if_name((char *)kvp_ip_val->adapter_id); 1527 1528 if (if_name == NULL) { 1529 /* 1530 * We could not map the mac address to an 1531 * interface name; return error. 1532 */ 1533 hv_msg->error = HV_E_FAIL; 1534 break; 1535 } 1536 error = kvp_get_ip_info( 1537 0, if_name, KVP_OP_GET_IP_INFO, 1538 kvp_ip_val, 1539 (MAX_IP_ADDR_SIZE * 2)); 1540 1541 if (error) 1542 hv_msg->error = error; 1543 1544 free(if_name); 1545 break; 1546 1547 case KVP_OP_SET_IP_INFO: 1548 kvp_ip_val = &hv_msg->body.kvp_ip_val; 1549 if_name = kvp_get_if_name( 1550 (char *)kvp_ip_val->adapter_id); 1551 if (if_name == NULL) { 1552 /* 1553 * We could not map the guid to an 1554 * interface name; return error. 1555 */ 1556 hv_msg->error = HV_GUID_NOTFOUND; 1557 break; 1558 } 1559 error = kvp_set_ip_info(if_name, kvp_ip_val); 1560 if (error) 1561 hv_msg->error = error; 1562 1563 free(if_name); 1564 break; 1565 1566 case KVP_OP_SET: 1567 if (kvp_key_add_or_modify(pool, 1568 hv_msg->body.kvp_set.data.key, 1569 hv_msg->body.kvp_set.data.key_size, 1570 hv_msg->body.kvp_set.data.value, 1571 hv_msg->body.kvp_set.data.value_size)) 1572 hv_msg->error = HV_S_CONT; 1573 break; 1574 1575 case KVP_OP_GET: 1576 if (kvp_get_value(pool, 1577 hv_msg->body.kvp_set.data.key, 1578 hv_msg->body.kvp_set.data.key_size, 1579 hv_msg->body.kvp_set.data.value, 1580 hv_msg->body.kvp_set.data.value_size)) 1581 hv_msg->error = HV_S_CONT; 1582 break; 1583 1584 case KVP_OP_DELETE: 1585 if (kvp_key_delete(pool, 1586 hv_msg->body.kvp_delete.key, 1587 hv_msg->body.kvp_delete.key_size)) 1588 hv_msg->error = HV_S_CONT; 1589 break; 1590 1591 default: 1592 break; 1593 } 1594 1595 if (op != KVP_OP_ENUMERATE) 1596 goto kvp_done; 1597 1598 /* 1599 * If the pool is KVP_POOL_AUTO, dynamically generate 1600 * both the key and the value; if not read from the 1601 * appropriate pool. 1602 */ 1603 if (pool != KVP_POOL_AUTO) { 1604 if (kvp_pool_enumerate(pool, 1605 hv_msg->body.kvp_enum_data.index, 1606 hv_msg->body.kvp_enum_data.data.key, 1607 HV_KVP_EXCHANGE_MAX_KEY_SIZE, 1608 hv_msg->body.kvp_enum_data.data.value, 1609 HV_KVP_EXCHANGE_MAX_VALUE_SIZE)) 1610 hv_msg->error = HV_S_CONT; 1611 goto kvp_done; 1612 } 1613 1614 key_name = (char *)hv_msg->body.kvp_enum_data.data.key; 1615 key_value = (char *)hv_msg->body.kvp_enum_data.data.value; 1616 1617 switch (hv_msg->body.kvp_enum_data.index) { 1618 case FullyQualifiedDomainName: 1619 strcpy(key_value, full_domain_name); 1620 strcpy(key_name, "FullyQualifiedDomainName"); 1621 break; 1622 case IntegrationServicesVersion: 1623 strcpy(key_name, "IntegrationServicesVersion"); 1624 strcpy(key_value, lic_version); 1625 break; 1626 case NetworkAddressIPv4: 1627 kvp_get_ip_info(AF_INET, NULL, KVP_OP_ENUMERATE, 1628 key_value, HV_KVP_EXCHANGE_MAX_VALUE_SIZE); 1629 strcpy(key_name, "NetworkAddressIPv4"); 1630 break; 1631 case NetworkAddressIPv6: 1632 kvp_get_ip_info(AF_INET6, NULL, KVP_OP_ENUMERATE, 1633 key_value, HV_KVP_EXCHANGE_MAX_VALUE_SIZE); 1634 strcpy(key_name, "NetworkAddressIPv6"); 1635 break; 1636 case OSBuildNumber: 1637 strcpy(key_value, os_build); 1638 strcpy(key_name, "OSBuildNumber"); 1639 break; 1640 case OSName: 1641 strcpy(key_value, os_name); 1642 strcpy(key_name, "OSName"); 1643 break; 1644 case OSMajorVersion: 1645 strcpy(key_value, os_major); 1646 strcpy(key_name, "OSMajorVersion"); 1647 break; 1648 case OSMinorVersion: 1649 strcpy(key_value, os_minor); 1650 strcpy(key_name, "OSMinorVersion"); 1651 break; 1652 case OSVersion: 1653 strcpy(key_value, os_version); 1654 strcpy(key_name, "OSVersion"); 1655 break; 1656 case ProcessorArchitecture: 1657 strcpy(key_value, processor_arch); 1658 strcpy(key_name, "ProcessorArchitecture"); 1659 break; 1660 default: 1661 hv_msg->error = HV_S_CONT; 1662 break; 1663 } 1664 1665 /* Send the value back to the kernel. */ 1666 kvp_done: 1667 len = write(kvp_fd, hv_msg, sizeof(struct hv_kvp_msg)); 1668 if (len != sizeof(struct hv_kvp_msg)) { 1669 syslog(LOG_ERR, "write failed; error: %d %s", errno, 1670 strerror(errno)); 1671 exit(EXIT_FAILURE); 1672 } 1673 } 1674 1675 close(kvp_fd); 1676 exit(0); 1677 } 1678