Root/tools/hv/hv_kvp_daemon.c

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

Archive Download this file



interactive