cregit-Linux how code gets into the kernel

Release 4.18 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 <limits.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 KVP_NET_DIR "/sys/class/net/"


#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
K. Y. Srinivasan6688.00%133.33%
Tomas Hozza810.67%133.33%
Ben 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
K. Y. Srinivasan6688.00%133.33%
Tomas Hozza810.67%133.33%
Ben 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
K. Y. Srinivasan8565.89%116.67%
Ben Hutchings3023.26%350.00%
Tomas 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 - records_read, filep); if (ferror(filep)) { syslog(LOG_ERR, "Failed to read file, pool: %d; error: %d %s", pool, errno, strerror(errno)); kvp_release_lock(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"); kvp_release_lock(pool); 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
K. Y. Srinivasan20074.63%114.29%
Ben Hutchings3412.69%342.86%
Paul Meyer207.46%114.29%
Tomas Hozza145.22%228.57%
Total268100.00%7100.00%


static int kvp_file_init(void) { int fd; char *fname; 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; 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; kvp_file_info[i].fd = fd; kvp_file_info[i].num_blocks = 1; kvp_file_info[i].records = malloc(alloc_unit); if (kvp_file_info[i].records == NULL) return 1; kvp_file_info[i].num_records = 0; kvp_update_mem_state(i); } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
K. Y. Srinivasan15177.44%225.00%
Paul Meyer2211.28%112.50%
Tomas Hozza178.72%337.50%
Ben Hutchings52.56%225.00%
Total195100.00%8100.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
K. Y. Srinivasan19098.96%250.00%
Vitaly Kuznetsov10.52%125.00%
Tomas 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
K. Y. Srinivasan24598.39%250.00%
Vitaly Kuznetsov20.80%125.00%
Tomas Hozza20.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
K. Y. Srinivasan12496.12%350.00%
Olaf Hering21.55%116.67%
Vitaly Kuznetsov21.55%116.67%
Tomas 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
K. Y. Srinivasan9197.85%266.67%
Vitaly 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 Hutchings22844.53%120.00%
K. Y. Srinivasan15329.88%360.00%
Olaf 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, *x; char *if_name = NULL; char buf[256]; char dev_id[PATH_MAX]; dir = opendir(KVP_NET_DIR); if (dir == NULL) return NULL; while ((entry = readdir(dir)) != NULL) { /* * Set the state for the next pass. */ snprintf(dev_id, sizeof(dev_id), "%s%s/device/device_id", KVP_NET_DIR, entry->d_name); 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
K. Y. Srinivasan18293.81%266.67%
Vitaly Kuznetsov126.19%133.33%
Total194100.00%3100.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[PATH_MAX]; unsigned int i; char *mac_addr = NULL; snprintf(addr_file, sizeof(addr_file), "%s%s%s", KVP_NET_DIR, 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
K. Y. Srinivasan15998.15%360.00%
Vitaly Kuznetsov31.85%240.00%
Total162100.00%5100.00%


static void kvp_process_ipconfig_file(char *cmd, char *config_buf, unsigned int len, int element_size, int offset) { char buf[256]; char *p; char *x; FILE *file; /* * First execute the command. */ file = popen(cmd, "r"); if (file == NULL) return; if (offset == 0) memset(config_buf, 0, len); while ((p = fgets(buf, sizeof(buf), file)) != NULL) { if (len < strlen(config_buf) + element_size + 1) break; x = strchr(p, '\n'); if (x) *x = '\0'; strcat(config_buf, p); strcat(config_buf, ";"); } pclose(file); }

Contributors

PersonTokensPropCommitsCommitProp
K. Y. Srinivasan14095.24%360.00%
Tomas Hozza42.72%120.00%
Vitaly Kuznetsov32.04%120.00%
Total147100.00%5100.00%


static void kvp_get_ipconfig_info(char *if_name, struct hv_kvp_ipaddr_value *buffer) { char cmd[512]; char dhcp_info[128]; char *p; FILE *file; /* * Get the address of default gateway (ipv4). */ sprintf(cmd, "%s %s", "ip route show dev", if_name); strcat(cmd, " | awk '/default/ {print $3 }'"); /* * Execute the command to gather gateway info. */ kvp_process_ipconfig_file(cmd, (char *)