/*
* 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 <stdbool.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
* 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
};
enum {
IPV4 = 1 ,
IPV6,
IP_TYPE_MAX
};
static int in_hand_shake;
static int debug;
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
/*
* Change this entry if the number of addresses increases in future
*/
#define MAX_IP_ENTRIES 64
#define OUTSTR_BUF_SIZE ((INET6_ADDRSTRLEN + 1 ) * MAX_IP_ENTRIES)
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);
}
}
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);
}
}
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);
}
static void kvp_dump_initial_pools(int pool)
{
int i;
syslog(LOG_DEBUG, "===Start dumping the contents of pool %d ===\n" ,
pool);
for (i = 0 ; i < kvp_file_info[pool].num_records; i++)
syslog(LOG_DEBUG, "pool: %d, %d/%d key=%s val=%s\n" ,
pool, i + 1 , kvp_file_info[pool].num_records,
kvp_file_info[pool].records[i].key,
kvp_file_info[pool].records[i].value);
}
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);
}
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);
if (debug)
kvp_dump_initial_pools(i);
}
return 0 ;
}
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 (debug)
syslog(LOG_DEBUG, "%s: deleting the KVP: pool=%d key=%s val=%s" ,
__func__, pool, record[i].key, record[i].value);
if (i == (num_records - 1 )) {
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 ;
}
if (debug)
syslog(LOG_DEBUG, "%s: could not delete KVP: pool=%d key=%s. Record not found" ,
__func__, pool, key);
return 1 ;
}
static int kvp_key_add_or_modify(int pool, const __u8 *key, int key_size,
const __u8 *value, int value_size)
{
struct kvp_record *record;
int num_records;
int num_blocks;
int i;
if (debug)
syslog(LOG_DEBUG, "%s: got a KVP: pool=%d key=%s val=%s" ,
__func__, pool, key, value);
if ((key_size > HV_KVP_EXCHANGE_MAX_KEY_SIZE) ||
(value_size > HV_KVP_EXCHANGE_MAX_VALUE_SIZE)) {
syslog(LOG_ERR, "%s: Too long key or value: key=%s, val=%s" ,
__func__, key, value);
if (debug)
syslog(LOG_DEBUG, "%s: Too long key or value: pool=%d, key=%s, val=%s" ,
__func__, pool, key, value);
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);
if (debug)
syslog(LOG_DEBUG, "%s: updated: pool=%d key=%s val=%s" ,
__func__, pool, key, value);
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) {
syslog(LOG_ERR, "%s: Memory alloc failure" , __func__);
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++;
if (debug)
syslog(LOG_DEBUG, "%s: added: pool=%d key=%s val=%s" ,
__func__, pool, key, value);
kvp_update_file(pool);
return 0 ;
}
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 ;
}
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 ;
}
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 :
* https : //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 ;
}
/*
* 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;
}
/*
* 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;
}
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);
}
static bool kvp_verify_ip_address(const void *address_string)
{
char verify_buf[sizeof (struct in6_addr)];
if (inet_pton(AF_INET, address_string, verify_buf) == 1 )
return true ;
if (inet_pton(AF_INET6, address_string, verify_buf) == 1 )
return true ;
return false ;
}
static void kvp_extract_routes(const char *line, void **output, size_t *remaining)
{
static const char needle[] = "via " ;
const char *match, *haystack = line;
while ((match = strstr(haystack, needle))) {
const char *address, *next_char;
/* Address starts after needle. */
address = match + strlen(needle);
/* The char following address is a space or end of line. */
next_char = strpbrk(address, " \t\\" );
if (!next_char)
next_char = address + strlen(address) + 1 ;
/* Enough room for address and semicolon. */
if (*remaining >= (next_char - address) + 1 ) {
memcpy(*output, address, next_char - address);
/* Terminate string for verification. */
memcpy(*output + (next_char - address), "" , 1 );
if (kvp_verify_ip_address(*output)) {
/* Advance output buffer. */
*output += next_char - address;
*remaining -= next_char - address;
/* Each address needs a trailing semicolon. */
memcpy(*output, ";" , 1 );
*output += 1 ;
*remaining -= 1 ;
}
}
haystack = next_char;
}
}
static void kvp_get_gateway(void *buffer, size_t buffer_len)
{
static const char needle[] = "default " ;
FILE *f;
void *output = buffer;
char *line = NULL;
size_t alloc_size = 0 , remaining = buffer_len - 1 ;
ssize_t num_chars;
/* Show route information in a single line, for each address family */
f = popen("ip --oneline -4 route show;ip --oneline -6 route show" , "r" );
if (!f) {
/* Convert buffer into C-String. */
memcpy(output, "" , 1 );
return ;
}
while ((num_chars = getline(&line, &alloc_size, f)) > 0 ) {
/* Skip short lines. */
if (num_chars <= strlen(needle))
continue ;
/* Skip lines without default route. */
if (memcmp(line, needle, strlen(needle)))
continue ;
/* Remove trailing newline to simplify further parsing. */
if (line[num_chars - 1 ] == '\n' )
line[num_chars - 1 ] = '\0' ;
/* Search routes after match. */
kvp_extract_routes(line + strlen(needle), &output, &remaining);
}
/* Convert buffer into C-String. */
memcpy(output, "" , 1 );
free(line);
pclose(f);
}
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;
kvp_get_gateway(buffer->gate_way, sizeof (buffer->gate_way));
/*
* Gather the DNS state .
* Since there is no standard way to get this information
* across various distributions of interest ; we just invoke
* an external script that needs to be ported across distros
* of interest .
*
* Following is the expected format of the information from the script :
*
* ipaddr1 ( nameserver1 )
* ipaddr2 ( nameserver2 )
* .
* .
*/
sprintf(cmd, "exec %s %s" , KVP_SCRIPTS_PATH "hv_get_dns_info" , if_name);
/*
* Execute the command to gather DNS info .
*/
kvp_process_ipconfig_file(cmd, (char *)buffer->dns_addr,
(MAX_IP_ADDR_SIZE * 2 ), INET_ADDRSTRLEN, 0 );
/*
* Gather the DHCP state .
* We will gather this state by invoking an external script .
* The parameter to the script is the interface name .
* Here is the expected output :
*
* Enabled : DHCP enabled .
*/
sprintf(cmd, "exec %s %s" , KVP_SCRIPTS_PATH "hv_get_dhcp_info" , if_name);
file = popen(cmd, "r" );
if (file == NULL)
return ;
p = fgets(dhcp_info, sizeof (dhcp_info), file);
if (p == NULL) {
pclose(file);
return ;
}
if (!strncmp(p, "Enabled" , 7 ))
buffer->dhcp_enabled = 1 ;
else
buffer->dhcp_enabled = 0 ;
pclose(file);
}
static unsigned int hweight32(unsigned int *w)
{
unsigned int res = *w - ((*w >> 1 ) & 0 x55555555);
res = (res & 0 x33333333) + ((res >> 2 ) & 0 x33333333);
res = (res + (res >> 4 )) & 0 x0F0F0F0F;
res = res + (res >> 8 );
return (res + (res >> 16 )) & 0 x000000FF;
}
static int kvp_process_ip_address(void *addrp,
int family, char *buffer,
int length, int *offset)
{
struct sockaddr_in *addr;
struct sockaddr_in6 *addr6;
int addr_length;
char tmp[50 ];
const char *str;
if (family == AF_INET) {
addr = addrp;
str = inet_ntop(family, &addr->sin_addr, tmp, 50 );
addr_length = INET_ADDRSTRLEN;
} else {
addr6 = addrp;
str = inet_ntop(family, &addr6->sin6_addr.s6_addr, tmp, 50 );
addr_length = INET6_ADDRSTRLEN;
}
if ((length - *offset) < addr_length + 2 )
return HV_E_FAIL;
if (str == NULL) {
strcpy(buffer, "inet_ntop failed\n" );
return HV_E_FAIL;
}
if (*offset == 0 )
strcpy(buffer, tmp);
else {
strcat(buffer, ";" );
strcat(buffer, tmp);
}
*offset += strlen(str) + 1 ;
return 0 ;
}
static int
kvp_get_ip_info(int family, char *if_name, int op,
void *out_buffer, unsigned int length)
{
struct ifaddrs *ifap;
struct ifaddrs *curp;
int offset = 0 ;
int sn_offset = 0 ;
int error = 0 ;
char *buffer;
struct hv_kvp_ipaddr_value *ip_buffer = NULL;
char cidr_mask[5 ]; /* /xyz */
int weight;
int i;
unsigned int *w;
char *sn_str;
struct sockaddr_in6 *addr6;
if (op == KVP_OP_ENUMERATE) {
buffer = out_buffer;
} else {
ip_buffer = out_buffer;
buffer = (char *)ip_buffer->ip_addr;
ip_buffer->addr_family = 0 ;
}
/*
* On entry into this function , the buffer is capable of holding the
* maximum key value .
*/
if (getifaddrs(&ifap)) {
strcpy(buffer, "getifaddrs failed\n" );
return HV_E_FAIL;
}
curp = ifap;
while (curp != NULL) {
if (curp->ifa_addr == NULL) {
curp = curp->ifa_next;
continue ;
}
if ((if_name != NULL) &&
(strncmp(curp->ifa_name, if_name, strlen(if_name)))) {
/*
* We want info about a specific interface ;
* just continue .
*/
curp = curp->ifa_next;
continue ;
}
/*
* We only support two address families : AF_INET and AF_INET6 .
* If a family value of 0 is specified , we collect both
* supported address families ; if not we gather info on
* the specified address family .
*/
if ((((family != 0 ) &&
(curp->ifa_addr->sa_family != family))) ||
(curp->ifa_flags & IFF_LOOPBACK)) {
curp = curp->ifa_next;
continue ;
}
if ((curp->ifa_addr->sa_family != AF_INET) &&
(curp->ifa_addr->sa_family != AF_INET6)) {
curp = curp->ifa_next;
continue ;
}
if (op == KVP_OP_GET_IP_INFO) {
/*
* Gather info other than the IP address .
* IP address info will be gathered later .
*/
if (curp->ifa_addr->sa_family == AF_INET) {
ip_buffer->addr_family |= ADDR_FAMILY_IPV4;
/*
* Get subnet info .
*/
error = kvp_process_ip_address(
curp->ifa_netmask,
AF_INET,
(char *)
ip_buffer->sub_net,
length,
&sn_offset);
if (error)
goto gather_ipaddr;
} else {
ip_buffer->addr_family |= ADDR_FAMILY_IPV6;
/*
* Get subnet info in CIDR format .
*/
weight = 0 ;
sn_str = (char *)ip_buffer->sub_net;
addr6 = (struct sockaddr_in6 *)
curp->ifa_netmask;
w = addr6->sin6_addr.s6_addr32;
for (i = 0 ; i < 4 ; i++)
weight += hweight32(&w[i]);
sprintf(cidr_mask, "/%d" , weight);
if (length < sn_offset + strlen(cidr_mask) + 1 )
goto gather_ipaddr;
if (sn_offset == 0 )
strcpy(sn_str, cidr_mask);
else {
strcat((char *)ip_buffer->sub_net, ";" );
strcat(sn_str, cidr_mask);
}
sn_offset += strlen(sn_str) + 1 ;
}
/*
* Collect other ip related configuration info .
*/
kvp_get_ipconfig_info(if_name, ip_buffer);
}
gather_ipaddr:
error = kvp_process_ip_address(curp->ifa_addr,
curp->ifa_addr->sa_family,
buffer,
length, &offset);
if (error)
goto getaddr_done;
curp = curp->ifa_next;
}
getaddr_done:
freeifaddrs(ifap);
return error;
}
/*
* Retrieve the IP given the MAC address .
*/
static int kvp_mac_to_ip(struct hv_kvp_ipaddr_value *kvp_ip_val)
{
char *mac = (char *)kvp_ip_val->adapter_id;
DIR *dir;
struct dirent *entry;
FILE *file;
char *p, *x;
char *if_name = NULL;
char buf[256 ];
char dev_id[PATH_MAX];
unsigned int i;
int error = HV_E_FAIL;
dir = opendir(KVP_NET_DIR);
if (dir == NULL)
return HV_E_FAIL;
while ((entry = readdir(dir)) != NULL) {
/*
* Set the state for the next pass .
*/
snprintf(dev_id, sizeof (dev_id), "%s%s/address" , KVP_NET_DIR,
entry->d_name);
file = fopen(dev_id, "r" );
if (file == NULL)
continue ;
p = fgets(buf, sizeof (buf), file);
fclose(file);
if (!p)
continue ;
x = strchr(p, '\n' );
if (x)
*x = '\0' ;
for (i = 0 ; i < strlen(p); i++)
p[i] = toupper(p[i]);
if (strcmp(p, mac))
continue ;
/*
* Found the MAC match .
* A NIC ( e . g . VF ) matching the MAC , but without IP , is skipped .
*/
if_name = entry->d_name;
if (!if_name)
continue ;
error = kvp_get_ip_info(0 , if_name, KVP_OP_GET_IP_INFO,
kvp_ip_val, MAX_IP_ADDR_SIZE * 2 );
if (!error && strlen((char *)kvp_ip_val->ip_addr))
break ;
}
closedir(dir);
return error;
}
static int expand_ipv6(char *addr, int type)
{
int ret;
struct in6_addr v6_addr;
ret = inet_pton(AF_INET6, addr, &v6_addr);
if (ret != 1 ) {
if (type == NETMASK)
return 1 ;
return 0 ;
}
sprintf(addr, "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:"
"%02x%02x:%02x%02x:%02x%02x" ,
(int )v6_addr.s6_addr[0 ], (int )v6_addr.s6_addr[1 ],
(int )v6_addr.s6_addr[2 ], (int )v6_addr.s6_addr[3 ],
(int )v6_addr.s6_addr[4 ], (int )v6_addr.s6_addr[5 ],
(int )v6_addr.s6_addr[6 ], (int )v6_addr.s6_addr[7 ],
(int )v6_addr.s6_addr[8 ], (int )v6_addr.s6_addr[9 ],
(int )v6_addr.s6_addr[10 ], (int )v6_addr.s6_addr[11 ],
(int )v6_addr.s6_addr[12 ], (int )v6_addr.s6_addr[13 ],
(int )v6_addr.s6_addr[14 ], (int )v6_addr.s6_addr[15 ]);
return 1 ;
}
static int is_ipv4(char *addr)
{
int ret;
struct in_addr ipv4_addr;
ret = inet_pton(AF_INET, addr, &ipv4_addr);
if (ret == 1 )
return 1 ;
return 0 ;
}
static int parse_ip_val_buffer(char *in_buf, int *offset,
char *out_buf, int out_len)
{
char *x;
char *start;
/*
* in_buf has sequence of characters that are separated by
* the character ' ; ' . The last sequence does not have the
* terminating " ; " character .
*/
start = in_buf + *offset;
x = strchr(start, ';' );
if (x)
*x = 0 ;
else
x = start + strlen(start);
if (strlen(start) != 0 ) {
int i = 0 ;
/*
* Get rid of leading spaces .
*/
while (start[i] == ' ' )
i++;
if ((x - start) <= out_len) {
strcpy(out_buf, (start + i));
*offset += (x - start) + 1 ;
return 1 ;
}
}
return 0 ;
}
static int kvp_write_file(FILE *f, char *s1, char *s2, char *s3)
{
int ret;
ret = fprintf(f, "%s%s%s%s\n" , s1, s2, "=" , s3);
if (ret < 0 )
return HV_E_FAIL;
return 0 ;
}
static int process_ip_string(FILE *f, char *ip_string, int type)
{
int error = 0 ;
char addr[INET6_ADDRSTRLEN];
int i = 0 ;
int j = 0 ;
char str[256 ];
char sub_str[13 ];
int offset = 0 ;
memset(addr, 0 , sizeof (addr));
while (parse_ip_val_buffer(ip_string, &offset, addr,
(MAX_IP_ADDR_SIZE * 2 ))) {
sub_str[0 ] = 0 ;
if (is_ipv4(addr)) {
switch (type) {
case IPADDR:
snprintf(str, sizeof (str), "%s" , "IPADDR" );
break ;
case NETMASK:
snprintf(str, sizeof (str), "%s" , "NETMASK" );
break ;
case GATEWAY:
snprintf(str, sizeof (str), "%s" , "GATEWAY" );
break ;
case DNS:
snprintf(str, sizeof (str), "%s" , "DNS" );
break ;
}
if (type == DNS) {
snprintf(sub_str, sizeof (sub_str), "%d" , ++i);
} else if (type == GATEWAY && i == 0 ) {
++i;
} else {
snprintf(sub_str, sizeof (sub_str), "%d" , i++);
}
} else if (expand_ipv6(addr, type)) {
switch (type) {
case IPADDR:
snprintf(str, sizeof (str), "%s" , "IPV6ADDR" );
break ;
case NETMASK:
snprintf(str, sizeof (str), "%s" , "IPV6NETMASK" );
break ;
case GATEWAY:
snprintf(str, sizeof (str), "%s" ,
"IPV6_DEFAULTGW" );
break ;
case DNS:
snprintf(str, sizeof (str), "%s" , "DNS" );
break ;
}
if (type == DNS) {
snprintf(sub_str, sizeof (sub_str), "%d" , ++i);
} else if (j == 0 ) {
++j;
} else {
snprintf(sub_str, sizeof (sub_str), "_%d" , j++);
}
} else {
return HV_INVALIDARG;
}
error = kvp_write_file(f, str, sub_str, addr);
if (error)
return error;
memset(addr, 0 , sizeof (addr));
}
return 0 ;
}
int ip_version_check(const char *input_addr)
{
struct in6_addr addr;
if (inet_pton(AF_INET, input_addr, &addr))
return IPV4;
else if (inet_pton(AF_INET6, input_addr, &addr))
return IPV6;
return -EINVAL;
}
/*
* Only IPv4 subnet strings needs to be converted to plen
* For IPv6 the subnet is already privided in plen format
*/
static int kvp_subnet_to_plen(char *subnet_addr_str)
{
int plen = 0 ;
struct in_addr subnet_addr4;
/*
* Convert subnet address to binary representation
*/
if (inet_pton(AF_INET, subnet_addr_str, &subnet_addr4) == 1 ) {
uint32_t subnet_mask = ntohl(subnet_addr4.s_addr);
while (subnet_mask & 0 x80000000) {
plen++;
subnet_mask <<= 1 ;
}
} else {
return -1 ;
}
return plen;
}
static int process_dns_gateway_nm(FILE *f, char *ip_string, int type,
int ip_sec)
{
char addr[INET6_ADDRSTRLEN], *output_str;
int ip_offset = 0 , error = 0 , ip_ver;
char *param_name;
if (type == DNS)
param_name = "dns" ;
else if (type == GATEWAY)
param_name = "gateway" ;
else
return -EINVAL;
output_str = (char *)calloc(OUTSTR_BUF_SIZE, sizeof (char ));
if (!output_str)
return -ENOMEM;
while (1 ) {
memset(addr, 0 , sizeof (addr));
if (!parse_ip_val_buffer(ip_string, &ip_offset, addr,
(MAX_IP_ADDR_SIZE * 2 )))
break ;
ip_ver = ip_version_check(addr);
if (ip_ver < 0 )
continue ;
if ((ip_ver == IPV4 && ip_sec == IPV4) ||
(ip_ver == IPV6 && ip_sec == IPV6)) {
/*
* do a bound check to avoid out - of bound writes
*/
if ((OUTSTR_BUF_SIZE - strlen(output_str)) >
(strlen(addr) + 1 )) {
strncat(output_str, addr,
OUTSTR_BUF_SIZE -
strlen(output_str) - 1 );
strncat(output_str, "," ,
OUTSTR_BUF_SIZE -
strlen(output_str) - 1 );
}
} else {
continue ;
}
}
if (strlen(output_str)) {
/*
* This is to get rid of that extra comma character
* in the end of the string
*/
output_str[strlen(output_str) - 1 ] = '\0' ;
error = fprintf(f, "%s=%s\n" , param_name, output_str);
}
free(output_str);
return error;
}
static int process_ip_string_nm(FILE *f, char *ip_string, char *subnet,
int ip_sec)
{
char addr[INET6_ADDRSTRLEN];
char subnet_addr[INET6_ADDRSTRLEN];
int error = 0 , i = 0 ;
int ip_offset = 0 , subnet_offset = 0 ;
int plen, ip_ver;
memset(addr, 0 , sizeof (addr));
memset(subnet_addr, 0 , sizeof (subnet_addr));
while (parse_ip_val_buffer(ip_string, &ip_offset, addr,
(MAX_IP_ADDR_SIZE * 2 )) &&
parse_ip_val_buffer(subnet,
&subnet_offset,
subnet_addr,
(MAX_IP_ADDR_SIZE *
2 ))) {
ip_ver = ip_version_check(addr);
if (ip_ver < 0 )
continue ;
if (ip_ver == IPV4 && ip_sec == IPV4)
plen = kvp_subnet_to_plen((char *)subnet_addr);
else if (ip_ver == IPV6 && ip_sec == IPV6)
plen = atoi(subnet_addr);
else
continue ;
if (plen < 0 )
return plen;
error = fprintf(f, "address%d=%s/%d\n" , ++i, (char *)addr,
plen);
if (error < 0 )
return error;
memset(addr, 0 , sizeof (addr));
memset(subnet_addr, 0 , sizeof (subnet_addr));
}
return error;
}
static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val)
{
int error = 0 , ip_ver;
char if_filename[PATH_MAX];
char nm_filename[PATH_MAX];
FILE *ifcfg_file, *nmfile;
char cmd[PATH_MAX];
char *mac_addr;
int str_len;
/*
* Set the configuration for the specified interface with
* the information provided . Since there is no standard
* way to configure an interface , we will have an external
* script that does the job of configuring the interface and
* flushing the configuration .
*
* The parameters passed to this external script are :
* 1 . A configuration file that has the specified configuration .
*
* We will embed the name of the interface in the configuration
* file : ifcfg - ethx ( where ethx is the interface name ) .
*
* The information provided here may be more than what is needed
* in a given distro to configure the interface and so are free
* ignore information that may not be relevant .
*
* Here is the ifcfg format of the ip configuration file :
*
* HWADDR = macaddr
* DEVICE = interface name
* BOOTPROTO = < protocol > ( where < protocol > is " dhcp " if DHCP is configured
* or " none " if no boot - time protocol should be used )
*
* IPADDR0 = ipaddr1
* IPADDR1 = ipaddr2
* IPADDRx = ipaddry ( where y = x + 1 )
*
* NETMASK0 = netmask1
* NETMASKx = netmasky ( where y = x + 1 )
*
* GATEWAY = ipaddr1
* GATEWAYx = ipaddry ( where y = x + 1 )
*
* DNSx = ipaddrx ( where first DNS address is tagged as DNS1 etc )
*
* IPV6 addresses will be tagged as IPV6ADDR , IPV6 gateway will be
* tagged as IPV6_DEFAULTGW and IPV6 NETMASK will be tagged as
* IPV6NETMASK .
*
* Here is the keyfile format of the ip configuration file :
*
* [ ethernet ]
* mac - address = macaddr
* [ connection ]
* interface - name = interface name
*
* [ ipv4 ]
* method = < protocol > ( where < protocol > is " auto " if DHCP is configured
* or " manual " if no boot - time protocol should be used )
*
* address1 = ipaddr1 / plen
* address2 = ipaddr2 / plen
*
* gateway = gateway1 ; gateway2
*
* dns = dns1 ; dns2
*
* [ ipv6 ]
* address1 = ipaddr1 / plen
* address2 = ipaddr2 / plen
*
* gateway = gateway1 ; gateway2
*
* dns = dns1 ; dns2
*
* The host can specify multiple ipv4 and ipv6 addresses to be
* configured for the interface . Furthermore , the configuration
* needs to be persistent . A subsequent GET call on the interface
* is expected to return the configuration that is set via the SET
* call .
*/
/*
* We are populating both ifcfg and nmconnection files
*/
snprintf(if_filename, sizeof (if_filename), "%s%s%s" , KVP_CONFIG_LOC,
"/ifcfg-" , if_name);
ifcfg_file = fopen(if_filename, "w" );
if (!ifcfg_file) {
syslog(LOG_ERR, "Failed to open config file; error: %d %s" ,
errno, strerror(errno));
return HV_E_FAIL;
}
snprintf(nm_filename, sizeof (nm_filename), "%s%s%s%s" , KVP_CONFIG_LOC,
"/" , if_name, ".nmconnection" );
nmfile = fopen(nm_filename, "w" );
if (!nmfile) {
syslog(LOG_ERR, "Failed to open config file; error: %d %s" ,
errno, strerror(errno));
fclose(ifcfg_file);
return HV_E_FAIL;
}
/*
* First write out the MAC address .
*/
mac_addr = kvp_if_name_to_mac(if_name);
if (mac_addr == NULL) {
error = HV_E_FAIL;
goto setval_error;
}
error = kvp_write_file(ifcfg_file, "HWADDR" , "" , mac_addr);
if (error < 0 )
goto setmac_error;
error = kvp_write_file(ifcfg_file, "DEVICE" , "" , if_name);
if (error < 0 )
goto setmac_error;
error = fprintf(nmfile, "\n[connection]\n" );
if (error < 0 )
goto setmac_error;
error = kvp_write_file(nmfile, "interface-name" , "" , if_name);
if (error)
goto setmac_error;
error = fprintf(nmfile, "\n[ethernet]\n" );
if (error < 0 )
goto setmac_error;
error = kvp_write_file(nmfile, "mac-address" , "" , mac_addr);
if (error)
goto setmac_error;
free(mac_addr);
/*
* The dhcp_enabled flag is only for IPv4 . In the case the host only
* injects an IPv6 address , the flag is true , but we still need to
* proceed to parse and pass the IPv6 information to the
* disto - specific script hv_set_ifconfig .
*/
/*
* First populate the ifcfg file format
*/
if (new_val->dhcp_enabled) {
error = kvp_write_file(ifcfg_file, "BOOTPROTO" , "" , "dhcp" );
if (error)
goto setval_error;
} else {
error = kvp_write_file(ifcfg_file, "BOOTPROTO" , "" , "none" );
if (error)
goto setval_error;
}
error = process_ip_string(ifcfg_file, (char *)new_val->ip_addr,
IPADDR);
if (error)
goto setval_error;
error = process_ip_string(ifcfg_file, (char *)new_val->sub_net,
NETMASK);
if (error)
goto setval_error;
error = process_ip_string(ifcfg_file, (char *)new_val->gate_way,
GATEWAY);
if (error)
goto setval_error;
error = process_ip_string(ifcfg_file, (char *)new_val->dns_addr, DNS);
if (error)
goto setval_error;
/*
* Now we populate the keyfile format
*
* The keyfile format expects the IPv6 and IPv4 configuration in
* different sections . Therefore we iterate through the list twice ,
* once to populate the IPv4 section and the next time for IPv6
*/
ip_ver = IPV4;
do {
if (ip_ver == IPV4) {
error = fprintf(nmfile, "\n[ipv4]\n" );
if (error < 0 )
goto setval_error;
} else {
error = fprintf(nmfile, "\n[ipv6]\n" );
if (error < 0 )
goto setval_error;
}
/*
* Write the configuration for ipaddress , netmask , gateway and
* name services
*/
error = process_ip_string_nm(nmfile, (char *)new_val->ip_addr,
(char *)new_val->sub_net,
ip_ver);
if (error < 0 )
goto setval_error;
/*
* As dhcp_enabled is only valid for ipv4 , we do not set dhcp
* methods for ipv6 based on dhcp_enabled flag .
*
* For ipv4 , set method to manual only when dhcp_enabled is
* false and specific ipv4 addresses are configured . If neither
* dhcp_enabled is true and no ipv4 addresses are configured ,
* set method to ' disabled ' .
*
* For ipv6 , set method to manual when we configure ipv6
* addresses . Otherwise set method to ' auto ' so that SLAAC from
* RA may be used .
*/
if (ip_ver == IPV4) {
if (new_val->dhcp_enabled) {
error = kvp_write_file(nmfile, "method" , "" ,
"auto" );
if (error < 0 )
goto setval_error;
} else if (error) {
error = kvp_write_file(nmfile, "method" , "" ,
"manual" );
if (error < 0 )
goto setval_error;
} else {
error = kvp_write_file(nmfile, "method" , "" ,
"disabled" );
if (error < 0 )
goto setval_error;
}
} else if (ip_ver == IPV6) {
if (error) {
error = kvp_write_file(nmfile, "method" , "" ,
"manual" );
if (error < 0 )
goto setval_error;
} else {
error = kvp_write_file(nmfile, "method" , "" ,
"auto" );
if (error < 0 )
goto setval_error;
}
}
error = process_dns_gateway_nm(nmfile,
(char *)new_val->gate_way,
GATEWAY, ip_ver);
if (error < 0 )
goto setval_error;
error = process_dns_gateway_nm(nmfile,
(char *)new_val->dns_addr, DNS,
ip_ver);
if (error < 0 )
goto setval_error;
ip_ver++;
} while (ip_ver < IP_TYPE_MAX);
fclose(nmfile);
fclose(ifcfg_file);
/*
* Now that we have populated the configuration file ,
* invoke the external script to do its magic .
*/
str_len = snprintf(cmd, sizeof (cmd), "exec %s %s %s" ,
KVP_SCRIPTS_PATH "hv_set_ifconfig" ,
if_filename, nm_filename);
/*
* This is a little overcautious , but it ' s necessary to suppress some
* false warnings from gcc 8 . 0 . 1 .
*/
if (str_len <= 0 || (unsigned int )str_len >= sizeof (cmd)) {
syslog(LOG_ERR, "Cmd '%s' (len=%d) may be too long" ,
cmd, str_len);
return HV_E_FAIL;
}
if (system(cmd)) {
syslog(LOG_ERR, "Failed to execute cmd '%s'; error: %d %s" ,
cmd, errno, strerror(errno));
return HV_E_FAIL;
}
return 0 ;
setmac_error:
free(mac_addr);
setval_error:
syslog(LOG_ERR, "Failed to write config file" );
fclose(ifcfg_file);
fclose(nmfile);
return error;
}
static void
kvp_get_domain_name(char *buffer, int length)
{
struct addrinfo hints, *info ;
int error = 0 ;
gethostname(buffer, length);
memset(&hints, 0 , sizeof (hints));
hints.ai_family = AF_INET; /*Get only ipv4 addrinfo. */
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_CANONNAME;
error = getaddrinfo(buffer, NULL, &hints, &info);
if (error != 0 ) {
snprintf(buffer, length, "getaddrinfo failed: 0x%x %s" ,
error, gai_strerror(error));
return ;
}
snprintf(buffer, length, "%s" , info->ai_canonname);
freeaddrinfo(info);
}
void print_usage(char *argv[])
{
fprintf(stderr, "Usage: %s [options]\n"
"Options are:\n"
" -n, --no-daemon stay in foreground, don't daemonize\n"
" -d, --debug Enable debug logs(syslog debug by default)\n"
" -h, --help print this help\n" , argv[0 ]);
}
int main(int argc, char *argv[])
{
int kvp_fd = -1 , len;
int error;
struct pollfd pfd;
char *p;
struct hv_kvp_msg hv_msg[1 ];
char *key_value;
char *key_name;
int op;
int pool;
char *if_name;
struct hv_kvp_ipaddr_value *kvp_ip_val;
int daemonize = 1 , long_index = 0 , opt;
static struct option long_options[] = {
{"help" , no_argument, 0 , 'h' },
{"no-daemon" , no_argument, 0 , 'n' },
{"debug" , no_argument, 0 , 'd' },
{0 , 0 , 0 , 0 }
};
while ((opt = getopt_long(argc, argv, "hnd" , long_options,
&long_index)) != -1 ) {
switch (opt) {
case 'n' :
daemonize = 0 ;
break ;
case 'h' :
print_usage(argv);
exit (0 );
case 'd' :
debug = 1 ;
break ;
default :
print_usage(argv);
exit (EXIT_FAILURE);
}
}
if (daemonize && daemon(1 , 0 ))
return 1 ;
openlog("KVP" , 0 , LOG_USER);
syslog(LOG_INFO, "KVP starting; pid is:%d" , getpid());
/*
* Retrieve OS release information .
*/
kvp_get_os_info();
/*
* Cache Fully Qualified Domain Name because getaddrinfo takes an
* unpredictable amount of time to finish .
*/
kvp_get_domain_name(full_domain_name, sizeof (full_domain_name));
if (debug)
syslog(LOG_INFO, "Logging debug info in syslog(debug)" );
if (kvp_file_init()) {
syslog(LOG_ERR, "Failed to initialize the pools" );
exit (EXIT_FAILURE);
}
reopen_kvp_fd:
if (kvp_fd != -1 )
close(kvp_fd);
in_hand_shake = 1 ;
kvp_fd = open("/dev/vmbus/hv_kvp" , O_RDWR | O_CLOEXEC);
if (kvp_fd < 0 ) {
syslog(LOG_ERR, "open /dev/vmbus/hv_kvp failed; error: %d %s" ,
errno, strerror(errno));
exit (EXIT_FAILURE);
}
/*
* Register ourselves with the kernel .
*/
hv_msg->kvp_hdr.operation = KVP_OP_REGISTER1;
len = write(kvp_fd, hv_msg, sizeof (struct hv_kvp_msg));
if (len != sizeof (struct hv_kvp_msg)) {
syslog(LOG_ERR, "registration to kernel failed; error: %d %s" ,
errno, strerror(errno));
close(kvp_fd);
exit (EXIT_FAILURE);
}
pfd.fd = kvp_fd;
while (1 ) {
pfd.events = POLLIN;
pfd.revents = 0 ;
if (poll(&pfd, 1 , -1 ) < 0 ) {
syslog(LOG_ERR, "poll failed; error: %d %s" , errno, strerror(errno));
if (errno == EINVAL) {
close(kvp_fd);
exit (EXIT_FAILURE);
}
else
continue ;
}
len = read(kvp_fd, hv_msg, sizeof (struct hv_kvp_msg));
if (len != sizeof (struct hv_kvp_msg)) {
syslog(LOG_ERR, "read failed; error:%d %s" ,
errno, strerror(errno));
goto reopen_kvp_fd;
}
/*
* We will use the KVP header information to pass back
* the error from this daemon . So , first copy the state
* and set the error code to success .
*/
op = hv_msg->kvp_hdr.operation;
pool = hv_msg->kvp_hdr.pool;
hv_msg->error = HV_S_OK;
if ((in_hand_shake) && (op == KVP_OP_REGISTER1)) {
/*
* Driver is registering with us ; stash away the version
* information .
*/
in_hand_shake = 0 ;
p = (char *)hv_msg->body.kvp_register.version;
lic_version = malloc(strlen(p) + 1 );
if (lic_version) {
strcpy(lic_version, p);
syslog(LOG_INFO, "KVP LIC Version: %s" ,
lic_version);
} else {
syslog(LOG_ERR, "malloc failed" );
}
continue ;
}
switch (op) {
case KVP_OP_GET_IP_INFO:
kvp_ip_val = &hv_msg->body.kvp_ip_val;
error = kvp_mac_to_ip(kvp_ip_val);
if (error)
hv_msg->error = error;
break ;
case KVP_OP_SET_IP_INFO:
kvp_ip_val = &hv_msg->body.kvp_ip_val;
if_name = kvp_get_if_name(
(char *)kvp_ip_val->adapter_id);
if (if_name == NULL) {
/*
* We could not map the guid to an
* interface name ; return error .
*/
hv_msg->error = HV_GUID_NOTFOUND;
break ;
}
error = kvp_set_ip_info(if_name, kvp_ip_val);
if (error)
hv_msg->error = error;
free(if_name);
break ;
case KVP_OP_SET:
if (kvp_key_add_or_modify(pool,
hv_msg->body.kvp_set.data.key,
hv_msg->body.kvp_set.data.key_size,
hv_msg->body.kvp_set.data.value,
hv_msg->body.kvp_set.data.value_size))
hv_msg->error = HV_S_CONT;
break ;
case KVP_OP_GET:
if (kvp_get_value(pool,
hv_msg->body.kvp_set.data.key,
hv_msg->body.kvp_set.data.key_size,
hv_msg->body.kvp_set.data.value,
hv_msg->body.kvp_set.data.value_size))
hv_msg->error = HV_S_CONT;
break ;
case KVP_OP_DELETE:
if (kvp_key_delete(pool,
hv_msg->body.kvp_delete.key,
hv_msg->body.kvp_delete.key_size))
hv_msg->error = HV_S_CONT;
break ;
default :
break ;
}
if (op != KVP_OP_ENUMERATE)
goto kvp_done;
/*
* If the pool is KVP_POOL_AUTO , dynamically generate
* both the key and the value ; if not read from the
* appropriate pool .
*/
if (pool != KVP_POOL_AUTO) {
if (kvp_pool_enumerate(pool,
hv_msg->body.kvp_enum_data.index,
hv_msg->body.kvp_enum_data.data.key,
HV_KVP_EXCHANGE_MAX_KEY_SIZE,
hv_msg->body.kvp_enum_data.data.value,
HV_KVP_EXCHANGE_MAX_VALUE_SIZE))
hv_msg->error = HV_S_CONT;
goto kvp_done;
}
key_name = (char *)hv_msg->body.kvp_enum_data.data.key;
key_value = (char *)hv_msg->body.kvp_enum_data.data.value;
switch (hv_msg->body.kvp_enum_data.index) {
case FullyQualifiedDomainName:
strcpy(key_value, full_domain_name);
strcpy(key_name, "FullyQualifiedDomainName" );
break ;
case IntegrationServicesVersion:
strcpy(key_name, "IntegrationServicesVersion" );
strcpy(key_value, lic_version);
break ;
case NetworkAddressIPv4:
kvp_get_ip_info(AF_INET, NULL, KVP_OP_ENUMERATE,
key_value, HV_KVP_EXCHANGE_MAX_VALUE_SIZE);
strcpy(key_name, "NetworkAddressIPv4" );
break ;
case NetworkAddressIPv6:
kvp_get_ip_info(AF_INET6, NULL, KVP_OP_ENUMERATE,
key_value, HV_KVP_EXCHANGE_MAX_VALUE_SIZE);
strcpy(key_name, "NetworkAddressIPv6" );
break ;
case OSBuildNumber:
strcpy(key_value, os_build);
strcpy(key_name, "OSBuildNumber" );
break ;
case OSName:
strcpy(key_value, os_name);
strcpy(key_name, "OSName" );
break ;
case OSMajorVersion:
strcpy(key_value, os_major);
strcpy(key_name, "OSMajorVersion" );
break ;
case OSMinorVersion:
strcpy(key_value, os_minor);
strcpy(key_name, "OSMinorVersion" );
break ;
case OSVersion:
strcpy(key_value, os_version);
strcpy(key_name, "OSVersion" );
break ;
case ProcessorArchitecture:
strcpy(key_value, processor_arch);
strcpy(key_name, "ProcessorArchitecture" );
break ;
default :
hv_msg->error = HV_S_CONT;
break ;
}
/*
* Send the value back to the kernel . Note : the write ( ) may
* return an error due to hibernation ; we can ignore the error
* by resetting the dev file , i . e . closing and re - opening it .
*/
kvp_done:
len = write(kvp_fd, hv_msg, sizeof (struct hv_kvp_msg));
if (len != sizeof (struct hv_kvp_msg)) {
syslog(LOG_ERR, "write failed; error: %d %s" , errno,
strerror(errno));
goto reopen_kvp_fd;
}
}
close(kvp_fd);
exit (0 );
}
Messung V0.5 in Prozent C=96 H=87 G=91
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.38Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-09-29)
¤
*Eine klare Vorstellung vom Zielzustand