cregit-Linux how code gets into the kernel

Release 4.10 tools/hv/hv_kvp_daemon.c

Directory: tools/hv
/*
 * An implementation of key value pair (KVP) functionality for Linux.
 *
 *
 * Copyright (C) 2010, Novell, Inc.
 * Author : K. Y. Srinivasan <ksrinivasan@novell.com>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 as published
 * by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
 * NON INFRINGEMENT.  See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 */


#include <sys/poll.h>
#include <sys/utsname.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <arpa/inet.h>
#include <linux/hyperv.h>
#include <ifaddrs.h>
#include <netdb.h>
#include <syslog.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
#include <net/if.h>
#include <getopt.h>

/*
 * KVP protocol: The user mode component first registers with the
 * the kernel component. Subsequently, the kernel component requests, data
 * for the specified keys. In response to this message the user mode component
 * fills in the value corresponding to the specified key. We overload the
 * sequence field in the cn_msg header to define our KVP message types.
 *
 * We use this infrastructure for also supporting queries from user mode
 * application for state that may be maintained in the KVP kernel component.
 *
 */



enum key_index {
	
FullyQualifiedDomainName = 0,
	
IntegrationServicesVersion, /*This key is serviced in the kernel*/
	
NetworkAddressIPv4,
	
NetworkAddressIPv6,
	
OSBuildNumber,
	
OSName,
	
OSMajorVersion,
	
OSMinorVersion,
	
OSVersion,
	
ProcessorArchitecture
};


enum {
	
IPADDR = 0,
	
NETMASK,
	
GATEWAY,
	
DNS
};


static int in_hand_shake = 1;


static char *os_name = "";

static char *os_major = "";

static char *os_minor = "";

static char *processor_arch;

static char *os_build;

static char *os_version;

static char *lic_version = "Unknown version";

static char full_domain_name[HV_KVP_EXCHANGE_MAX_VALUE_SIZE];

static struct utsname uts_buf;

/*
 * The location of the interface configuration file.
 */


#define KVP_CONFIG_LOC	"/var/lib/hyperv"

#ifndef KVP_SCRIPTS_PATH

#define KVP_SCRIPTS_PATH "/usr/libexec/hypervkvpd/"
#endif


#define MAX_FILE_NAME 100

#define ENTRIES_PER_BLOCK 50


struct kvp_record {
	
char key[HV_KVP_EXCHANGE_MAX_KEY_SIZE];
	
char value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE];
};


struct kvp_file_state {
	
int fd;
	
int num_blocks;
	
struct kvp_record *records;
	
int num_records;
	
char fname[MAX_FILE_NAME];
};


static struct kvp_file_state kvp_file_info[KVP_POOL_COUNT];


static void kvp_acquire_lock(int pool) { struct flock fl = {F_WRLCK, SEEK_SET, 0, 0, 0}; fl.l_pid = getpid(); if (fcntl(kvp_file_info[pool].fd, F_SETLKW, &fl) == -1) { syslog(LOG_ERR, "Failed to acquire the lock pool: %d; error: %d %s", pool, errno, strerror(errno)); exit(EXIT_FAILURE); } }

Contributors

PersonTokensPropCommitsCommitProp
ky srinivasanky srinivasan6688.00%133.33%
tomas hozzatomas hozza810.67%133.33%
ben hutchingsben hutchings11.33%133.33%
Total75100.00%3100.00%


static void kvp_release_lock(int pool) { struct flock fl = {F_UNLCK, SEEK_SET, 0, 0, 0}; fl.l_pid = getpid(); if (fcntl(kvp_file_info[pool].fd, F_SETLK, &fl) == -1) { syslog(LOG_ERR, "Failed to release the lock pool: %d; error: %d %s", pool, errno, strerror(errno)); exit(EXIT_FAILURE); } }

Contributors

PersonTokensPropCommitsCommitProp
ky srinivasanky srinivasan6688.00%133.33%
tomas hozzatomas hozza810.67%133.33%
ben hutchingsben hutchings11.33%133.33%
Total75100.00%3100.00%


static void kvp_update_file(int pool) { FILE *filep; /* * We are going to write our in-memory registry out to * disk; acquire the lock first. */ kvp_acquire_lock(pool); filep = fopen(kvp_file_info[pool].fname, "we"); if (!filep) { syslog(LOG_ERR, "Failed to open file, pool: %d; error: %d %s", pool, errno, strerror(errno)); kvp_release_lock(pool); exit(EXIT_FAILURE); } fwrite(kvp_file_info[pool].records, sizeof(struct kvp_record), kvp_file_info[pool].num_records, filep); if (ferror(filep) || fclose(filep)) { kvp_release_lock(pool); syslog(LOG_ERR, "Failed to write file, pool: %d", pool); exit(EXIT_FAILURE); } kvp_release_lock(pool); }

