2010-12-17 04:56:54 +03:00
/*
* 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>
2012-09-06 00:50:13 +04:00
# include <ctype.h>
2010-12-17 04:56:54 +03:00
# include <errno.h>
# include <arpa/inet.h>
2012-02-03 04:56:49 +04:00
# include <linux/hyperv.h>
2010-12-17 04:56:54 +03:00
# include <ifaddrs.h>
# include <netdb.h>
# include <syslog.h>
2012-03-16 19:02:26 +04:00
# include <sys/stat.h>
# include <fcntl.h>
2012-09-06 00:50:13 +04:00
# include <dirent.h>
2012-10-26 01:15:50 +04:00
# include <net/if.h>
2017-05-01 02:21:15 +03:00
# include <limits.h>
2014-10-22 20:07:11 +04:00
# include <getopt.h>
2010-12-17 04:56:54 +03:00
/*
* 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
} ;
2012-09-06 00:50:13 +04:00
enum {
IPADDR = 0 ,
NETMASK ,
GATEWAY ,
DNS
} ;
2012-08-13 21:06:52 +04:00
static int in_hand_shake = 1 ;
2010-12-17 04:56:54 +03:00
2011-03-22 12:02:17 +03:00
static char * os_name = " " ;
static char * os_major = " " ;
static char * os_minor = " " ;
static char * processor_arch ;
static char * os_build ;
2012-10-26 01:15:49 +04:00
static char * os_version ;
2012-08-13 21:06:52 +04:00
static char * lic_version = " Unknown version " ;
2013-08-07 21:14:37 +04:00
static char full_domain_name [ HV_KVP_EXCHANGE_MAX_VALUE_SIZE ] ;
2011-03-22 12:02:17 +03:00
static struct utsname uts_buf ;
2010-12-17 04:56:54 +03:00
2012-09-06 00:50:13 +04:00
/*
* The location of the interface configuration file .
*/
2012-11-27 11:56:33 +04:00
# define KVP_CONFIG_LOC " / var / lib / hyperv"
2012-03-16 19:02:26 +04:00
2016-12-03 23:34:41 +03:00
# ifndef KVP_SCRIPTS_PATH
# define KVP_SCRIPTS_PATH " / usr / libexec / hypervkvpd / "
# endif
2017-05-01 02:21:15 +03:00
# define KVP_NET_DIR " / sys / class / net / "
2012-03-16 19:02:26 +04:00
# define MAX_FILE_NAME 100
# define ENTRIES_PER_BLOCK 50
struct kvp_record {
2012-09-05 01:46:34 +04:00
char key [ HV_KVP_EXCHANGE_MAX_KEY_SIZE ] ;
char value [ HV_KVP_EXCHANGE_MAX_VALUE_SIZE ] ;
2012-03-16 19:02:26 +04:00
} ;
struct kvp_file_state {
int fd ;
int num_blocks ;
struct kvp_record * records ;
int num_records ;
2012-09-05 01:46:34 +04:00
char fname [ MAX_FILE_NAME ] ;
2012-03-16 19:02:26 +04:00
} ;
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 ) {
2013-06-17 12:39:44 +04:00
syslog ( LOG_ERR , " Failed to acquire the lock pool: %d; error: %d %s " , pool ,
errno , strerror ( errno ) ) ;
2012-09-06 01:37:36 +04:00
exit ( EXIT_FAILURE ) ;
2012-03-16 19:02:26 +04: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 ) {
2013-06-17 12:39:44 +04:00
syslog ( LOG_ERR , " Failed to release the lock pool: %d; error: %d %s " , pool ,
errno , strerror ( errno ) ) ;
2012-09-06 01:37:36 +04:00
exit ( EXIT_FAILURE ) ;
2012-03-16 19:02:26 +04: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 ) ;
2013-01-18 18:23:41 +04:00
filep = fopen ( kvp_file_info [ pool ] . fname , " we " ) ;
2012-03-16 19:02:26 +04:00
if ( ! filep ) {
2013-06-17 12:39:44 +04:00
syslog ( LOG_ERR , " Failed to open file, pool: %d; error: %d %s " , pool ,
errno , strerror ( errno ) ) ;
2012-03-16 19:02:26 +04:00
kvp_release_lock ( pool ) ;
2012-09-06 01:37:36 +04:00
exit ( EXIT_FAILURE ) ;
2012-03-16 19:02:26 +04:00
}
2015-01-10 09:18:52 +03:00
fwrite ( kvp_file_info [ pool ] . records , sizeof ( struct kvp_record ) ,
2012-03-16 19:02:26 +04:00
kvp_file_info [ pool ] . num_records , filep ) ;
2012-09-06 01:37:37 +04:00
if ( ferror ( filep ) | | fclose ( filep ) ) {
kvp_release_lock ( pool ) ;
syslog ( LOG_ERR , " Failed to write file, pool: %d " , pool ) ;
exit ( EXIT_FAILURE ) ;
}
2012-03-16 19:02:26 +04:00
kvp_release_lock ( pool ) ;
}
2012-03-16 19:02:27 +04: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 ) ;
2013-01-18 18:23:41 +04:00
filep = fopen ( kvp_file_info [ pool ] . fname , " re " ) ;
2012-03-16 19:02:27 +04:00
if ( ! filep ) {
2013-06-17 12:39:44 +04:00
syslog ( LOG_ERR , " Failed to open file, pool: %d; error: %d %s " , pool ,
errno , strerror ( errno ) ) ;
2012-03-16 19:02:27 +04:00
kvp_release_lock ( pool ) ;
2012-09-06 01:37:36 +04:00
exit ( EXIT_FAILURE ) ;
2012-03-16 19:02:27 +04:00
}
2012-09-06 01:37:37 +04:00
for ( ; ; ) {
2012-03-16 19:02:27 +04:00
readp = & record [ records_read ] ;
records_read + = fread ( readp , sizeof ( struct kvp_record ) ,
2017-11-14 23:06:47 +03:00
ENTRIES_PER_BLOCK * num_blocks - records_read ,
filep ) ;
2012-03-16 19:02:27 +04:00
2012-09-06 01:37:37 +04:00
if ( ferror ( filep ) ) {
2017-11-14 23:06:47 +03:00
syslog ( LOG_ERR ,
" Failed to read file, pool: %d; error: %d %s " ,
pool , errno , strerror ( errno ) ) ;
kvp_release_lock ( pool ) ;
2012-09-06 01:37:37 +04:00
exit ( EXIT_FAILURE ) ;
}
2012-03-16 19:02:27 +04:00
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 " ) ;
2017-11-14 23:06:47 +03:00
kvp_release_lock ( pool ) ;
2012-09-06 01:37:36 +04:00
exit ( EXIT_FAILURE ) ;
2012-03-16 19:02:27 +04:00
}
continue ;
}
break ;
}
kvp_file_info [ pool ] . num_blocks = num_blocks ;
kvp_file_info [ pool ] . records = record ;
kvp_file_info [ pool ] . num_records = records_read ;
2012-09-06 01:37:35 +04:00
fclose ( filep ) ;
2012-03-16 19:02:27 +04:00
kvp_release_lock ( pool ) ;
}
2017-11-14 23:06:47 +03:00
2012-03-16 19:02:26 +04:00
static int kvp_file_init ( void )
{
2012-09-05 01:46:33 +04:00
int fd ;
2012-09-05 01:46:34 +04:00
char * fname ;
2012-03-16 19:02:26 +04:00
int i ;
int alloc_unit = sizeof ( struct kvp_record ) * ENTRIES_PER_BLOCK ;
2012-11-27 11:56:33 +04:00
if ( access ( KVP_CONFIG_LOC , F_OK ) ) {
2012-11-27 11:56:34 +04:00
if ( mkdir ( KVP_CONFIG_LOC , 0755 /* rwxr-xr-x */ ) ) {
2013-06-17 12:39:44 +04:00
syslog ( LOG_ERR , " Failed to create '%s'; error: %d %s " , KVP_CONFIG_LOC ,
errno , strerror ( errno ) ) ;
2012-09-06 01:37:36 +04:00
exit ( EXIT_FAILURE ) ;
2012-03-16 19:02:26 +04:00
}
}
for ( i = 0 ; i < KVP_POOL_COUNT ; i + + ) {
fname = kvp_file_info [ i ] . fname ;
2012-11-27 11:56:33 +04:00
sprintf ( fname , " %s/.kvp_pool_%d " , KVP_CONFIG_LOC , i ) ;
2013-01-18 18:23:41 +04:00
fd = open ( fname , O_RDWR | O_CREAT | O_CLOEXEC , 0644 /* rw-r--r-- */ ) ;
2012-03-16 19:02:26 +04:00
if ( fd = = - 1 )
return 1 ;
kvp_file_info [ i ] . fd = fd ;
2017-11-14 23:06:47 +03:00
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 ) ;
2012-03-16 19:02:26 +04:00
}
return 0 ;
}
2015-01-10 09:18:53 +03:00
static int kvp_key_delete ( int pool , const __u8 * key , int key_size )
2012-03-16 19:02:26 +04:00
{
int i ;
int j , k ;
2012-03-16 19:02:27 +04:00
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 ;
2012-03-16 19:02:26 +04:00
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 ;
}
2015-01-10 09:18:53 +03:00
static int kvp_key_add_or_modify ( int pool , const __u8 * key , int key_size ,
const __u8 * value , int value_size )
2012-03-16 19:02:26 +04:00
{
int i ;
2012-03-16 19:02:27 +04:00
int num_records ;
struct kvp_record * record ;
int num_blocks ;
2012-03-16 19:02:26 +04:00
if ( ( key_size > HV_KVP_EXCHANGE_MAX_KEY_SIZE ) | |
( value_size > HV_KVP_EXCHANGE_MAX_VALUE_SIZE ) )
return 1 ;
2012-03-16 19:02:27 +04:00
/*
* 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 ;
2012-03-16 19:02:26 +04:00
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 ;
}
2015-01-10 09:18:53 +03:00
static int kvp_get_value ( int pool , const __u8 * key , int key_size , __u8 * value ,
2012-03-16 19:02:26 +04:00
int value_size )
{
int i ;
2012-03-16 19:02:27 +04:00
int num_records ;
struct kvp_record * record ;
2012-03-16 19:02:26 +04:00
if ( ( key_size > HV_KVP_EXCHANGE_MAX_KEY_SIZE ) | |
( value_size > HV_KVP_EXCHANGE_MAX_VALUE_SIZE ) )
return 1 ;
2012-03-16 19:02:27 +04:00
/*
* 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 ;
2012-03-16 19:02:26 +04:00
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 ;
}
2015-01-10 09:18:53 +03:00
static int kvp_pool_enumerate ( int pool , int index , __u8 * key , int key_size ,
__u8 * value , int value_size )
2012-03-16 19:02:27 +04:00
{
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 ) {
2012-08-13 21:06:52 +04:00
return 1 ;
2012-03-16 19:02:27 +04:00
}
memcpy ( key , record [ index ] . key , key_size ) ;
memcpy ( value , record [ index ] . value , value_size ) ;
2012-08-13 21:06:52 +04:00
return 0 ;
2012-03-16 19:02:27 +04:00
}
2010-12-17 04:56:54 +03:00
void kvp_get_os_info ( void )
{
FILE * file ;
2011-03-22 12:02:17 +03:00
char * p , buf [ 512 ] ;
2010-12-17 04:56:54 +03:00
2011-03-22 12:02:17 +03:00
uname ( & uts_buf ) ;
2012-10-26 01:15:49 +04:00
os_version = uts_buf . release ;
os_build = strdup ( uts_buf . release ) ;
2012-09-07 00:32:14 +04:00
os_name = uts_buf . sysname ;
2011-07-19 22:44:20 +04:00
processor_arch = uts_buf . machine ;
2010-12-17 04:56:54 +03:00
2011-07-22 21:14:31 +04:00
/*
* The current windows host ( win7 ) expects the build
* string to be of the form : x . y . z
* Strip additional information we may have .
*/
2012-10-26 01:15:49 +04:00
p = strchr ( os_version , ' - ' ) ;
2011-07-22 21:14:31 +04:00
if ( p )
* p = ' \0 ' ;
2012-09-07 00:32:14 +04:00
/*
* 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 */
2010-12-17 04:56:54 +03:00
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 :
2011-03-22 12:02:17 +03:00
/* 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 :
2010-12-17 04:56:54 +03:00
fclose ( file ) ;
return ;
}
2012-09-06 00:50:13 +04: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 ;
2017-05-01 02:21:15 +03:00
char * p , * x ;
2012-09-06 00:50:13 +04:00
char * if_name = NULL ;
char buf [ 256 ] ;
2017-05-01 02:21:15 +03:00
char dev_id [ PATH_MAX ] ;
2012-09-06 00:50:13 +04:00
2017-05-01 02:21:15 +03:00
dir = opendir ( KVP_NET_DIR ) ;
2012-09-06 00:50:13 +04:00
if ( dir = = NULL )
return NULL ;
while ( ( entry = readdir ( dir ) ) ! = NULL ) {
/*
* Set the state for the next pass .
*/
2017-05-01 02:21:15 +03:00
snprintf ( dev_id , sizeof ( dev_id ) , " %s%s/device/device_id " ,
KVP_NET_DIR , entry - > d_name ) ;
2012-09-06 00:50:13 +04:00
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 ] ;
2017-05-01 02:21:15 +03:00
char addr_file [ PATH_MAX ] ;
2015-01-10 09:18:53 +03:00
unsigned int i ;
2012-09-06 00:50:13 +04:00
char * mac_addr = NULL ;
2017-05-01 02:21:15 +03:00
snprintf ( addr_file , sizeof ( addr_file ) , " %s%s%s " , KVP_NET_DIR ,
if_name , " /address " ) ;
2012-09-06 00:50:13 +04:00
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 ;
}
2012-08-17 05:32:18 +04:00
static void kvp_process_ipconfig_file ( char * cmd ,
2015-01-10 09:18:53 +03:00
char * config_buf , unsigned int len ,
2012-08-17 05:32:18 +04:00
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 ) {
2015-01-10 09:18:53 +03:00
if ( len < strlen ( config_buf ) + element_size + 1 )
2012-08-17 05:32:18 +04:00
break ;
x = strchr ( p , ' \n ' ) ;
2013-05-22 16:54:32 +04:00
if ( x )
* x = ' \0 ' ;
2012-08-17 05:32:18 +04:00
strcat ( config_buf , p ) ;
strcat ( config_buf , " ; " ) ;
}
pclose ( file ) ;
}
static void kvp_get_ipconfig_info ( char * if_name ,
struct hv_kvp_ipaddr_value * buffer )
{
char cmd [ 512 ] ;
2012-09-06 00:50:11 +04:00
char dhcp_info [ 128 ] ;
char * p ;
FILE * file ;
2012-08-17 05:32:18 +04:00
/*
* 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 * ) buffer - > gate_way ,
( MAX_GATEWAY_SIZE * 2 ) , INET_ADDRSTRLEN , 0 ) ;
/*
* Get the address of default gateway ( ipv6 ) .
*/
sprintf ( cmd , " %s %s " , " ip -f inet6 route show dev " , if_name ) ;
strcat ( cmd , " | awk '/default/ {print $3 }' " ) ;
/*
* Execute the command to gather gateway info ( ipv6 ) .
*/
kvp_process_ipconfig_file ( cmd , ( char * ) buffer - > gate_way ,
( MAX_GATEWAY_SIZE * 2 ) , INET6_ADDRSTRLEN , 1 ) ;
2012-09-05 01:46:36 +04:00
/*
* 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 )
* .
* .
*/
2016-12-03 23:34:41 +03:00
sprintf ( cmd , KVP_SCRIPTS_PATH " %s " , " hv_get_dns_info " ) ;
2012-09-05 01:46:36 +04:00
/*
* Execute the command to gather DNS info .
*/
kvp_process_ipconfig_file ( cmd , ( char * ) buffer - > dns_addr ,
( MAX_IP_ADDR_SIZE * 2 ) , INET_ADDRSTRLEN , 0 ) ;
2012-09-06 00:50:11 +04:00
/*
* 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 .
*/
2016-12-03 23:34:41 +03:00
sprintf ( cmd , KVP_SCRIPTS_PATH " %s %s " , " hv_get_dhcp_info " , if_name ) ;
2012-09-06 00:50:11 +04:00
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 ) ;
2012-08-17 05:32:18 +04:00
}
2012-08-17 05:32:17 +04:00
static unsigned int hweight32 ( unsigned int * w )
{
unsigned int res = * w - ( ( * w > > 1 ) & 0x55555555 ) ;
res = ( res & 0x33333333 ) + ( ( res > > 2 ) & 0x33333333 ) ;
res = ( res + ( res > > 4 ) ) & 0x0F0F0F0F ;
res = res + ( res > > 8 ) ;
return ( res + ( res > > 16 ) ) & 0x000000FF ;
}
2012-08-17 05:32:14 +04:00
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 = ( struct sockaddr_in * ) addrp ;
str = inet_ntop ( family , & addr - > sin_addr , tmp , 50 ) ;
addr_length = INET_ADDRSTRLEN ;
} else {
addr6 = ( struct sockaddr_in6 * ) addrp ;
str = inet_ntop ( family , & addr6 - > sin6_addr . s6_addr , tmp , 50 ) ;
addr_length = INET6_ADDRSTRLEN ;
}
2012-10-26 01:15:50 +04:00
if ( ( length - * offset ) < addr_length + 2 )
2012-09-06 00:50:15 +04:00
return HV_E_FAIL ;
2012-08-17 05:32:14 +04:00
if ( str = = NULL ) {
strcpy ( buffer , " inet_ntop failed \n " ) ;
2012-09-06 00:50:15 +04:00
return HV_E_FAIL ;
2012-08-17 05:32:14 +04:00
}
if ( * offset = = 0 )
strcpy ( buffer , tmp ) ;
2012-10-26 01:15:50 +04:00
else {
strcat ( buffer , " ; " ) ;
2012-08-17 05:32:14 +04:00
strcat ( buffer , tmp ) ;
2012-10-26 01:15:50 +04:00
}
2012-08-17 05:32:14 +04:00
* offset + = strlen ( str ) + 1 ;
2012-10-26 01:15:50 +04:00
2012-08-17 05:32:14 +04:00
return 0 ;
}
2010-12-17 04:56:54 +03:00
static int
2012-09-06 00:50:14 +04:00
kvp_get_ip_info ( int family , char * if_name , int op ,
2015-01-10 09:18:53 +03:00
void * out_buffer , unsigned int length )
2010-12-17 04:56:54 +03:00
{
struct ifaddrs * ifap ;
struct ifaddrs * curp ;
int offset = 0 ;
2012-08-17 05:32:16 +04:00
int sn_offset = 0 ;
2010-12-17 04:56:54 +03:00
int error = 0 ;
2012-08-17 05:32:13 +04:00
char * buffer ;
struct hv_kvp_ipaddr_value * ip_buffer ;
2012-08-17 05:32:17 +04:00
char cidr_mask [ 5 ] ; /* /xyz */
int weight ;
int i ;
unsigned int * w ;
char * sn_str ;
struct sockaddr_in6 * addr6 ;
2012-08-17 05:32:13 +04:00
if ( op = = KVP_OP_ENUMERATE ) {
buffer = out_buffer ;
} else {
ip_buffer = out_buffer ;
buffer = ( char * ) ip_buffer - > ip_addr ;
ip_buffer - > addr_family = 0 ;
}
2010-12-17 04:56:54 +03:00
/*
* On entry into this function , the buffer is capable of holding the
2012-08-17 05:32:13 +04:00
* maximum key value .
2010-12-17 04:56:54 +03:00
*/
if ( getifaddrs ( & ifap ) ) {
strcpy ( buffer , " getifaddrs failed \n " ) ;
2012-09-06 00:50:15 +04:00
return HV_E_FAIL ;
2010-12-17 04:56:54 +03:00
}
curp = ifap ;
while ( curp ! = NULL ) {
2012-08-17 05:32:13 +04:00
if ( curp - > ifa_addr = = NULL ) {
curp = curp - > ifa_next ;
continue ;
}
2010-12-17 04:56:54 +03:00
2012-08-17 05:32:13 +04:00
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 ;
}
2010-12-17 04:56:54 +03:00
2012-08-17 05:32:13 +04:00
/*
* 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 .
*/
2012-10-26 01:15:50 +04:00
if ( ( ( ( family ! = 0 ) & &
( curp - > ifa_addr - > sa_family ! = family ) ) ) | |
( curp - > ifa_flags & IFF_LOOPBACK ) ) {
2012-08-17 05:32:13 +04:00
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 ;
}
2012-08-17 05:32:15 +04:00
if ( op = = KVP_OP_GET_IP_INFO ) {
/*
* Gather info other than the IP address .
* IP address info will be gathered later .
*/
2012-08-17 05:32:16 +04:00
if ( curp - > ifa_addr - > sa_family = = AF_INET ) {
2012-08-17 05:32:15 +04:00
ip_buffer - > addr_family | = ADDR_FAMILY_IPV4 ;
2012-08-17 05:32:16 +04:00
/*
* 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 {
2012-08-17 05:32:15 +04:00
ip_buffer - > addr_family | = ADDR_FAMILY_IPV6 ;
2012-08-17 05:32:17 +04:00
2012-08-17 05:32:16 +04:00
/*
2012-08-17 05:32:17 +04:00
* Get subnet info in CIDR format .
2012-08-17 05:32:16 +04:00
*/
2012-08-17 05:32:17 +04:00
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 ) ;
2015-01-10 09:18:53 +03:00
if ( length < sn_offset + strlen ( cidr_mask ) + 1 )
2012-08-17 05:32:16 +04:00
goto gather_ipaddr ;
2012-08-17 05:32:17 +04:00
if ( sn_offset = = 0 )
strcpy ( sn_str , cidr_mask ) ;
2013-07-11 23:03:31 +04:00
else {
strcat ( ( char * ) ip_buffer - > sub_net , " ; " ) ;
2012-08-17 05:32:17 +04:00
strcat ( sn_str , cidr_mask ) ;
2013-07-11 23:03:31 +04:00
}
2012-08-17 05:32:17 +04:00
sn_offset + = strlen ( sn_str ) + 1 ;
2012-08-17 05:32:16 +04:00
}
2012-08-17 05:32:18 +04:00
/*
* Collect other ip related configuration info .
*/
kvp_get_ipconfig_info ( if_name , ip_buffer ) ;
2012-08-17 05:32:15 +04:00
}
2012-08-17 05:32:16 +04:00
gather_ipaddr :
2012-08-17 05:32:14 +04:00
error = kvp_process_ip_address ( curp - > ifa_addr ,
curp - > ifa_addr - > sa_family ,
buffer ,
length , & offset ) ;
if ( error )
goto getaddr_done ;
2012-08-17 05:32:13 +04:00
2010-12-17 04:56:54 +03:00
curp = curp - > ifa_next ;
}
getaddr_done :
freeifaddrs ( ifap ) ;
return error ;
}
2018-03-05 08:17:11 +03:00
/*
* 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 ;
}
2010-12-17 04:56:54 +03:00
2012-09-06 00:50:13 +04:00
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 seperated 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 ] ;
2017-08-11 01:45:15 +03:00
char sub_str [ 13 ] ;
2012-09-06 00:50:13 +04:00
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 ;
}
2013-01-14 01:27:40 +04:00
if ( type = = DNS ) {
2012-09-06 00:50:13 +04:00
snprintf ( sub_str , sizeof ( sub_str ) , " %d " , + + i ) ;
2013-01-14 01:27:40 +04:00
} else if ( type = = GATEWAY & & i = = 0 ) {
+ + i ;
} else {
snprintf ( sub_str , sizeof ( sub_str ) , " %d " , i + + ) ;
2012-09-06 00:50:13 +04:00
}
} 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 ;
}
2013-01-14 01:27:40 +04:00
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 + + ) ;
2012-09-06 00:50:13 +04:00
}
} else {
return HV_INVALIDARG ;
}
error = kvp_write_file ( f , str , sub_str , addr ) ;
if ( error )
return error ;
memset ( addr , 0 , sizeof ( addr ) ) ;
}
return 0 ;
}
static int kvp_set_ip_info ( char * if_name , struct hv_kvp_ipaddr_value * new_val )
{
int error = 0 ;
2017-05-01 02:21:15 +03:00
char if_file [ PATH_MAX ] ;
2012-09-06 00:50:13 +04:00
FILE * file ;
2017-05-01 02:21:15 +03:00
char cmd [ PATH_MAX ] ;
2012-09-06 00:50:13 +04:00
char * mac_addr ;
/*
* 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 format of the ip configuration file :
*
* HWADDR = macaddr
2013-01-14 01:27:40 +04:00
* DEVICE = interface name
* BOOTPROTO = < protocol > ( where < protocol > is " dhcp " if DHCP is configured
* or " none " if no boot - time protocol should be used )
2012-09-06 00:50:13 +04:00
*
2013-01-14 01:27:40 +04:00
* IPADDR0 = ipaddr1
* IPADDR1 = ipaddr2
* IPADDRx = ipaddry ( where y = x + 1 )
2012-09-06 00:50:13 +04:00
*
2013-01-14 01:27:40 +04:00
* NETMASK0 = netmask1
* NETMASKx = netmasky ( where y = x + 1 )
2012-09-06 00:50:13 +04:00
*
* GATEWAY = ipaddr1
2013-01-14 01:27:40 +04:00
* GATEWAYx = ipaddry ( where y = x + 1 )
2012-09-06 00:50:13 +04:00
*
* 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 .
*
* 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 .
*/
snprintf ( if_file , sizeof ( if_file ) , " %s%s%s " , KVP_CONFIG_LOC ,
2012-11-27 11:56:33 +04:00
" /ifcfg- " , if_name ) ;
2012-09-06 00:50:13 +04:00
file = fopen ( if_file , " w " ) ;
if ( file = = NULL ) {
2013-06-17 12:39:44 +04:00
syslog ( LOG_ERR , " Failed to open config file; error: %d %s " ,
errno , strerror ( errno ) ) ;
2012-09-06 00:50:13 +04:00
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 ( file , " HWADDR " , " " , mac_addr ) ;
2013-08-04 18:41:24 +04:00
free ( mac_addr ) ;
2012-09-06 00:50:13 +04:00
if ( error )
goto setval_error ;
2013-01-14 01:27:40 +04:00
error = kvp_write_file ( file , " DEVICE " , " " , if_name ) ;
2012-09-06 00:50:13 +04:00
if ( error )
goto setval_error ;
2014-12-10 14:33:20 +03:00
/*
* 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 .
*/
2012-09-06 00:50:13 +04:00
if ( new_val - > dhcp_enabled ) {
2013-01-14 01:27:40 +04:00
error = kvp_write_file ( file , " BOOTPROTO " , " " , " dhcp " ) ;
2012-09-06 00:50:13 +04:00
if ( error )
goto setval_error ;
2013-01-14 01:27:40 +04:00
} else {
error = kvp_write_file ( file , " BOOTPROTO " , " " , " none " ) ;
if ( error )
goto setval_error ;
2012-09-06 00:50:13 +04:00
}
/*
* Write the configuration for ipaddress , netmask , gateway and
* name servers .
*/
error = process_ip_string ( file , ( char * ) new_val - > ip_addr , IPADDR ) ;
if ( error )
goto setval_error ;
error = process_ip_string ( file , ( char * ) new_val - > sub_net , NETMASK ) ;
if ( error )
goto setval_error ;
error = process_ip_string ( file , ( char * ) new_val - > gate_way , GATEWAY ) ;
if ( error )
goto setval_error ;
error = process_ip_string ( file , ( char * ) new_val - > dns_addr , DNS ) ;
if ( error )
goto setval_error ;
fclose ( file ) ;
/*
* Now that we have populated the configuration file ,
* invoke the external script to do its magic .
*/
2016-12-03 23:34:41 +03:00
snprintf ( cmd , sizeof ( cmd ) , KVP_SCRIPTS_PATH " %s %s " ,
" hv_set_ifconfig " , if_file ) ;
2013-08-04 18:40:44 +04:00
if ( system ( cmd ) ) {
syslog ( LOG_ERR , " Failed to execute cmd '%s'; error: %d %s " ,
cmd , errno , strerror ( errno ) ) ;
return HV_E_FAIL ;
}
2012-09-06 00:50:13 +04:00
return 0 ;
setval_error :
syslog ( LOG_ERR , " Failed to write config file " ) ;
fclose ( file ) ;
return error ;
}
2013-08-07 21:14:37 +04:00
static void
2010-12-17 04:56:54 +03:00
kvp_get_domain_name ( char * buffer , int length )
{
struct addrinfo hints , * info ;
int error = 0 ;
2011-07-26 22:03:10 +04:00
gethostname ( buffer , length ) ;
2010-12-17 04:56:54 +03:00
memset ( & hints , 0 , sizeof ( hints ) ) ;
hints . ai_family = AF_INET ; /*Get only ipv4 addrinfo. */
hints . ai_socktype = SOCK_STREAM ;
hints . ai_flags = AI_CANONNAME ;
2011-07-26 22:03:10 +04:00
error = getaddrinfo ( buffer , NULL , & hints , & info ) ;
2010-12-17 04:56:54 +03:00
if ( error ! = 0 ) {
2013-08-07 21:14:37 +04:00
snprintf ( buffer , length , " getaddrinfo failed: 0x%x %s " ,
error , gai_strerror ( error ) ) ;
return ;
2010-12-17 04:56:54 +03:00
}
2013-08-07 21:14:37 +04:00
snprintf ( buffer , length , " %s " , info - > ai_canonname ) ;
2010-12-17 04:56:54 +03:00
freeaddrinfo ( info ) ;
}
2014-10-22 20:07:11 +04:00
void print_usage ( char * argv [ ] )
{
fprintf ( stderr , " Usage: %s [options] \n "
" Options are: \n "
" -n, --no-daemon stay in foreground, don't daemonize \n "
" -h, --help print this help \n " , argv [ 0 ] ) ;
}
int main ( int argc , char * argv [ ] )
2010-12-17 04:56:54 +03:00
{
2015-04-12 04:07:55 +03:00
int kvp_fd , len ;
2010-12-17 04:56:54 +03:00
int error ;
struct pollfd pfd ;
2015-04-12 04:07:55 +03:00
char * p ;
struct hv_kvp_msg hv_msg [ 1 ] ;
2010-12-17 04:56:54 +03:00
char * key_value ;
char * key_name ;
2012-08-13 21:06:52 +04:00
int op ;
int pool ;
2012-09-06 00:50:13 +04:00
char * if_name ;
struct hv_kvp_ipaddr_value * kvp_ip_val ;
2014-10-22 20:07:11 +04:00
int daemonize = 1 , long_index = 0 , opt ;
static struct option long_options [ ] = {
{ " help " , no_argument , 0 , ' h ' } ,
{ " no-daemon " , no_argument , 0 , ' n ' } ,
{ 0 , 0 , 0 , 0 }
} ;
while ( ( opt = getopt_long ( argc , argv , " hn " , long_options ,
& long_index ) ) ! = - 1 ) {
switch ( opt ) {
case ' n ' :
daemonize = 0 ;
break ;
case ' h ' :
default :
print_usage ( argv ) ;
exit ( EXIT_FAILURE ) ;
}
}
2010-12-17 04:56:54 +03:00
2014-10-22 20:07:11 +04:00
if ( daemonize & & daemon ( 1 , 0 ) )
2013-08-01 16:43:12 +04:00
return 1 ;
2014-10-22 20:07:11 +04:00
2010-12-17 04:56:54 +03:00
openlog ( " KVP " , 0 , LOG_USER ) ;
syslog ( LOG_INFO , " KVP starting; pid is:%d " , getpid ( ) ) ;
2013-08-01 16:34:26 +04:00
2016-07-07 04:24:10 +03:00
kvp_fd = open ( " /dev/vmbus/hv_kvp " , O_RDWR | O_CLOEXEC ) ;
2015-04-12 04:07:55 +03:00
if ( kvp_fd < 0 ) {
syslog ( LOG_ERR , " open /dev/vmbus/hv_kvp failed; error: %d %s " ,
errno , strerror ( errno ) ) ;
2013-08-01 16:34:26 +04:00
exit ( EXIT_FAILURE ) ;
}
2015-04-12 04:07:55 +03:00
2010-12-17 04:56:54 +03:00
/*
* Retrieve OS release information .
*/
kvp_get_os_info ( ) ;
2013-08-07 21:14:37 +04:00
/*
* 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 ) ) ;
2010-12-17 04:56:54 +03:00
2012-03-16 19:02:26 +04:00
if ( kvp_file_init ( ) ) {
syslog ( LOG_ERR , " Failed to initialize the pools " ) ;
2012-09-06 01:37:36 +04:00
exit ( EXIT_FAILURE ) ;
2012-03-16 19:02:26 +04:00
}
2010-12-17 04:56:54 +03:00
/*
* Register ourselves with the kernel .
*/
2012-08-13 21:06:52 +04:00
hv_msg - > kvp_hdr . operation = KVP_OP_REGISTER1 ;
2015-04-12 04:07:55 +03:00
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 ) ;
2012-09-06 01:37:36 +04:00
exit ( EXIT_FAILURE ) ;
2010-12-17 04:56:54 +03:00
}
2015-04-12 04:07:55 +03:00
pfd . fd = kvp_fd ;
2010-12-17 04:56:54 +03:00
while ( 1 ) {
pfd . events = POLLIN ;
pfd . revents = 0 ;
2013-05-22 16:54:31 +04:00
if ( poll ( & pfd , 1 , - 1 ) < 0 ) {
syslog ( LOG_ERR , " poll failed; error: %d %s " , errno , strerror ( errno ) ) ;
if ( errno = = EINVAL ) {
2015-04-12 04:07:55 +03:00
close ( kvp_fd ) ;
2013-05-22 16:54:31 +04:00
exit ( EXIT_FAILURE ) ;
}
else
continue ;
}
2010-12-17 04:56:54 +03:00
2015-04-12 04:07:55 +03:00
len = read ( kvp_fd , hv_msg , sizeof ( struct hv_kvp_msg ) ) ;
2014-11-20 08:51:22 +03:00
2015-04-12 04:07:55 +03:00
if ( len ! = sizeof ( struct hv_kvp_msg ) ) {
syslog ( LOG_ERR , " read failed; error:%d %s " ,
errno , strerror ( errno ) ) ;
2014-11-20 08:51:22 +03:00
2015-04-12 04:07:55 +03:00
close ( kvp_fd ) ;
return EXIT_FAILURE ;
2010-12-17 04:56:54 +03:00
}
2012-08-13 21:06:52 +04:00
/*
* 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 ) ) {
2010-12-17 04:56:54 +03:00
/*
* Driver is registering with us ; stash away the version
* information .
*/
2012-08-13 21:06:52 +04:00
in_hand_shake = 0 ;
2012-03-11 03:32:08 +04:00
p = ( char * ) hv_msg - > body . kvp_register . version ;
2011-03-22 12:02:17 +03:00
lic_version = malloc ( strlen ( p ) + 1 ) ;
2010-12-17 04:56:54 +03:00
if ( lic_version ) {
2011-03-22 12:02:17 +03:00
strcpy ( lic_version , p ) ;
2010-12-17 04:56:54 +03:00
syslog ( LOG_INFO , " KVP LIC Version: %s " ,
2015-04-12 04:07:55 +03:00
lic_version ) ;
2010-12-17 04:56:54 +03:00
} else {
syslog ( LOG_ERR , " malloc failed " ) ;
}
continue ;
2012-08-13 21:06:52 +04:00
}
2010-12-17 04:56:54 +03:00
2012-08-13 21:06:52 +04:00
switch ( op ) {
2012-09-06 00:50:15 +04:00
case KVP_OP_GET_IP_INFO :
kvp_ip_val = & hv_msg - > body . kvp_ip_val ;
2018-03-05 08:17:11 +03:00
error = kvp_mac_to_ip ( kvp_ip_val ) ;
2012-09-06 00:50:15 +04:00
if ( error )
hv_msg - > error = error ;
break ;
2012-09-06 00:50:13 +04:00
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 ;
2012-03-16 19:02:25 +04:00
case KVP_OP_SET :
2012-08-13 21:06:52 +04:00
if ( kvp_key_add_or_modify ( pool ,
2012-03-16 19:02:26 +04:00
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 ) )
2012-08-13 21:06:52 +04:00
hv_msg - > error = HV_S_CONT ;
2012-03-16 19:02:26 +04:00
break ;
2012-03-16 19:02:25 +04:00
case KVP_OP_GET :
2012-08-13 21:06:52 +04:00
if ( kvp_get_value ( pool ,
2012-03-16 19:02:26 +04:00
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 ) )
2012-08-13 21:06:52 +04:00
hv_msg - > error = HV_S_CONT ;
2012-03-16 19:02:26 +04:00
break ;
2012-03-16 19:02:25 +04:00
case KVP_OP_DELETE :
2012-08-13 21:06:52 +04:00
if ( kvp_key_delete ( pool ,
2012-03-16 19:02:26 +04:00
hv_msg - > body . kvp_delete . key ,
hv_msg - > body . kvp_delete . key_size ) )
2012-08-13 21:06:52 +04:00
hv_msg - > error = HV_S_CONT ;
2012-03-16 19:02:26 +04:00
break ;
2010-12-17 04:56:54 +03:00
default :
2012-02-03 04:56:50 +04:00
break ;
2010-12-17 04:56:54 +03:00
}
2012-08-13 21:06:52 +04:00
if ( op ! = KVP_OP_ENUMERATE )
2012-03-16 19:02:25 +04:00
goto kvp_done ;
2012-03-16 19:02:27 +04:00
/*
* If the pool is KVP_POOL_AUTO , dynamically generate
* both the key and the value ; if not read from the
* appropriate pool .
*/
2012-08-13 21:06:52 +04:00
if ( pool ! = KVP_POOL_AUTO ) {
if ( kvp_pool_enumerate ( pool ,
2012-03-16 19:02:27 +04:00
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 ,
2012-08-13 21:06:52 +04:00
HV_KVP_EXCHANGE_MAX_VALUE_SIZE ) )
hv_msg - > error = HV_S_CONT ;
2012-03-16 19:02:27 +04:00
goto kvp_done ;
}
2012-02-03 04:56:50 +04:00
key_name = ( char * ) hv_msg - > body . kvp_enum_data . data . key ;
key_value = ( char * ) hv_msg - > body . kvp_enum_data . data . value ;
2010-12-17 04:56:54 +03:00
2012-02-03 04:56:50 +04:00
switch ( hv_msg - > body . kvp_enum_data . index ) {
2010-12-17 04:56:54 +03:00
case FullyQualifiedDomainName :
2013-08-07 21:14:37 +04:00
strcpy ( key_value , full_domain_name ) ;
2010-12-17 04:56:54 +03:00
strcpy ( key_name , " FullyQualifiedDomainName " ) ;
break ;
case IntegrationServicesVersion :
strcpy ( key_name , " IntegrationServicesVersion " ) ;
strcpy ( key_value , lic_version ) ;
break ;
case NetworkAddressIPv4 :
2012-09-06 00:50:14 +04:00
kvp_get_ip_info ( AF_INET , NULL , KVP_OP_ENUMERATE ,
2012-08-17 05:32:13 +04:00
key_value , HV_KVP_EXCHANGE_MAX_VALUE_SIZE ) ;
2010-12-17 04:56:54 +03:00
strcpy ( key_name , " NetworkAddressIPv4 " ) ;
break ;
case NetworkAddressIPv6 :
2012-09-06 00:50:14 +04:00
kvp_get_ip_info ( AF_INET6 , NULL , KVP_OP_ENUMERATE ,
2012-08-17 05:32:13 +04:00
key_value , HV_KVP_EXCHANGE_MAX_VALUE_SIZE ) ;
2010-12-17 04:56:54 +03:00
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 :
2012-10-26 01:15:49 +04:00
strcpy ( key_value , os_version ) ;
2010-12-17 04:56:54 +03:00
strcpy ( key_name , " OSVersion " ) ;
break ;
case ProcessorArchitecture :
strcpy ( key_value , processor_arch ) ;
strcpy ( key_name , " ProcessorArchitecture " ) ;
break ;
default :
2012-08-13 21:06:52 +04:00
hv_msg - > error = HV_S_CONT ;
2010-12-17 04:56:54 +03:00
break ;
}
2014-11-20 08:51:22 +03:00
2015-04-12 04:07:55 +03:00
/* Send the value back to the kernel. */
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 ) ) ;
2012-09-06 01:37:36 +04:00
exit ( EXIT_FAILURE ) ;
2010-12-17 04:56:54 +03:00
}
}
2015-04-12 04:07:55 +03:00
close ( kvp_fd ) ;
exit ( 0 ) ;
2010-12-17 04:56:54 +03:00
}