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