linux/tools/hv/hv_kvp_daemon.c
<<
>>
Prefs
   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/netlink.h>
  38#include <ifaddrs.h>
  39#include <netdb.h>
  40#include <syslog.h>
  41
  42/*
  43 * KYS: TODO. Need to register these in the kernel.
  44 *
  45 * The following definitions are shared with the in-kernel component; do not
  46 * change any of this without making the corresponding changes in
  47 * the KVP kernel component.
  48 */
  49#define CN_KVP_IDX              0x9     /* MSFT KVP functionality */
  50#define CN_KVP_VAL              0x1 /* This supports queries from the kernel */
  51#define CN_KVP_USER_VAL         0x2 /* This supports queries from the user  */
  52
  53/*
  54 * KVP protocol: The user mode component first registers with the
  55 * the kernel component. Subsequently, the kernel component requests, data
  56 * for the specified keys. In response to this message the user mode component
  57 * fills in the value corresponding to the specified key. We overload the
  58 * sequence field in the cn_msg header to define our KVP message types.
  59 *
  60 * We use this infrastructure for also supporting queries from user mode
  61 * application for state that may be maintained in the KVP kernel component.
  62 *
  63 * XXXKYS: Have a shared header file between the user and kernel (TODO)
  64 */
  65
  66enum kvp_op {
  67        KVP_REGISTER = 0, /* Register the user mode component*/
  68        KVP_KERNEL_GET, /*Kernel is requesting the value for the specified key*/
  69        KVP_KERNEL_SET, /*Kernel is providing the value for the specified key*/
  70        KVP_USER_GET, /*User is requesting the value for the specified key*/
  71        KVP_USER_SET /*User is providing the value for the specified key*/
  72};
  73
  74#define HV_KVP_EXCHANGE_MAX_KEY_SIZE    512
  75#define HV_KVP_EXCHANGE_MAX_VALUE_SIZE  2048
  76
  77struct hv_ku_msg {
  78        __u32   kvp_index;
  79        __u8  kvp_key[HV_KVP_EXCHANGE_MAX_KEY_SIZE]; /* Key name */
  80        __u8  kvp_value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE]; /* Key  value */
  81};
  82
  83enum key_index {
  84        FullyQualifiedDomainName = 0,
  85        IntegrationServicesVersion, /*This key is serviced in the kernel*/
  86        NetworkAddressIPv4,
  87        NetworkAddressIPv6,
  88        OSBuildNumber,
  89        OSName,
  90        OSMajorVersion,
  91        OSMinorVersion,
  92        OSVersion,
  93        ProcessorArchitecture
  94};
  95
  96/*
  97 * End of shared definitions.
  98 */
  99
 100static char kvp_send_buffer[4096];
 101static char kvp_recv_buffer[4096];
 102static struct sockaddr_nl addr;
 103
 104static char *os_name = "";
 105static char *os_major = "";
 106static char *os_minor = "";
 107static char *processor_arch;
 108static char *os_build;
 109static char *lic_version;
 110static struct utsname uts_buf;
 111
 112void kvp_get_os_info(void)
 113{
 114        FILE    *file;
 115        char    *p, buf[512];
 116
 117        uname(&uts_buf);
 118        os_build = uts_buf.release;
 119        processor_arch = uts_buf.machine;
 120
 121        /*
 122         * The current windows host (win7) expects the build
 123         * string to be of the form: x.y.z
 124         * Strip additional information we may have.
 125         */
 126        p = strchr(os_build, '-');
 127        if (p)
 128                *p = '\0';
 129
 130        file = fopen("/etc/SuSE-release", "r");
 131        if (file != NULL)
 132                goto kvp_osinfo_found;
 133        file  = fopen("/etc/redhat-release", "r");
 134        if (file != NULL)
 135                goto kvp_osinfo_found;
 136        /*
 137         * Add code for other supported platforms.
 138         */
 139
 140        /*
 141         * We don't have information about the os.
 142         */
 143        os_name = uts_buf.sysname;
 144        return;
 145
 146kvp_osinfo_found:
 147        /* up to three lines */
 148        p = fgets(buf, sizeof(buf), file);
 149        if (p) {
 150                p = strchr(buf, '\n');
 151                if (p)
 152                        *p = '\0';
 153                p = strdup(buf);
 154                if (!p)
 155                        goto done;
 156                os_name = p;
 157
 158                /* second line */
 159                p = fgets(buf, sizeof(buf), file);
 160                if (p) {
 161                        p = strchr(buf, '\n');
 162                        if (p)
 163                                *p = '\0';
 164                        p = strdup(buf);
 165                        if (!p)
 166                                goto done;
 167                        os_major = p;
 168
 169                        /* third line */
 170                        p = fgets(buf, sizeof(buf), file);
 171                        if (p)  {
 172                                p = strchr(buf, '\n');
 173                                if (p)
 174                                        *p = '\0';
 175                                p = strdup(buf);
 176                                if (p)
 177                                        os_minor = p;
 178                        }
 179                }
 180        }
 181
 182done:
 183        fclose(file);
 184        return;
 185}
 186
 187static int
 188kvp_get_ip_address(int family, char *buffer, int length)
 189{
 190        struct ifaddrs *ifap;
 191        struct ifaddrs *curp;
 192        int ipv4_len = strlen("255.255.255.255") + 1;
 193        int ipv6_len = strlen("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")+1;
 194        int offset = 0;
 195        const char *str;
 196        char tmp[50];
 197        int error = 0;
 198
 199        /*
 200         * On entry into this function, the buffer is capable of holding the
 201         * maximum key value (2048 bytes).
 202         */
 203
 204        if (getifaddrs(&ifap)) {
 205                strcpy(buffer, "getifaddrs failed\n");
 206                return 1;
 207        }
 208
 209        curp = ifap;
 210        while (curp != NULL) {
 211                if ((curp->ifa_addr != NULL) &&
 212                   (curp->ifa_addr->sa_family == family)) {
 213                        if (family == AF_INET) {
 214                                struct sockaddr_in *addr =
 215                                (struct sockaddr_in *) curp->ifa_addr;
 216
 217                                str = inet_ntop(family, &addr->sin_addr,
 218                                                tmp, 50);
 219                                if (str == NULL) {
 220                                        strcpy(buffer, "inet_ntop failed\n");
 221                                        error = 1;
 222                                        goto getaddr_done;
 223                                }
 224                                if (offset == 0)
 225                                        strcpy(buffer, tmp);
 226                                else
 227                                        strcat(buffer, tmp);
 228                                strcat(buffer, ";");
 229
 230                                offset += strlen(str) + 1;
 231                                if ((length - offset) < (ipv4_len + 1))
 232                                        goto getaddr_done;
 233
 234                        } else {
 235
 236                        /*
 237                         * We only support AF_INET and AF_INET6
 238                         * and the list of addresses is separated by a ";".
 239                         */
 240                                struct sockaddr_in6 *addr =
 241                                (struct sockaddr_in6 *) curp->ifa_addr;
 242
 243                                str = inet_ntop(family,
 244                                        &addr->sin6_addr.s6_addr,
 245                                        tmp, 50);
 246                                if (str == NULL) {
 247                                        strcpy(buffer, "inet_ntop failed\n");
 248                                        error = 1;
 249                                        goto getaddr_done;
 250                                }
 251                                if (offset == 0)
 252                                        strcpy(buffer, tmp);
 253                                else
 254                                        strcat(buffer, tmp);
 255                                strcat(buffer, ";");
 256                                offset += strlen(str) + 1;
 257                                if ((length - offset) < (ipv6_len + 1))
 258                                        goto getaddr_done;
 259
 260                        }
 261
 262                }
 263                curp = curp->ifa_next;
 264        }
 265
 266getaddr_done:
 267        freeifaddrs(ifap);
 268        return error;
 269}
 270
 271
 272static int
 273kvp_get_domain_name(char *buffer, int length)
 274{
 275        struct addrinfo hints, *info ;
 276        int error = 0;
 277
 278        gethostname(buffer, length);
 279        memset(&hints, 0, sizeof(hints));
 280        hints.ai_family = AF_INET; /*Get only ipv4 addrinfo. */
 281        hints.ai_socktype = SOCK_STREAM;
 282        hints.ai_flags = AI_CANONNAME;
 283
 284        error = getaddrinfo(buffer, NULL, &hints, &info);
 285        if (error != 0) {
 286                strcpy(buffer, "getaddrinfo failed\n");
 287                return error;
 288        }
 289        strcpy(buffer, info->ai_canonname);
 290        freeaddrinfo(info);
 291        return error;
 292}
 293
 294static int
 295netlink_send(int fd, struct cn_msg *msg)
 296{
 297        struct nlmsghdr *nlh;
 298        unsigned int size;
 299        struct msghdr message;
 300        char buffer[64];
 301        struct iovec iov[2];
 302
 303        size = NLMSG_SPACE(sizeof(struct cn_msg) + msg->len);
 304
 305        nlh = (struct nlmsghdr *)buffer;
 306        nlh->nlmsg_seq = 0;
 307        nlh->nlmsg_pid = getpid();
 308        nlh->nlmsg_type = NLMSG_DONE;
 309        nlh->nlmsg_len = NLMSG_LENGTH(size - sizeof(*nlh));
 310        nlh->nlmsg_flags = 0;
 311
 312        iov[0].iov_base = nlh;
 313        iov[0].iov_len = sizeof(*nlh);
 314
 315        iov[1].iov_base = msg;
 316        iov[1].iov_len = size;
 317
 318        memset(&message, 0, sizeof(message));
 319        message.msg_name = &addr;
 320        message.msg_namelen = sizeof(addr);
 321        message.msg_iov = iov;
 322        message.msg_iovlen = 2;
 323
 324        return sendmsg(fd, &message, 0);
 325}
 326
 327int main(void)
 328{
 329        int fd, len, sock_opt;
 330        int error;
 331        struct cn_msg *message;
 332        struct pollfd pfd;
 333        struct nlmsghdr *incoming_msg;
 334        struct cn_msg   *incoming_cn_msg;
 335        struct hv_ku_msg *hv_msg;
 336        char    *p;
 337        char    *key_value;
 338        char    *key_name;
 339
 340        daemon(1, 0);
 341        openlog("KVP", 0, LOG_USER);
 342        syslog(LOG_INFO, "KVP starting; pid is:%d", getpid());
 343        /*
 344         * Retrieve OS release information.
 345         */
 346        kvp_get_os_info();
 347
 348        fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR);
 349        if (fd < 0) {
 350                syslog(LOG_ERR, "netlink socket creation failed; error:%d", fd);
 351                exit(-1);
 352        }
 353        addr.nl_family = AF_NETLINK;
 354        addr.nl_pad = 0;
 355        addr.nl_pid = 0;
 356        addr.nl_groups = CN_KVP_IDX;
 357
 358
 359        error = bind(fd, (struct sockaddr *)&addr, sizeof(addr));
 360        if (error < 0) {
 361                syslog(LOG_ERR, "bind failed; error:%d", error);
 362                close(fd);
 363                exit(-1);
 364        }
 365        sock_opt = addr.nl_groups;
 366        setsockopt(fd, 270, 1, &sock_opt, sizeof(sock_opt));
 367        /*
 368         * Register ourselves with the kernel.
 369         */
 370        message = (struct cn_msg *)kvp_send_buffer;
 371        message->id.idx = CN_KVP_IDX;
 372        message->id.val = CN_KVP_VAL;
 373        message->seq = KVP_REGISTER;
 374        message->ack = 0;
 375        message->len = 0;
 376
 377        len = netlink_send(fd, message);
 378        if (len < 0) {
 379                syslog(LOG_ERR, "netlink_send failed; error:%d", len);
 380                close(fd);
 381                exit(-1);
 382        }
 383
 384        pfd.fd = fd;
 385
 386        while (1) {
 387                pfd.events = POLLIN;
 388                pfd.revents = 0;
 389                poll(&pfd, 1, -1);
 390
 391                len = recv(fd, kvp_recv_buffer, sizeof(kvp_recv_buffer), 0);
 392
 393                if (len < 0) {
 394                        syslog(LOG_ERR, "recv failed; error:%d", len);
 395                        close(fd);
 396                        return -1;
 397                }
 398
 399                incoming_msg = (struct nlmsghdr *)kvp_recv_buffer;
 400                incoming_cn_msg = (struct cn_msg *)NLMSG_DATA(incoming_msg);
 401
 402                switch (incoming_cn_msg->seq) {
 403                case KVP_REGISTER:
 404                        /*
 405                         * Driver is registering with us; stash away the version
 406                         * information.
 407                         */
 408                        p = (char *)incoming_cn_msg->data;
 409                        lic_version = malloc(strlen(p) + 1);
 410                        if (lic_version) {
 411                                strcpy(lic_version, p);
 412                                syslog(LOG_INFO, "KVP LIC Version: %s",
 413                                        lic_version);
 414                        } else {
 415                                syslog(LOG_ERR, "malloc failed");
 416                        }
 417                        continue;
 418
 419                case KVP_KERNEL_GET:
 420                        break;
 421                default:
 422                        continue;
 423                }
 424
 425                hv_msg = (struct hv_ku_msg *)incoming_cn_msg->data;
 426                key_name = (char *)hv_msg->kvp_key;
 427                key_value = (char *)hv_msg->kvp_value;
 428
 429                switch (hv_msg->kvp_index) {
 430                case FullyQualifiedDomainName:
 431                        kvp_get_domain_name(key_value,
 432                                        HV_KVP_EXCHANGE_MAX_VALUE_SIZE);
 433                        strcpy(key_name, "FullyQualifiedDomainName");
 434                        break;
 435                case IntegrationServicesVersion:
 436                        strcpy(key_name, "IntegrationServicesVersion");
 437                        strcpy(key_value, lic_version);
 438                        break;
 439                case NetworkAddressIPv4:
 440                        kvp_get_ip_address(AF_INET, key_value,
 441                                        HV_KVP_EXCHANGE_MAX_VALUE_SIZE);
 442                        strcpy(key_name, "NetworkAddressIPv4");
 443                        break;
 444                case NetworkAddressIPv6:
 445                        kvp_get_ip_address(AF_INET6, key_value,
 446                                        HV_KVP_EXCHANGE_MAX_VALUE_SIZE);
 447                        strcpy(key_name, "NetworkAddressIPv6");
 448                        break;
 449                case OSBuildNumber:
 450                        strcpy(key_value, os_build);
 451                        strcpy(key_name, "OSBuildNumber");
 452                        break;
 453                case OSName:
 454                        strcpy(key_value, os_name);
 455                        strcpy(key_name, "OSName");
 456                        break;
 457                case OSMajorVersion:
 458                        strcpy(key_value, os_major);
 459                        strcpy(key_name, "OSMajorVersion");
 460                        break;
 461                case OSMinorVersion:
 462                        strcpy(key_value, os_minor);
 463                        strcpy(key_name, "OSMinorVersion");
 464                        break;
 465                case OSVersion:
 466                        strcpy(key_value, os_build);
 467                        strcpy(key_name, "OSVersion");
 468                        break;
 469                case ProcessorArchitecture:
 470                        strcpy(key_value, processor_arch);
 471                        strcpy(key_name, "ProcessorArchitecture");
 472                        break;
 473                default:
 474                        strcpy(key_value, "Unknown Key");
 475                        /*
 476                         * We use a null key name to terminate enumeration.
 477                         */
 478                        strcpy(key_name, "");
 479                        break;
 480                }
 481                /*
 482                 * Send the value back to the kernel. The response is
 483                 * already in the receive buffer. Update the cn_msg header to
 484                 * reflect the key value that has been added to the message
 485                 */
 486
 487                incoming_cn_msg->id.idx = CN_KVP_IDX;
 488                incoming_cn_msg->id.val = CN_KVP_VAL;
 489                incoming_cn_msg->seq = KVP_USER_SET;
 490                incoming_cn_msg->ack = 0;
 491                incoming_cn_msg->len = sizeof(struct hv_ku_msg);
 492
 493                len = netlink_send(fd, incoming_cn_msg);
 494                if (len < 0) {
 495                        syslog(LOG_ERR, "net_link send failed; error:%d", len);
 496                        exit(-1);
 497                }
 498        }
 499
 500}
 501
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.