Contributors

PersonTokensPropCommitsCommitProp
ky srinivasanky srinivasan8565.89%116.67%
ben hutchingsben hutchings3023.26%350.00%
tomas hozzatomas hozza1410.85%233.33%
Total129100.00%6100.00%


static void kvp_update_mem_state(int pool) { FILE *filep; size_t records_read = 0; struct kvp_record *record = kvp_file_info[pool].records; struct kvp_record *readp; int num_blocks = kvp_file_info[pool].num_blocks; int alloc_unit = sizeof(struct kvp_record) * ENTRIES_PER_BLOCK; kvp_acquire_lock(pool); filep = fopen(kvp_file_info[pool].fname, "re"); if (!filep) { syslog(LOG_ERR, "Failed to open file, pool: %d; error: %d %s", pool, errno, strerror(errno)); kvp_release_lock(pool); exit(EXIT_FAILURE); } for (;;) { readp = &record[records_read]; records_read += fread(readp, sizeof(struct kvp_record), ENTRIES_PER_BLOCK * num_blocks, filep); if (ferror(filep)) { syslog(LOG_ERR, "Failed to read file, pool: %d", pool); exit(EXIT_FAILURE); } if (!feof(filep)) { /* * We have more data to read. */ num_blocks++; record = realloc(record, alloc_unit * num_blocks); if (record == NULL) { syslog(LOG_ERR, "malloc failed"); exit(EXIT_FAILURE); } continue; } break; } kvp_file_info[pool].num_blocks = num_blocks; kvp_file_info[pool].records = record; kvp_file_info[pool].num_records = records_read; fclose(filep); kvp_release_lock(pool); }

Contributors

PersonTokensPropCommitsCommitProp
ky srinivasanky srinivasan20080.32%116.67%
ben hutchingsben hutchings3514.06%350.00%
tomas hozzatomas hozza145.62%233.33%
Total249100.00%6100.00%


