1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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 <limits.h>
43#include <getopt.h>
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58enum key_index {
59 FullyQualifiedDomainName = 0,
60 IntegrationServicesVersion,
61 NetworkAddressIPv4,
62 NetworkAddressIPv6,
63 OSBuildNumber,
64 OSName,
65 OSMajorVersion,
66 OSMinorVersion,
67 OSVersion,
68 ProcessorArchitecture
69};
70
71
72enum {
73 IPADDR = 0,
74 NETMASK,
75 GATEWAY,
76 DNS
77};
78
79static int in_hand_shake = 1;
80
81static char *os_name = "";
82static char *os_major = "";
83static char *os_minor = "";
84static char *processor_arch;
85static char *os_build;
86static char *os_version;
87static char *lic_version = "Unknown version";
88static char full_domain_name[HV_KVP_EXCHANGE_MAX_VALUE_SIZE];
89static struct utsname uts_buf;
90
91
92
93
94
95#define KVP_CONFIG_LOC "/var/lib/hyperv"
96
97#ifndef KVP_SCRIPTS_PATH
98#define KVP_SCRIPTS_PATH "/usr/libexec/hypervkvpd/"
99#endif
100
101#define KVP_NET_DIR "/sys/class/net/"
102
103#define MAX_FILE_NAME 100
104#define ENTRIES_PER_BLOCK 50
105
106struct kvp_record {
107 char key[HV_KVP_EXCHANGE_MAX_KEY_SIZE];
108 char value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE];
109};
110
111struct 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
119static struct kvp_file_state kvp_file_info[KVP_POOL_COUNT];
120
121static 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
133static 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
145static void kvp_update_file(int pool)
146{
147 FILE *filep;
148
149
150
151
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
175static 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 - records_read,
197 filep);
198
199 if (ferror(filep)) {
200 syslog(LOG_ERR,
201 "Failed to read file, pool: %d; error: %d %s",
202 pool, errno, strerror(errno));
203 kvp_release_lock(pool);
204 exit(EXIT_FAILURE);
205 }
206
207 if (!feof(filep)) {
208
209
210
211 num_blocks++;
212 record = realloc(record, alloc_unit * num_blocks);
213
214 if (record == NULL) {
215 syslog(LOG_ERR, "malloc failed");
216 kvp_release_lock(pool);
217 exit(EXIT_FAILURE);
218 }
219 continue;
220 }
221 break;
222 }
223
224 kvp_file_info[pool].num_blocks = num_blocks;
225 kvp_file_info[pool].records = record;
226 kvp_file_info[pool].num_records = records_read;
227
228 fclose(filep);
229 kvp_release_lock(pool);
230}
231
232static int kvp_file_init(void)
233{
234 int fd;
235 char *fname;
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 )) {
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 sprintf(fname, "%s/.kvp_pool_%d", KVP_CONFIG_LOC, i);
250 fd = open(fname, O_RDWR | O_CREAT | O_CLOEXEC, 0644 );
251
252 if (fd == -1)
253 return 1;
254
255 kvp_file_info[i].fd = fd;
256 kvp_file_info[i].num_blocks = 1;
257 kvp_file_info[i].records = malloc(alloc_unit);
258 if (kvp_file_info[i].records == NULL)
259 return 1;
260 kvp_file_info[i].num_records = 0;
261 kvp_update_mem_state(i);
262 }
263
264 return 0;
265}
266
267static int kvp_key_delete(int pool, const __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
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
287
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
310static int kvp_key_add_or_modify(int pool, const __u8 *key, int key_size,
311 const __u8 *value, int value_size)
312{
313 int i;
314 int num_records;
315 struct kvp_record *record;
316 int num_blocks;
317
318 if ((key_size > HV_KVP_EXCHANGE_MAX_KEY_SIZE) ||
319 (value_size > HV_KVP_EXCHANGE_MAX_VALUE_SIZE))
320 return 1;
321
322
323
324
325 kvp_update_mem_state(pool);
326
327 num_records = kvp_file_info[pool].num_records;
328 record = kvp_file_info[pool].records;
329 num_blocks = kvp_file_info[pool].num_blocks;
330
331 for (i = 0; i < num_records; i++) {
332 if (memcmp(key, record[i].key, key_size))
333 continue;
334
335
336
337
338 memcpy(record[i].value, value, value_size);
339 kvp_update_file(pool);
340 return 0;
341 }
342
343
344
345
346 if (num_records == (ENTRIES_PER_BLOCK * num_blocks)) {
347
348 record = realloc(record, sizeof(struct kvp_record) *
349 ENTRIES_PER_BLOCK * (num_blocks + 1));
350
351 if (record == NULL)
352 return 1;
353 kvp_file_info[pool].num_blocks++;
354
355 }
356 memcpy(record[i].value, value, value_size);
357 memcpy(record[i].key, key, key_size);
358 kvp_file_info[pool].records = record;
359 kvp_file_info[pool].num_records++;
360 kvp_update_file(pool);
361 return 0;
362}
363
364static int kvp_get_value(int pool, const __u8 *key, int key_size, __u8 *value,
365 int value_size)
366{
367 int i;
368 int num_records;
369 struct kvp_record *record;
370
371 if ((key_size > HV_KVP_EXCHANGE_MAX_KEY_SIZE) ||
372 (value_size > HV_KVP_EXCHANGE_MAX_VALUE_SIZE))
373 return 1;
374
375
376
377
378 kvp_update_mem_state(pool);
379
380 num_records = kvp_file_info[pool].num_records;
381 record = kvp_file_info[pool].records;
382
383 for (i = 0; i < num_records; i++) {
384 if (memcmp(key, record[i].key, key_size))
385 continue;
386
387
388
389 memcpy(value, record[i].value, value_size);
390 return 0;
391 }
392
393 return 1;
394}
395
396static int kvp_pool_enumerate(int pool, int index, __u8 *key, int key_size,
397 __u8 *value, int value_size)
398{
399 struct kvp_record *record;
400
401
402
403
404 kvp_update_mem_state(pool);
405 record = kvp_file_info[pool].records;
406
407 if (index >= kvp_file_info[pool].num_records) {
408 return 1;
409 }
410
411 memcpy(key, record[index].key, key_size);
412 memcpy(value, record[index].value, value_size);
413 return 0;
414}
415
416
417void kvp_get_os_info(void)
418{
419 FILE *file;
420 char *p, buf[512];
421
422 uname(&uts_buf);
423 os_version = uts_buf.release;
424 os_build = strdup(uts_buf.release);
425
426 os_name = uts_buf.sysname;
427 processor_arch = uts_buf.machine;
428
429
430
431
432
433
434 p = strchr(os_version, '-');
435 if (p)
436 *p = '\0';
437
438
439
440
441
442 file = fopen("/etc/os-release", "r");
443 if (file != NULL) {
444 while (fgets(buf, sizeof(buf), file)) {
445 char *value, *q;
446
447
448 if (buf[0] == '#')
449 continue;
450
451
452 p = strchr(buf, '=');
453 if (!p)
454 continue;
455 *p++ = 0;
456
457
458 value = p;
459 q = p;
460 while (*p) {
461 if (*p == '\\') {
462 ++p;
463 if (!*p)
464 break;
465 *q++ = *p++;
466 } else if (*p == '\'' || *p == '"' ||
467 *p == '\n') {
468 ++p;
469 } else {
470 *q++ = *p++;
471 }
472 }
473 *q = 0;
474
475 if (!strcmp(buf, "NAME")) {
476 p = strdup(value);
477 if (!p)
478 break;
479 os_name = p;
480 } else if (!strcmp(buf, "VERSION_ID")) {
481 p = strdup(value);
482 if (!p)
483 break;
484 os_major = p;
485 }
486 }
487 fclose(file);
488 return;
489 }
490
491
492 file = fopen("/etc/SuSE-release", "r");
493 if (file != NULL)
494 goto kvp_osinfo_found;
495 file = fopen("/etc/redhat-release", "r");
496 if (file != NULL)
497 goto kvp_osinfo_found;
498
499
500
501
502 return;
503
504kvp_osinfo_found:
505
506 p = fgets(buf, sizeof(buf), file);
507 if (p) {
508 p = strchr(buf, '\n');
509 if (p)
510 *p = '\0';
511 p = strdup(buf);
512 if (!p)
513 goto done;
514 os_name = p;
515
516
517 p = fgets(buf, sizeof(buf), file);
518 if (p) {
519 p = strchr(buf, '\n');
520 if (p)
521 *p = '\0';
522 p = strdup(buf);
523 if (!p)
524 goto done;
525 os_major = p;
526
527
528 p = fgets(buf, sizeof(buf), file);
529 if (p) {
530 p = strchr(buf, '\n');
531 if (p)
532 *p = '\0';
533 p = strdup(buf);
534 if (p)
535 os_minor = p;
536 }
537 }
538 }
539
540done:
541 fclose(file);
542 return;
543}
544
545
546
547
548
549
550
551
552
553
554
555static char *kvp_get_if_name(char *guid)
556{
557 DIR *dir;
558 struct dirent *entry;
559 FILE *file;
560 char *p, *x;
561 char *if_name = NULL;
562 char buf[256];
563 char dev_id[PATH_MAX];
564
565 dir = opendir(KVP_NET_DIR);
566 if (dir == NULL)
567 return NULL;
568
569 while ((entry = readdir(dir)) != NULL) {
570
571
572
573 snprintf(dev_id, sizeof(dev_id), "%s%s/device/device_id",
574 KVP_NET_DIR, entry->d_name);
575
576 file = fopen(dev_id, "r");
577 if (file == NULL)
578 continue;
579
580 p = fgets(buf, sizeof(buf), file);
581 if (p) {
582 x = strchr(p, '\n');
583 if (x)
584 *x = '\0';
585
586 if (!strcmp(p, guid)) {
587
588
589
590
591 if_name = strdup(entry->d_name);
592 fclose(file);
593 break;
594 }
595 }
596 fclose(file);
597 }
598
599 closedir(dir);
600 return if_name;
601}
602
603
604
605
606
607static char *kvp_if_name_to_mac(char *if_name)
608{
609 FILE *file;
610 char *p, *x;
611 char buf[256];
612 char addr_file[PATH_MAX];
613 unsigned int i;
614 char *mac_addr = NULL;
615
616 snprintf(addr_file, sizeof(addr_file), "%s%s%s", KVP_NET_DIR,
617 if_name, "/address");
618
619 file = fopen(addr_file, "r");
620 if (file == NULL)
621 return NULL;
622
623 p = fgets(buf, sizeof(buf), file);
624 if (p) {
625 x = strchr(p, '\n');
626 if (x)
627 *x = '\0';
628 for (i = 0; i < strlen(p); i++)
629 p[i] = toupper(p[i]);
630 mac_addr = strdup(p);
631 }
632
633 fclose(file);
634 return mac_addr;
635}
636
637static void kvp_process_ipconfig_file(char *cmd,
638 char *config_buf, unsigned int len,
639 int element_size, int offset)
640{
641 char buf[256];
642 char *p;
643 char *x;
644 FILE *file;
645
646
647
648
649 file = popen(cmd, "r");
650 if (file == NULL)
651 return;
652
653 if (offset == 0)
654 memset(config_buf, 0, len);
655 while ((p = fgets(buf, sizeof(buf), file)) != NULL) {
656 if (len < strlen(config_buf) + element_size + 1)
657 break;
658
659 x = strchr(p, '\n');
660 if (x)
661 *x = '\0';
662
663 strcat(config_buf, p);
664 strcat(config_buf, ";");
665 }
666 pclose(file);
667}
668
669static void kvp_get_ipconfig_info(char *if_name,
670 struct hv_kvp_ipaddr_value *buffer)
671{
672 char cmd[512];
673 char dhcp_info[128];
674 char *p;
675 FILE *file;
676
677
678
679
680 sprintf(cmd, "%s %s", "ip route show dev", if_name);
681 strcat(cmd, " | awk '/default/ {print $3 }'");
682
683
684
685
686 kvp_process_ipconfig_file(cmd, (char *)buffer->gate_way,
687 (MAX_GATEWAY_SIZE * 2), INET_ADDRSTRLEN, 0);
688
689
690
691
692 sprintf(cmd, "%s %s", "ip -f inet6 route show dev", if_name);
693 strcat(cmd, " | awk '/default/ {print $3 }'");
694
695
696
697
698 kvp_process_ipconfig_file(cmd, (char *)buffer->gate_way,
699 (MAX_GATEWAY_SIZE * 2), INET6_ADDRSTRLEN, 1);
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717 sprintf(cmd, KVP_SCRIPTS_PATH "%s", "hv_get_dns_info");
718
719
720
721
722 kvp_process_ipconfig_file(cmd, (char *)buffer->dns_addr,
723 (MAX_IP_ADDR_SIZE * 2), INET_ADDRSTRLEN, 0);
724
725
726
727
728
729
730
731
732
733
734 sprintf(cmd, KVP_SCRIPTS_PATH "%s %s", "hv_get_dhcp_info", if_name);
735
736 file = popen(cmd, "r");
737 if (file == NULL)
738 return;
739
740 p = fgets(dhcp_info, sizeof(dhcp_info), file);
741 if (p == NULL) {
742 pclose(file);
743 return;
744 }
745
746 if (!strncmp(p, "Enabled", 7))
747 buffer->dhcp_enabled = 1;
748 else
749 buffer->dhcp_enabled = 0;
750
751 pclose(file);
752}
753
754
755static unsigned int hweight32(unsigned int *w)
756{
757 unsigned int res = *w - ((*w >> 1) & 0x55555555);
758 res = (res & 0x33333333) + ((res >> 2) & 0x33333333);
759 res = (res + (res >> 4)) & 0x0F0F0F0F;
760 res = res + (res >> 8);
761 return (res + (res >> 16)) & 0x000000FF;
762}
763
764static int kvp_process_ip_address(void *addrp,
765 int family, char *buffer,
766 int length, int *offset)
767{
768 struct sockaddr_in *addr;
769 struct sockaddr_in6 *addr6;
770 int addr_length;
771 char tmp[50];
772 const char *str;
773
774 if (family == AF_INET) {
775 addr = (struct sockaddr_in *)addrp;
776 str = inet_ntop(family, &addr->sin_addr, tmp, 50);
777 addr_length = INET_ADDRSTRLEN;
778 } else {
779 addr6 = (struct sockaddr_in6 *)addrp;
780 str = inet_ntop(family, &addr6->sin6_addr.s6_addr, tmp, 50);
781 addr_length = INET6_ADDRSTRLEN;
782 }
783
784 if ((length - *offset) < addr_length + 2)
785 return HV_E_FAIL;
786 if (str == NULL) {
787 strcpy(buffer, "inet_ntop failed\n");
788 return HV_E_FAIL;
789 }
790 if (*offset == 0)
791 strcpy(buffer, tmp);
792 else {
793 strcat(buffer, ";");
794 strcat(buffer, tmp);
795 }
796
797 *offset += strlen(str) + 1;
798
799 return 0;
800}
801
802static int
803kvp_get_ip_info(int family, char *if_name, int op,
804 void *out_buffer, unsigned int length)
805{
806 struct ifaddrs *ifap;
807 struct ifaddrs *curp;
808 int offset = 0;
809 int sn_offset = 0;
810 int error = 0;
811 char *buffer;
812 struct hv_kvp_ipaddr_value *ip_buffer;
813 char cidr_mask[5];
814 int weight;
815 int i;
816 unsigned int *w;
817 char *sn_str;
818 struct sockaddr_in6 *addr6;
819
820 if (op == KVP_OP_ENUMERATE) {
821 buffer = out_buffer;
822 } else {
823 ip_buffer = out_buffer;
824 buffer = (char *)ip_buffer->ip_addr;
825 ip_buffer->addr_family = 0;
826 }
827
828
829
830
831
832 if (getifaddrs(&ifap)) {
833 strcpy(buffer, "getifaddrs failed\n");
834 return HV_E_FAIL;
835 }
836
837 curp = ifap;
838 while (curp != NULL) {
839 if (curp->ifa_addr == NULL) {
840 curp = curp->ifa_next;
841 continue;
842 }
843
844 if ((if_name != NULL) &&
845 (strncmp(curp->ifa_name, if_name, strlen(if_name)))) {
846
847
848
849
850 curp = curp->ifa_next;
851 continue;
852 }
853
854
855
856
857
858
859
860 if ((((family != 0) &&
861 (curp->ifa_addr->sa_family != family))) ||
862 (curp->ifa_flags & IFF_LOOPBACK)) {
863 curp = curp->ifa_next;
864 continue;
865 }
866 if ((curp->ifa_addr->sa_family != AF_INET) &&
867 (curp->ifa_addr->sa_family != AF_INET6)) {
868 curp = curp->ifa_next;
869 continue;
870 }
871
872 if (op == KVP_OP_GET_IP_INFO) {
873
874
875
876
877 if (curp->ifa_addr->sa_family == AF_INET) {
878 ip_buffer->addr_family |= ADDR_FAMILY_IPV4;
879
880
881
882 error = kvp_process_ip_address(
883 curp->ifa_netmask,
884 AF_INET,
885 (char *)
886 ip_buffer->sub_net,
887 length,
888 &sn_offset);
889 if (error)
890 goto gather_ipaddr;
891 } else {
892 ip_buffer->addr_family |= ADDR_FAMILY_IPV6;
893
894
895
896
897 weight = 0;
898 sn_str = (char *)ip_buffer->sub_net;
899 addr6 = (struct sockaddr_in6 *)
900 curp->ifa_netmask;
901 w = addr6->sin6_addr.s6_addr32;
902
903 for (i = 0; i < 4; i++)
904 weight += hweight32(&w[i]);
905
906 sprintf(cidr_mask, "/%d", weight);
907 if (length < sn_offset + strlen(cidr_mask) + 1)
908 goto gather_ipaddr;
909
910 if (sn_offset == 0)
911 strcpy(sn_str, cidr_mask);
912 else {
913 strcat((char *)ip_buffer->sub_net, ";");
914 strcat(sn_str, cidr_mask);
915 }
916 sn_offset += strlen(sn_str) + 1;
917 }
918
919
920
921
922
923 kvp_get_ipconfig_info(if_name, ip_buffer);
924 }
925
926gather_ipaddr:
927 error = kvp_process_ip_address(curp->ifa_addr,
928 curp->ifa_addr->sa_family,
929 buffer,
930 length, &offset);
931 if (error)
932 goto getaddr_done;
933
934 curp = curp->ifa_next;
935 }
936
937getaddr_done:
938 freeifaddrs(ifap);
939 return error;
940}
941
942
943
944
945static int kvp_mac_to_ip(struct hv_kvp_ipaddr_value *kvp_ip_val)
946{
947 char *mac = (char *)kvp_ip_val->adapter_id;
948 DIR *dir;
949 struct dirent *entry;
950 FILE *file;
951 char *p, *x;
952 char *if_name = NULL;
953 char buf[256];
954 char dev_id[PATH_MAX];
955 unsigned int i;
956 int error = HV_E_FAIL;
957
958 dir = opendir(KVP_NET_DIR);
959 if (dir == NULL)
960 return HV_E_FAIL;
961
962 while ((entry = readdir(dir)) != NULL) {
963
964
965
966 snprintf(dev_id, sizeof(dev_id), "%s%s/address", KVP_NET_DIR,
967 entry->d_name);
968
969 file = fopen(dev_id, "r");
970 if (file == NULL)
971 continue;
972
973 p = fgets(buf, sizeof(buf), file);
974 fclose(file);
975 if (!p)
976 continue;
977
978 x = strchr(p, '\n');
979 if (x)
980 *x = '\0';
981
982 for (i = 0; i < strlen(p); i++)
983 p[i] = toupper(p[i]);
984
985 if (strcmp(p, mac))
986 continue;
987
988
989
990
991
992 if_name = entry->d_name;
993 if (!if_name)
994 continue;
995
996 error = kvp_get_ip_info(0, if_name, KVP_OP_GET_IP_INFO,
997 kvp_ip_val, MAX_IP_ADDR_SIZE * 2);
998
999 if (!error && strlen((char *)kvp_ip_val->ip_addr))
1000 break;
1001 }
1002
1003 closedir(dir);
1004 return error;
1005}
1006
1007static int expand_ipv6(char *addr, int type)
1008{
1009 int ret;
1010 struct in6_addr v6_addr;
1011
1012 ret = inet_pton(AF_INET6, addr, &v6_addr);
1013
1014 if (ret != 1) {
1015 if (type == NETMASK)
1016 return 1;
1017 return 0;
1018 }
1019
1020 sprintf(addr, "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:"
1021 "%02x%02x:%02x%02x:%02x%02x",
1022 (int)v6_addr.s6_addr[0], (int)v6_addr.s6_addr[1],
1023 (int)v6_addr.s6_addr[2], (int)v6_addr.s6_addr[3],
1024 (int)v6_addr.s6_addr[4], (int)v6_addr.s6_addr[5],
1025 (int)v6_addr.s6_addr[6], (int)v6_addr.s6_addr[7],
1026 (int)v6_addr.s6_addr[8], (int)v6_addr.s6_addr[9],
1027 (int)v6_addr.s6_addr[10], (int)v6_addr.s6_addr[11],
1028 (int)v6_addr.s6_addr[12], (int)v6_addr.s6_addr[13],
1029 (int)v6_addr.s6_addr[14], (int)v6_addr.s6_addr[15]);
1030
1031 return 1;
1032
1033}
1034
1035static int is_ipv4(char *addr)
1036{
1037 int ret;
1038 struct in_addr ipv4_addr;
1039
1040 ret = inet_pton(AF_INET, addr, &ipv4_addr);
1041
1042 if (ret == 1)
1043 return 1;
1044 return 0;
1045}
1046
1047static int parse_ip_val_buffer(char *in_buf, int *offset,
1048 char *out_buf, int out_len)
1049{
1050 char *x;
1051 char *start;
1052
1053
1054
1055
1056
1057
1058 start = in_buf + *offset;
1059
1060 x = strchr(start, ';');
1061 if (x)
1062 *x = 0;
1063 else
1064 x = start + strlen(start);
1065
1066 if (strlen(start) != 0) {
1067 int i = 0;
1068
1069
1070
1071 while (start[i] == ' ')
1072 i++;
1073
1074 if ((x - start) <= out_len) {
1075 strcpy(out_buf, (start + i));
1076 *offset += (x - start) + 1;
1077 return 1;
1078 }
1079 }
1080 return 0;
1081}
1082
1083static int kvp_write_file(FILE *f, char *s1, char *s2, char *s3)
1084{
1085 int ret;
1086
1087 ret = fprintf(f, "%s%s%s%s\n", s1, s2, "=", s3);
1088
1089 if (ret < 0)
1090 return HV_E_FAIL;
1091
1092 return 0;
1093}
1094
1095
1096static int process_ip_string(FILE *f, char *ip_string, int type)
1097{
1098 int error = 0;
1099 char addr[INET6_ADDRSTRLEN];
1100 int i = 0;
1101 int j = 0;
1102 char str[256];
1103 char sub_str[13];
1104 int offset = 0;
1105
1106 memset(addr, 0, sizeof(addr));
1107
1108 while (parse_ip_val_buffer(ip_string, &offset, addr,
1109 (MAX_IP_ADDR_SIZE * 2))) {
1110
1111 sub_str[0] = 0;
1112 if (is_ipv4(addr)) {
1113 switch (type) {
1114 case IPADDR:
1115 snprintf(str, sizeof(str), "%s", "IPADDR");
1116 break;
1117 case NETMASK:
1118 snprintf(str, sizeof(str), "%s", "NETMASK");
1119 break;
1120 case GATEWAY:
1121 snprintf(str, sizeof(str), "%s", "GATEWAY");
1122 break;
1123 case DNS:
1124 snprintf(str, sizeof(str), "%s", "DNS");
1125 break;
1126 }
1127
1128 if (type == DNS) {
1129 snprintf(sub_str, sizeof(sub_str), "%d", ++i);
1130 } else if (type == GATEWAY && i == 0) {
1131 ++i;
1132 } else {
1133 snprintf(sub_str, sizeof(sub_str), "%d", i++);
1134 }
1135
1136
1137 } else if (expand_ipv6(addr, type)) {
1138 switch (type) {
1139 case IPADDR:
1140 snprintf(str, sizeof(str), "%s", "IPV6ADDR");
1141 break;
1142 case NETMASK:
1143 snprintf(str, sizeof(str), "%s", "IPV6NETMASK");
1144 break;
1145 case GATEWAY:
1146 snprintf(str, sizeof(str), "%s",
1147 "IPV6_DEFAULTGW");
1148 break;
1149 case DNS:
1150 snprintf(str, sizeof(str), "%s", "DNS");
1151 break;
1152 }
1153
1154 if (type == DNS) {
1155 snprintf(sub_str, sizeof(sub_str), "%d", ++i);
1156 } else if (j == 0) {
1157 ++j;
1158 } else {
1159 snprintf(sub_str, sizeof(sub_str), "_%d", j++);
1160 }
1161 } else {
1162 return HV_INVALIDARG;
1163 }
1164
1165 error = kvp_write_file(f, str, sub_str, addr);
1166 if (error)
1167 return error;
1168 memset(addr, 0, sizeof(addr));
1169 }
1170
1171 return 0;
1172}
1173
1174static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val)
1175{
1176 int error = 0;
1177 char if_file[PATH_MAX];
1178 FILE *file;
1179 char cmd[PATH_MAX];
1180 char *mac_addr;
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229 snprintf(if_file, sizeof(if_file), "%s%s%s", KVP_CONFIG_LOC,
1230 "/ifcfg-", if_name);
1231
1232 file = fopen(if_file, "w");
1233
1234 if (file == NULL) {
1235 syslog(LOG_ERR, "Failed to open config file; error: %d %s",
1236 errno, strerror(errno));
1237 return HV_E_FAIL;
1238 }
1239
1240
1241
1242
1243
1244 mac_addr = kvp_if_name_to_mac(if_name);
1245 if (mac_addr == NULL) {
1246 error = HV_E_FAIL;
1247 goto setval_error;
1248 }
1249
1250 error = kvp_write_file(file, "HWADDR", "", mac_addr);
1251 free(mac_addr);
1252 if (error)
1253 goto setval_error;
1254
1255 error = kvp_write_file(file, "DEVICE", "", if_name);
1256 if (error)
1257 goto setval_error;
1258
1259
1260
1261
1262
1263
1264
1265 if (new_val->dhcp_enabled) {
1266 error = kvp_write_file(file, "BOOTPROTO", "", "dhcp");
1267 if (error)
1268 goto setval_error;
1269
1270 } else {
1271 error = kvp_write_file(file, "BOOTPROTO", "", "none");
1272 if (error)
1273 goto setval_error;
1274 }
1275
1276
1277
1278
1279
1280
1281 error = process_ip_string(file, (char *)new_val->ip_addr, IPADDR);
1282 if (error)
1283 goto setval_error;
1284
1285 error = process_ip_string(file, (char *)new_val->sub_net, NETMASK);
1286 if (error)
1287 goto setval_error;
1288
1289 error = process_ip_string(file, (char *)new_val->gate_way, GATEWAY);
1290 if (error)
1291 goto setval_error;
1292
1293 error = process_ip_string(file, (char *)new_val->dns_addr, DNS);
1294 if (error)
1295 goto setval_error;
1296
1297 fclose(file);
1298
1299
1300
1301
1302
1303
1304 snprintf(cmd, sizeof(cmd), KVP_SCRIPTS_PATH "%s %s",
1305 "hv_set_ifconfig", if_file);
1306 if (system(cmd)) {
1307 syslog(LOG_ERR, "Failed to execute cmd '%s'; error: %d %s",
1308 cmd, errno, strerror(errno));
1309 return HV_E_FAIL;
1310 }
1311 return 0;
1312
1313setval_error:
1314 syslog(LOG_ERR, "Failed to write config file");
1315 fclose(file);
1316 return error;
1317}
1318
1319
1320static void
1321kvp_get_domain_name(char *buffer, int length)
1322{
1323 struct addrinfo hints, *info ;
1324 int error = 0;
1325
1326 gethostname(buffer, length);
1327 memset(&hints, 0, sizeof(hints));
1328 hints.ai_family = AF_INET;
1329 hints.ai_socktype = SOCK_STREAM;
1330 hints.ai_flags = AI_CANONNAME;
1331
1332 error = getaddrinfo(buffer, NULL, &hints, &info);
1333 if (error != 0) {
1334 snprintf(buffer, length, "getaddrinfo failed: 0x%x %s",
1335 error, gai_strerror(error));
1336 return;
1337 }
1338 snprintf(buffer, length, "%s", info->ai_canonname);
1339 freeaddrinfo(info);
1340}
1341
1342void print_usage(char *argv[])
1343{
1344 fprintf(stderr, "Usage: %s [options]\n"
1345 "Options are:\n"
1346 " -n, --no-daemon stay in foreground, don't daemonize\n"
1347 " -h, --help print this help\n", argv[0]);
1348}
1349
1350int main(int argc, char *argv[])
1351{
1352 int kvp_fd, len;
1353 int error;
1354 struct pollfd pfd;
1355 char *p;
1356 struct hv_kvp_msg hv_msg[1];
1357 char *key_value;
1358 char *key_name;
1359 int op;
1360 int pool;
1361 char *if_name;
1362 struct hv_kvp_ipaddr_value *kvp_ip_val;
1363 int daemonize = 1, long_index = 0, opt;
1364
1365 static struct option long_options[] = {
1366 {"help", no_argument, 0, 'h' },
1367 {"no-daemon", no_argument, 0, 'n' },
1368 {0, 0, 0, 0 }
1369 };
1370
1371 while ((opt = getopt_long(argc, argv, "hn", long_options,
1372 &long_index)) != -1) {
1373 switch (opt) {
1374 case 'n':
1375 daemonize = 0;
1376 break;
1377 case 'h':
1378 default:
1379 print_usage(argv);
1380 exit(EXIT_FAILURE);
1381 }
1382 }
1383
1384 if (daemonize && daemon(1, 0))
1385 return 1;
1386
1387 openlog("KVP", 0, LOG_USER);
1388 syslog(LOG_INFO, "KVP starting; pid is:%d", getpid());
1389
1390 kvp_fd = open("/dev/vmbus/hv_kvp", O_RDWR | O_CLOEXEC);
1391
1392 if (kvp_fd < 0) {
1393 syslog(LOG_ERR, "open /dev/vmbus/hv_kvp failed; error: %d %s",
1394 errno, strerror(errno));
1395 exit(EXIT_FAILURE);
1396 }
1397
1398
1399
1400
1401 kvp_get_os_info();
1402
1403
1404
1405
1406 kvp_get_domain_name(full_domain_name, sizeof(full_domain_name));
1407
1408 if (kvp_file_init()) {
1409 syslog(LOG_ERR, "Failed to initialize the pools");
1410 exit(EXIT_FAILURE);
1411 }
1412
1413
1414
1415
1416 hv_msg->kvp_hdr.operation = KVP_OP_REGISTER1;
1417 len = write(kvp_fd, hv_msg, sizeof(struct hv_kvp_msg));
1418 if (len != sizeof(struct hv_kvp_msg)) {
1419 syslog(LOG_ERR, "registration to kernel failed; error: %d %s",
1420 errno, strerror(errno));
1421 close(kvp_fd);
1422 exit(EXIT_FAILURE);
1423 }
1424
1425 pfd.fd = kvp_fd;
1426
1427 while (1) {
1428 pfd.events = POLLIN;
1429 pfd.revents = 0;
1430
1431 if (poll(&pfd, 1, -1) < 0) {
1432 syslog(LOG_ERR, "poll failed; error: %d %s", errno, strerror(errno));
1433 if (errno == EINVAL) {
1434 close(kvp_fd);
1435 exit(EXIT_FAILURE);
1436 }
1437 else
1438 continue;
1439 }
1440
1441 len = read(kvp_fd, hv_msg, sizeof(struct hv_kvp_msg));
1442
1443 if (len != sizeof(struct hv_kvp_msg)) {
1444 syslog(LOG_ERR, "read failed; error:%d %s",
1445 errno, strerror(errno));
1446
1447 close(kvp_fd);
1448 return EXIT_FAILURE;
1449 }
1450
1451
1452
1453
1454
1455
1456 op = hv_msg->kvp_hdr.operation;
1457 pool = hv_msg->kvp_hdr.pool;
1458 hv_msg->error = HV_S_OK;
1459
1460 if ((in_hand_shake) && (op == KVP_OP_REGISTER1)) {
1461
1462
1463
1464
1465 in_hand_shake = 0;
1466 p = (char *)hv_msg->body.kvp_register.version;
1467 lic_version = malloc(strlen(p) + 1);
1468 if (lic_version) {
1469 strcpy(lic_version, p);
1470 syslog(LOG_INFO, "KVP LIC Version: %s",
1471 lic_version);
1472 } else {
1473 syslog(LOG_ERR, "malloc failed");
1474 }
1475 continue;
1476 }
1477
1478 switch (op) {
1479 case KVP_OP_GET_IP_INFO:
1480 kvp_ip_val = &hv_msg->body.kvp_ip_val;
1481
1482 error = kvp_mac_to_ip(kvp_ip_val);
1483
1484 if (error)
1485 hv_msg->error = error;
1486
1487 break;
1488
1489 case KVP_OP_SET_IP_INFO:
1490 kvp_ip_val = &hv_msg->body.kvp_ip_val;
1491 if_name = kvp_get_if_name(
1492 (char *)kvp_ip_val->adapter_id);
1493 if (if_name == NULL) {
1494
1495
1496
1497
1498 hv_msg->error = HV_GUID_NOTFOUND;
1499 break;
1500 }
1501 error = kvp_set_ip_info(if_name, kvp_ip_val);
1502 if (error)
1503 hv_msg->error = error;
1504
1505 free(if_name);
1506 break;
1507
1508 case KVP_OP_SET:
1509 if (kvp_key_add_or_modify(pool,
1510 hv_msg->body.kvp_set.data.key,
1511 hv_msg->body.kvp_set.data.key_size,
1512 hv_msg->body.kvp_set.data.value,
1513 hv_msg->body.kvp_set.data.value_size))
1514 hv_msg->error = HV_S_CONT;
1515 break;
1516
1517 case KVP_OP_GET:
1518 if (kvp_get_value(pool,
1519 hv_msg->body.kvp_set.data.key,
1520 hv_msg->body.kvp_set.data.key_size,
1521 hv_msg->body.kvp_set.data.value,
1522 hv_msg->body.kvp_set.data.value_size))
1523 hv_msg->error = HV_S_CONT;
1524 break;
1525
1526 case KVP_OP_DELETE:
1527 if (kvp_key_delete(pool,
1528 hv_msg->body.kvp_delete.key,
1529 hv_msg->body.kvp_delete.key_size))
1530 hv_msg->error = HV_S_CONT;
1531 break;
1532
1533 default:
1534 break;
1535 }
1536
1537 if (op != KVP_OP_ENUMERATE)
1538 goto kvp_done;
1539
1540
1541
1542
1543
1544
1545 if (pool != KVP_POOL_AUTO) {
1546 if (kvp_pool_enumerate(pool,
1547 hv_msg->body.kvp_enum_data.index,
1548 hv_msg->body.kvp_enum_data.data.key,
1549 HV_KVP_EXCHANGE_MAX_KEY_SIZE,
1550 hv_msg->body.kvp_enum_data.data.value,
1551 HV_KVP_EXCHANGE_MAX_VALUE_SIZE))
1552 hv_msg->error = HV_S_CONT;
1553 goto kvp_done;
1554 }
1555
1556 key_name = (char *)hv_msg->body.kvp_enum_data.data.key;
1557 key_value = (char *)hv_msg->body.kvp_enum_data.data.value;
1558
1559 switch (hv_msg->body.kvp_enum_data.index) {
1560 case FullyQualifiedDomainName:
1561 strcpy(key_value, full_domain_name);
1562 strcpy(key_name, "FullyQualifiedDomainName");
1563 break;
1564 case IntegrationServicesVersion:
1565 strcpy(key_name, "IntegrationServicesVersion");
1566 strcpy(key_value, lic_version);
1567 break;
1568 case NetworkAddressIPv4:
1569 kvp_get_ip_info(AF_INET, NULL, KVP_OP_ENUMERATE,
1570 key_value, HV_KVP_EXCHANGE_MAX_VALUE_SIZE);
1571 strcpy(key_name, "NetworkAddressIPv4");
1572 break;
1573 case NetworkAddressIPv6:
1574 kvp_get_ip_info(AF_INET6, NULL, KVP_OP_ENUMERATE,
1575 key_value, HV_KVP_EXCHANGE_MAX_VALUE_SIZE);
1576 strcpy(key_name, "NetworkAddressIPv6");
1577 break;
1578 case OSBuildNumber:
1579 strcpy(key_value, os_build);
1580 strcpy(key_name, "OSBuildNumber");
1581 break;
1582 case OSName:
1583 strcpy(key_value, os_name);
1584 strcpy(key_name, "OSName");
1585 break;
1586 case OSMajorVersion:
1587 strcpy(key_value, os_major);
1588 strcpy(key_name, "OSMajorVersion");
1589 break;
1590 case OSMinorVersion:
1591 strcpy(key_value, os_minor);
1592 strcpy(key_name, "OSMinorVersion");
1593 break;
1594 case OSVersion:
1595 strcpy(key_value, os_version);
1596 strcpy(key_name, "OSVersion");
1597 break;
1598 case ProcessorArchitecture:
1599 strcpy(key_value, processor_arch);
1600 strcpy(key_name, "ProcessorArchitecture");
1601 break;
1602 default:
1603 hv_msg->error = HV_S_CONT;
1604 break;
1605 }
1606
1607
1608kvp_done:
1609 len = write(kvp_fd, hv_msg, sizeof(struct hv_kvp_msg));
1610 if (len != sizeof(struct hv_kvp_msg)) {
1611 syslog(LOG_ERR, "write failed; error: %d %s", errno,
1612 strerror(errno));
1613 exit(EXIT_FAILURE);
1614 }
1615 }
1616
1617 close(kvp_fd);
1618 exit(0);
1619}
1620