static int kvp_file_init(void) { int fd; FILE *filep; size_t records_read; char *fname; struct kvp_record *record; struct kvp_record *readp; int num_blocks; int i; int alloc_unit = sizeof(struct kvp_record) * ENTRIES_PER_BLOCK; if (access(KVP_CONFIG_LOC, F_OK)) { if (mkdir(KVP_CONFIG_LOC, 0755 /* rwxr-xr-x */)) { syslog(LOG_ERR, "Failed to create '%s'; error: %d %s", KVP_CONFIG_LOC, errno, strerror(errno)); exit(EXIT_FAILURE); } } for (i = 0; i < KVP_POOL_COUNT; i++) { fname = kvp_file_info[i].fname; records_read = 0; num_blocks = 1; sprintf(fname, "%s/.kvp_pool_%d", KVP_CONFIG_LOC, i); fd = open(fname, O_RDWR | O_CREAT | O_CLOEXEC, 0644 /* rw-r--r-- */); if (fd == -1) return 1; filep = fopen(fname, "re"); if (!filep) { close(fd); return 1; } record = malloc(alloc_unit * num_blocks); if (record == NULL) { fclose(filep); close(fd); return 1; } for (;;) { readp = &record[records_read]; records_read += fread(readp, sizeof(struct kvp_record), ENTRIES_PER_BLOCK, filep); if (ferror(filep)) { syslog(LOG_ERR, "Failed to read file, pool: %d", i); exit(EXIT_FAILURE); } if (!feof(filep)) { /* * We have more data to read. */ num_blocks++; record = realloc(record, alloc_unit * num_blocks); if (record == NULL) { fclose(filep); close(fd); return 1; } continue; } break; } kvp_file_info[i].fd = fd; kvp_file_info[i].num_blocks = num_blocks; kvp_file_info[i].records = record; kvp_file_info[i].num_records = records_read; fclose(filep); } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
ky srinivasanky srinivasan29581.27%222.22%
tomas hozzatomas hozza359.64%444.44%
ben hutchingsben hutchings339.09%333.33%
Total363100.00%9100.00%


static int kvp_key_delete(int pool, const __u8 *key, int key_size) { int i; int j, k; int num_records; struct kvp_record *record; /* * First update the in-memory state. */ kvp_update_mem_state(pool); num_records = kvp_file_info[pool].num_records; record = kvp_file_info[pool].records; for (i = 0; i < num_records; i++) { if (memcmp(key, record[i].key, key_size)) continue; /* * Found a match; just move the remaining * entries up. */ if (i == num_records) { kvp_file_info[pool].num_records--; kvp_update_file(pool); return 0; } j = i; k = j + 1; for (; k < num_records; k++) { strcpy(record[j].key, record[k].key); strcpy(record[j].value, record[k].value); j++; } kvp_file_info[pool].num_records--; kvp_update_file(pool); return 0; } return 1; }

Contributors

PersonTokensPropCommitsCommitProp
ky srinivasanky srinivasan19098.96%250.00%
vitaly kuznetsovvitaly kuznetsov10.52%125.00%
tomas hozzatomas hozza10.52%125.00%
Total192100.00%4100.00%


static int kvp_key_add_or_modify(int pool, const __u8 *key, int key_size, const __u8 *value, int value_size) { int i; int num_records; struct kvp_record *record; int num_blocks; if ((key_size > HV_KVP_EXCHANGE_MAX_KEY_SIZE) || (value_size > HV_KVP_EXCHANGE_MAX_VALUE_SIZE)) return 1; /* * First update the in-memory state. */ kvp_update_mem_state(pool); num_records = kvp_file_info[pool].num_records; record = kvp_file_info[pool].records; num_blocks = kvp_file_info[pool].num_blocks; for (i = 0; i < num_records; i++) { if (memcmp(key, record[i].key, key_size)) continue; /* * Found a match; just update the value - * this is the modify case. */ memcpy(record[i].value, value, value_size); kvp_update_file(pool); return 0; } /* * Need to add a new entry; */ if (num_records == (ENTRIES_PER_BLOCK * num_blocks)) { /* Need to allocate a larger array for reg entries. */ record = realloc(record, sizeof(struct kvp_record) * ENTRIES_PER_BLOCK * (num_blocks + 1)); if (record == NULL) return 1; kvp_file_info[pool].num_blocks++; } memcpy(record[i].value, value, value_size); memcpy(record[i].key, key, key_size); kvp_file_info[pool].records = record; kvp_file_info[pool].num_records++; kvp_update_file(pool); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
ky srinivasanky srinivasan24598.39%250.00%
tomas hozzatomas hozza20.80%125.00%
vitaly kuznetsovvitaly kuznetsov20.80%125.00%
Total249100.00%4100.00%


static int kvp_get_value(int pool, const __u8 *key, int key_size, __u8 *value, int value_size) { int i; int num_records; struct kvp_record *record; if ((key_size > HV_KVP_EXCHANGE_MAX_KEY_SIZE) || (value_size > HV_KVP_EXCHANGE_MAX_VALUE_SIZE)) return 1; /* * First update the in-memory state. */ kvp_update_mem_state(pool); num_records = kvp_file_info[pool].num_records; record = kvp_file_info[pool].records; for (i = 0; i < num_records; i++) { if (memcmp(key, record[i].key, key_size)) continue; /* * Found a match; just copy the value out. */ memcpy(value, record[i].value, value_size); return 0; } return 1; }

Contributors

PersonTokensPropCommitsCommitProp
ky srinivasanky srinivasan12496.12%350.00%
olaf heringolaf hering21.55%116.67%
vitaly kuznetsovvitaly kuznetsov21.55%116.67%
tomas hozzatomas hozza10.78%116.67%
Total129100.00%6100.00%


static int kvp_pool_enumerate(int pool, int index, __u8 *key, int key_size, __u8 *value, int value_size) { struct kvp_record *record; /* * First update our in-memory database. */ kvp_update_mem_state(pool); record = kvp_file_info[pool].records; if (index >= kvp_file_info[pool].num_records) { return 1; } memcpy(key, record[index].key, key_size); memcpy(value, record[index].value, value_size); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
ky srinivasanky srinivasan9197.85%266.67%
vitaly kuznetsovvitaly kuznetsov22.15%133.33%
Total93100.00%3100.00%


void kvp_get_os_info(void) { FILE *file; char *p, buf[512]; uname(&uts_buf); os_version = uts_buf.release; os_build = strdup(uts_buf.release); os_name = uts_buf.sysname; processor_arch = uts_buf.machine; /* * The current windows host (win7) expects the build * string to be of the form: x.y.z * Strip additional information we may have. */ p = strchr(os_version, '-'); if (p) *p = '\0'; /* * Parse the /etc/os-release file if present: * http://www.freedesktop.org/software/systemd/man/os-release.html */ file = fopen("/etc/os-release", "r"); if (file != NULL) { while (fgets(buf, sizeof(buf), file)) { char *value, *q; /* Ignore comments */ if (buf[0] == '#') continue; /* Split into name=value */ p = strchr(buf, '='); if (!p) continue; *p++ = 0; /* Remove quotes and newline; un-escape */ value = p; q = p; while (*p) { if (*p == '\\') { ++p; if (!*p) break; *q++ = *p++; } else if (*p == '\'' || *p == '"' || *p == '\n') { ++p; } else { *q++ = *p++; } } *q = 0; if (!strcmp(buf, "NAME")) { p = strdup(value); if (!p) break; os_name = p; } else if (!strcmp(buf, "VERSION_ID")) { p = strdup(value); if (!p) break; os_major = p; } } fclose(file); return; } /* Fallback for older RH/SUSE releases */ file = fopen("/etc/SuSE-release", "r"); if (file != NULL) goto kvp_osinfo_found; file = fopen("/etc/redhat-release", "r"); if (file != NULL) goto kvp_osinfo_found; /* * We don't have information about the os. */ return; kvp_osinfo_found: /* up to three lines */ p = fgets(buf, sizeof(buf), file); if (p) { p = strchr(buf, '\n'); if (p) *p = '\0'; p = strdup(buf); if (!p) goto done; os_name = p; /* second line */ p = fgets(buf, sizeof(buf), file); if (p) { p = strchr(buf, '\n'); if (p) *p = '\0'; p = strdup(buf); if (!p) goto done; os_major = p; /* third line */ p = fgets(buf, sizeof(buf), file); if (p) { p = strchr(buf, '\n'); if (p) *p = '\0'; p = strdup(buf); if (p) os_minor = p; } } } done: fclose(file); return; }

Contributors

PersonTokensPropCommitsCommitProp
ben hutchingsben hutchings22844.53%120.00%
ky srinivasanky srinivasan15329.88%360.00%
olaf heringolaf hering13125.59%120.00%
Total512100.00%5100.00%

/* * Retrieve an interface name corresponding to the specified guid. * If there is a match, the function returns a pointer * to the interface name and if not, a NULL is returned. * If a match is found, the caller is responsible for * freeing the memory. */
static char *kvp_get_if_name(char *guid) { DIR *dir; struct dirent *entry; FILE *file; char *p, *q, *x; char *if_name = NULL; char buf[256]; char *kvp_net_dir = "/sys/class/net/"; char dev_id[256]; dir = opendir(kvp_net_dir); if (dir == NULL) return NULL; snprintf(dev_id, sizeof(dev_id), "%s", kvp_net_dir); q = dev_id + strlen(kvp_net_dir); while ((entry = readdir(dir)) != NULL) { /* * Set the state for the next pass. */ *q = '\0'; strcat(dev_id, entry->d_name); strcat(dev_id, "/device/device_id"); file = fopen(dev_id, "r"); if (file == NULL) continue; p = fgets(buf, sizeof(buf), file); if (p) { x = strchr(p, '\n'); if (x) *x = '\0'; if (!strcmp(p, guid)) { /* * Found the guid match; return the interface * name. The caller will free the memory. */ if_name = strdup(entry->d_name); fclose(file); break; } } fclose(file); } closedir(dir); return if_name; }

Contributors

PersonTokensPropCommitsCommitProp
ky srinivasanky srinivasan229100.00%2100.00%
Total229100.00%2100.00%

/* * Retrieve the MAC address given the interface name. */
static char *kvp_if_name_to_mac(char *if_name) { FILE *file; char *p, *x; char buf[256]; char addr_file[256]; unsigned int i; char *mac_addr = NULL; snprintf(addr_file, sizeof(addr_file), "%s%s%s", "/sys/class/net/", if_name, "/address"); file = fopen(addr_file, "r"); if (file == NULL) return NULL; p = fgets(buf, sizeof(buf), file); if (p) { x = strchr(p, '\n'); if (x) *x = '\0'; for (i = 0; i < strlen(p); i++) p[i] = toupper(p[i]); mac_addr = strdup(p); } fclose(file); return mac_addr; }

Contributors

PersonTokensPropCommitsCommitProp
ky srinivasanky srinivasan16199.38%480.00%
vitaly kuznetsovvitaly kuznetsov10.62%120.00%
Total162100.00%5100.00%

/* * Retrieve the interface name given tha MAC address. */
static char *kvp_mac_to_if_name(char *mac) { DIR *dir; struct dirent *entry; FILE *file; char *p, *q, *x; char *if_name = NULL; char buf[256]; char *kvp_net_dir = "/sys/class/net/"; char dev_id[256]; unsigned int i; dir = opendir(kvp_net_dir); if (dir == NULL) return NULL; snprintf(dev_id, sizeof(dev_id), "%s", kvp_net_dir); q = dev_id + strlen(kvp_net_dir); while ((entry = readdir(dir)) != NULL) { /* * Set the state for the next pass. */ *q = '\0'; strcat(dev_id, entry->d_name); strcat(dev_id, "/address"); file = fopen(dev_id, "r"); if (file == NULL) continue; p =