mirror of
https://github.com/gnif/LookingGlass.git
synced 2024-11-25 06:47:19 +00:00
[c-host] initial agnostic option api and parser
This commit is contained in:
parent
22f9fa3938
commit
e7345b9711
@ -74,6 +74,8 @@ CapturePointer;
|
|||||||
typedef struct CaptureInterface
|
typedef struct CaptureInterface
|
||||||
{
|
{
|
||||||
const char * (*getName )();
|
const char * (*getName )();
|
||||||
|
void (*initOptions )();
|
||||||
|
|
||||||
bool (*create )();
|
bool (*create )();
|
||||||
bool (*init )(void * pointerShape, const unsigned int pointerSize);
|
bool (*init )(void * pointerShape, const unsigned int pointerSize);
|
||||||
void (*stop )();
|
void (*stop )();
|
||||||
|
@ -22,6 +22,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
int app_main(int argc, char * argv[]);
|
int app_main(int argc, char * argv[]);
|
||||||
|
bool app_init();
|
||||||
void app_quit();
|
void app_quit();
|
||||||
|
|
||||||
// these must be implemented for each OS
|
// these must be implemented for each OS
|
||||||
|
@ -19,6 +19,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
|||||||
|
|
||||||
#include "interface/platform.h"
|
#include "interface/platform.h"
|
||||||
#include "common/debug.h"
|
#include "common/debug.h"
|
||||||
|
#include "common/option.h"
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
@ -40,18 +41,8 @@ struct app
|
|||||||
void * shmMap;
|
void * shmMap;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct params
|
|
||||||
{
|
|
||||||
const char * shmDevice;
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct app app;
|
static struct app app;
|
||||||
|
|
||||||
static struct params params =
|
|
||||||
{
|
|
||||||
.shmDevice = "uio0"
|
|
||||||
};
|
|
||||||
|
|
||||||
struct osThreadHandle
|
struct osThreadHandle
|
||||||
{
|
{
|
||||||
const char * name;
|
const char * name;
|
||||||
@ -71,39 +62,47 @@ int main(int argc, char * argv[])
|
|||||||
{
|
{
|
||||||
app.executable = argv[0];
|
app.executable = argv[0];
|
||||||
|
|
||||||
static struct option longOptions[] =
|
struct Option options[] =
|
||||||
{
|
{
|
||||||
{"shmDevice", required_argument, 0, 'f'},
|
{
|
||||||
{0, 0, 0, 0}
|
.module = "os",
|
||||||
|
.name = "shmDevice",
|
||||||
|
.description = "The IVSHMEM device to use",
|
||||||
|
.value = {
|
||||||
|
.type = OPTION_TYPE_STRING,
|
||||||
|
.v.x_string = "uio0"
|
||||||
|
},
|
||||||
|
.validator = NULL,
|
||||||
|
.printHelp = NULL
|
||||||
|
},
|
||||||
|
{0}
|
||||||
};
|
};
|
||||||
|
|
||||||
int optionIndex = 0;
|
option_register(options);
|
||||||
while(true)
|
|
||||||
{
|
|
||||||
int c = getopt_long(argc, argv, "f:", longOptions, &optionIndex);
|
|
||||||
if (c == -1)
|
|
||||||
break;
|
|
||||||
|
|
||||||
switch(c)
|
int result = app_main(argc, argv);
|
||||||
{
|
os_shmemUnmap();
|
||||||
case 'f':
|
close(app.shmFD);
|
||||||
params.shmDevice = optarg;
|
|
||||||
break;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
bool app_init()
|
||||||
|
{
|
||||||
|
const char * shmDevice = option_get_string("os", "shmDevice");
|
||||||
|
|
||||||
// check the deice name
|
// check the deice name
|
||||||
{
|
{
|
||||||
char file[100] = "/sys/class/uio/";
|
char file[100] = "/sys/class/uio/";
|
||||||
strncat(file, params.shmDevice, sizeof(file) - 1);
|
strncat(file, shmDevice, sizeof(file) - 1);
|
||||||
strncat(file, "/name" , sizeof(file) - 1);
|
strncat(file, "/name" , sizeof(file) - 1);
|
||||||
|
|
||||||
int fd = open(file, O_RDONLY);
|
int fd = open(file, O_RDONLY);
|
||||||
if (fd < 0)
|
if (fd < 0)
|
||||||
{
|
{
|
||||||
DEBUG_ERROR("Failed to open: %s", file);
|
DEBUG_ERROR("Failed to open: %s", file);
|
||||||
DEBUG_ERROR("Did you remmeber to modprobe the kvmfr module?");
|
DEBUG_ERROR("Did you remmeber to modprobe the kvmfr module?");
|
||||||
return -1;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
char name[32];
|
char name[32];
|
||||||
@ -112,7 +111,7 @@ int main(int argc, char * argv[])
|
|||||||
{
|
{
|
||||||
DEBUG_ERROR("Failed to read: %s", file);
|
DEBUG_ERROR("Failed to read: %s", file);
|
||||||
close(fd);
|
close(fd);
|
||||||
return -1;
|
return false;
|
||||||
}
|
}
|
||||||
name[len] = '\0';
|
name[len] = '\0';
|
||||||
close(fd);
|
close(fd);
|
||||||
@ -126,21 +125,21 @@ int main(int argc, char * argv[])
|
|||||||
if (strcmp(name, "KVMFR") != 0)
|
if (strcmp(name, "KVMFR") != 0)
|
||||||
{
|
{
|
||||||
DEBUG_ERROR("Device is not a KVMFR device \"%s\" reports as: %s", file, name);
|
DEBUG_ERROR("Device is not a KVMFR device \"%s\" reports as: %s", file, name);
|
||||||
return -1;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the device size
|
// get the device size
|
||||||
{
|
{
|
||||||
char file[100] = "/sys/class/uio/";
|
char file[100] = "/sys/class/uio/";
|
||||||
strncat(file, params.shmDevice , sizeof(file) - 1);
|
strncat(file, shmDevice , sizeof(file) - 1);
|
||||||
strncat(file, "/maps/map0/size", sizeof(file) - 1);
|
strncat(file, "/maps/map0/size", sizeof(file) - 1);
|
||||||
|
|
||||||
int fd = open(file, O_RDONLY);
|
int fd = open(file, O_RDONLY);
|
||||||
if (fd < 0)
|
if (fd < 0)
|
||||||
{
|
{
|
||||||
DEBUG_ERROR("Failed to open: %s", file);
|
DEBUG_ERROR("Failed to open: %s", file);
|
||||||
return -1;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
char size[32];
|
char size[32];
|
||||||
@ -149,7 +148,7 @@ int main(int argc, char * argv[])
|
|||||||
{
|
{
|
||||||
DEBUG_ERROR("Failed to read: %s", file);
|
DEBUG_ERROR("Failed to read: %s", file);
|
||||||
close(fd);
|
close(fd);
|
||||||
return -1;
|
return false;
|
||||||
}
|
}
|
||||||
size[len] = '\0';
|
size[len] = '\0';
|
||||||
close(fd);
|
close(fd);
|
||||||
@ -160,13 +159,13 @@ int main(int argc, char * argv[])
|
|||||||
// open the device
|
// open the device
|
||||||
{
|
{
|
||||||
char file[100] = "/dev/";
|
char file[100] = "/dev/";
|
||||||
strncat(file, params.shmDevice, sizeof(file) - 1);
|
strncat(file, shmDevice, sizeof(file) - 1);
|
||||||
app.shmFD = open(file, O_RDWR, (mode_t)0600);
|
app.shmFD = open(file, O_RDWR, (mode_t)0600);
|
||||||
app.shmMap = MAP_FAILED;
|
app.shmMap = MAP_FAILED;
|
||||||
if (app.shmFD < 0)
|
if (app.shmFD < 0)
|
||||||
{
|
{
|
||||||
DEBUG_ERROR("Failed to open: %s", file);
|
DEBUG_ERROR("Failed to open: %s", file);
|
||||||
return -1;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
DEBUG_INFO("KVMFR Device : %s", file);
|
DEBUG_INFO("KVMFR Device : %s", file);
|
||||||
@ -174,11 +173,7 @@ int main(int argc, char * argv[])
|
|||||||
|
|
||||||
signal(SIGINT, sigHandler);
|
signal(SIGINT, sigHandler);
|
||||||
|
|
||||||
int result = app_main(argc, argv);
|
return true;
|
||||||
os_shmemUnmap();
|
|
||||||
close(app.shmFD);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const char * os_getExecutable()
|
const char * os_getExecutable()
|
||||||
@ -198,7 +193,8 @@ bool os_shmemMmap(void **ptr)
|
|||||||
app.shmMap = mmap(0, app.shmSize, PROT_READ | PROT_WRITE, MAP_SHARED, app.shmFD, 0);
|
app.shmMap = mmap(0, app.shmSize, PROT_READ | PROT_WRITE, MAP_SHARED, app.shmFD, 0);
|
||||||
if (app.shmMap == MAP_FAILED)
|
if (app.shmMap == MAP_FAILED)
|
||||||
{
|
{
|
||||||
DEBUG_ERROR("Failed to map the shared memory device: %s", params.shmDevice);
|
const char * shmDevice = option_get_string("os", "shmDevice");
|
||||||
|
DEBUG_ERROR("Failed to map the shared memory device: %s", shmDevice);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -110,11 +110,6 @@ static BOOL WINAPI CtrlHandler(DWORD dwCtrlType)
|
|||||||
|
|
||||||
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
|
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
|
||||||
{
|
{
|
||||||
int result = 0;
|
|
||||||
HDEVINFO deviceInfoSet;
|
|
||||||
PSP_DEVICE_INTERFACE_DETAIL_DATA infData = NULL;
|
|
||||||
SP_DEVICE_INTERFACE_DATA deviceInterfaceData;
|
|
||||||
|
|
||||||
// convert the command line to the standard argc and argv
|
// convert the command line to the standard argc and argv
|
||||||
LPWSTR * wargv = CommandLineToArgvW(GetCommandLineW(), &app.argc);
|
LPWSTR * wargv = CommandLineToArgvW(GetCommandLineW(), &app.argc);
|
||||||
app.argv = malloc(sizeof(char *) * app.argc);
|
app.argv = malloc(sizeof(char *) * app.argc);
|
||||||
@ -143,57 +138,6 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
|
|||||||
// always flush stderr
|
// always flush stderr
|
||||||
setbuf(stderr, NULL);
|
setbuf(stderr, NULL);
|
||||||
|
|
||||||
deviceInfoSet = SetupDiGetClassDevs(NULL, NULL, NULL, DIGCF_PRESENT | DIGCF_ALLCLASSES | DIGCF_DEVICEINTERFACE);
|
|
||||||
memset(&deviceInterfaceData, 0, sizeof(SP_DEVICE_INTERFACE_DATA));
|
|
||||||
deviceInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
|
|
||||||
|
|
||||||
if (SetupDiEnumDeviceInterfaces(deviceInfoSet, NULL, &GUID_DEVINTERFACE_IVSHMEM, 0, &deviceInterfaceData) == FALSE)
|
|
||||||
{
|
|
||||||
DWORD error = GetLastError();
|
|
||||||
if (error == ERROR_NO_MORE_ITEMS)
|
|
||||||
{
|
|
||||||
DEBUG_WINERROR("Unable to enumerate the device, is it attached?", error);
|
|
||||||
result = -1;
|
|
||||||
goto finish;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEBUG_WINERROR("SetupDiEnumDeviceInterfaces failed", error);
|
|
||||||
result = -1;
|
|
||||||
goto finish;
|
|
||||||
}
|
|
||||||
|
|
||||||
DWORD reqSize = 0;
|
|
||||||
SetupDiGetDeviceInterfaceDetail(deviceInfoSet, &deviceInterfaceData, NULL, 0, &reqSize, NULL);
|
|
||||||
if (!reqSize)
|
|
||||||
{
|
|
||||||
DEBUG_WINERROR("SetupDiGetDeviceInterfaceDetail", GetLastError());
|
|
||||||
result = -1;
|
|
||||||
goto finish;
|
|
||||||
}
|
|
||||||
|
|
||||||
infData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)calloc(reqSize, 1);
|
|
||||||
infData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
|
|
||||||
if (!SetupDiGetDeviceInterfaceDetail(deviceInfoSet, &deviceInterfaceData, infData, reqSize, NULL, NULL))
|
|
||||||
{
|
|
||||||
free(infData);
|
|
||||||
DEBUG_WINERROR("SetupDiGetDeviceInterfaceDetail", GetLastError());
|
|
||||||
result = -1;
|
|
||||||
goto finish;
|
|
||||||
}
|
|
||||||
|
|
||||||
app.shmemHandle = CreateFile(infData->DevicePath, 0, 0, NULL, OPEN_EXISTING, 0, 0);
|
|
||||||
if (app.shmemHandle == INVALID_HANDLE_VALUE)
|
|
||||||
{
|
|
||||||
SetupDiDestroyDeviceInfoList(deviceInfoSet);
|
|
||||||
free(infData);
|
|
||||||
DEBUG_WINERROR("CreateFile returned INVALID_HANDLE_VALUE", GetLastError());
|
|
||||||
result = -1;
|
|
||||||
goto finish;
|
|
||||||
}
|
|
||||||
|
|
||||||
free(infData);
|
|
||||||
SetupDiDestroyDeviceInfoList(deviceInfoSet);
|
|
||||||
|
|
||||||
// setup a handler for ctrl+c
|
// setup a handler for ctrl+c
|
||||||
SetConsoleCtrlHandler(CtrlHandler, TRUE);
|
SetConsoleCtrlHandler(CtrlHandler, TRUE);
|
||||||
|
|
||||||
@ -260,6 +204,62 @@ finish:
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool app_init()
|
||||||
|
{
|
||||||
|
int result = 0;
|
||||||
|
HDEVINFO deviceInfoSet;
|
||||||
|
PSP_DEVICE_INTERFACE_DETAIL_DATA infData = NULL;
|
||||||
|
SP_DEVICE_INTERFACE_DATA deviceInterfaceData;
|
||||||
|
|
||||||
|
deviceInfoSet = SetupDiGetClassDevs(NULL, NULL, NULL, DIGCF_PRESENT | DIGCF_ALLCLASSES | DIGCF_DEVICEINTERFACE);
|
||||||
|
memset(&deviceInterfaceData, 0, sizeof(SP_DEVICE_INTERFACE_DATA));
|
||||||
|
deviceInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
|
||||||
|
|
||||||
|
if (SetupDiEnumDeviceInterfaces(deviceInfoSet, NULL, &GUID_DEVINTERFACE_IVSHMEM, 0, &deviceInterfaceData) == FALSE)
|
||||||
|
{
|
||||||
|
DWORD error = GetLastError();
|
||||||
|
if (error == ERROR_NO_MORE_ITEMS)
|
||||||
|
{
|
||||||
|
DEBUG_WINERROR("Unable to enumerate the device, is it attached?", error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG_WINERROR("SetupDiEnumDeviceInterfaces failed", error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD reqSize = 0;
|
||||||
|
SetupDiGetDeviceInterfaceDetail(deviceInfoSet, &deviceInterfaceData, NULL, 0, &reqSize, NULL);
|
||||||
|
if (!reqSize)
|
||||||
|
{
|
||||||
|
DEBUG_WINERROR("SetupDiGetDeviceInterfaceDetail", GetLastError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
infData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)calloc(reqSize, 1);
|
||||||
|
infData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
|
||||||
|
if (!SetupDiGetDeviceInterfaceDetail(deviceInfoSet, &deviceInterfaceData, infData, reqSize, NULL, NULL))
|
||||||
|
{
|
||||||
|
free(infData);
|
||||||
|
DEBUG_WINERROR("SetupDiGetDeviceInterfaceDetail", GetLastError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
app.shmemHandle = CreateFile(infData->DevicePath, 0, 0, NULL, OPEN_EXISTING, 0, 0);
|
||||||
|
if (app.shmemHandle == INVALID_HANDLE_VALUE)
|
||||||
|
{
|
||||||
|
SetupDiDestroyDeviceInfoList(deviceInfoSet);
|
||||||
|
free(infData);
|
||||||
|
DEBUG_WINERROR("CreateFile returned INVALID_HANDLE_VALUE", GetLastError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(infData);
|
||||||
|
SetupDiDestroyDeviceInfoList(deviceInfoSet);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
const char * os_getExecutable()
|
const char * os_getExecutable()
|
||||||
{
|
{
|
||||||
return app.executable;
|
return app.executable;
|
||||||
|
@ -21,6 +21,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
|||||||
#include "interface/capture.h"
|
#include "interface/capture.h"
|
||||||
#include "dynamic/capture.h"
|
#include "dynamic/capture.h"
|
||||||
#include "common/debug.h"
|
#include "common/debug.h"
|
||||||
|
#include "common/option.h"
|
||||||
#include "common/locking.h"
|
#include "common/locking.h"
|
||||||
#include "common/KVMFR.h"
|
#include "common/KVMFR.h"
|
||||||
#include "common/crash.h"
|
#include "common/crash.h"
|
||||||
@ -256,11 +257,29 @@ static bool captureRestart()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// this is called from the platform specific startup routine
|
||||||
int app_main(int argc, char * argv[])
|
int app_main(int argc, char * argv[])
|
||||||
{
|
{
|
||||||
if (!installCrashHandler(os_getExecutable()))
|
if (!installCrashHandler(os_getExecutable()))
|
||||||
DEBUG_WARN("Failed to install the crash handler");
|
DEBUG_WARN("Failed to install the crash handler");
|
||||||
|
|
||||||
|
// register capture interface options
|
||||||
|
for(int i = 0; CaptureInterfaces[i]; ++i)
|
||||||
|
if (CaptureInterfaces[i]->initOptions)
|
||||||
|
CaptureInterfaces[i]->initOptions();
|
||||||
|
|
||||||
|
// parse the command line arguments
|
||||||
|
if (!option_parse(argc, argv))
|
||||||
|
{
|
||||||
|
option_free();
|
||||||
|
DEBUG_ERROR("Failure to parse the command line");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// perform platform specific initialization
|
||||||
|
if (!app_init())
|
||||||
|
return -1;
|
||||||
|
|
||||||
unsigned int shmemSize = os_shmemSize();
|
unsigned int shmemSize = os_shmemSize();
|
||||||
uint8_t * shmemMap = NULL;
|
uint8_t * shmemMap = NULL;
|
||||||
int exitcode = 0;
|
int exitcode = 0;
|
||||||
|
@ -5,10 +5,14 @@ include_directories(
|
|||||||
${PROJECT_SOURCE_DIR}/include
|
${PROJECT_SOURCE_DIR}/include
|
||||||
)
|
)
|
||||||
|
|
||||||
|
set(SOURCES
|
||||||
|
src/option.c
|
||||||
|
)
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
add_library(lg_common STATIC src/crash.windows.c)
|
add_library(lg_common STATIC src/crash.windows.c ${SOURCES})
|
||||||
else()
|
else()
|
||||||
add_library(lg_common STATIC src/crash.linux.c)
|
add_library(lg_common STATIC src/crash.linux.c ${SOURCES})
|
||||||
target_link_libraries(lg_common bfd)
|
target_link_libraries(lg_common bfd)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
75
common/include/common/option.h
Normal file
75
common/include/common/option.h
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
KVMGFX Client - A KVM Client for VGA Passthrough
|
||||||
|
Copyright (C) 2017-2019 Geoffrey McRae <geoff@hostfission.com>
|
||||||
|
https://looking-glass.hostfission.com
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify it under
|
||||||
|
the terms of the GNU General Public License as published by the Free Software
|
||||||
|
Foundation; either version 2 of the License, or (at your option) any later
|
||||||
|
version.
|
||||||
|
|
||||||
|
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. 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., 59 Temple
|
||||||
|
Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
enum OptionType
|
||||||
|
{
|
||||||
|
OPTION_TYPE_NONE = 0,
|
||||||
|
OPTION_TYPE_INT,
|
||||||
|
OPTION_TYPE_STRING,
|
||||||
|
OPTION_TYPE_BOOL
|
||||||
|
};
|
||||||
|
|
||||||
|
struct OptionState;
|
||||||
|
|
||||||
|
struct OptionValue
|
||||||
|
{
|
||||||
|
enum OptionType type;
|
||||||
|
union
|
||||||
|
{
|
||||||
|
int x_int;
|
||||||
|
char * x_string;
|
||||||
|
bool x_bool;
|
||||||
|
}
|
||||||
|
v;
|
||||||
|
|
||||||
|
// internal state
|
||||||
|
struct OptionState * state;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Option
|
||||||
|
{
|
||||||
|
const char * module;
|
||||||
|
const char * name;
|
||||||
|
const char * description;
|
||||||
|
struct OptionValue value;
|
||||||
|
|
||||||
|
bool (*validator)(struct OptionValue * value);
|
||||||
|
void (*printHelp)();
|
||||||
|
};
|
||||||
|
|
||||||
|
// register an NULL terminated array of options
|
||||||
|
bool option_register(struct Option options[]);
|
||||||
|
|
||||||
|
// lookup the value of an option
|
||||||
|
struct OptionValue * option_get (const char * module, const char * name);
|
||||||
|
int option_get_int (const char * module, const char * name);
|
||||||
|
const char * option_get_string(const char * module, const char * name);
|
||||||
|
bool option_get_bool (const char * module, const char * name);
|
||||||
|
|
||||||
|
// called by the main application to parse the command line arguments
|
||||||
|
bool option_parse(int argc, char * argv[]);
|
||||||
|
|
||||||
|
// print out the options, help, and their current values
|
||||||
|
void option_print();
|
||||||
|
|
||||||
|
// final cleanup
|
||||||
|
void option_free();
|
267
common/src/option.c
Normal file
267
common/src/option.c
Normal file
@ -0,0 +1,267 @@
|
|||||||
|
/*
|
||||||
|
KVMGFX Client - A KVM Client for VGA Passthrough
|
||||||
|
Copyright (C) 2017-2019 Geoffrey McRae <geoff@hostfission.com>
|
||||||
|
https://looking-glass.hostfission.com
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify it under
|
||||||
|
the terms of the GNU General Public License as published by the Free Software
|
||||||
|
Foundation; either version 2 of the License, or (at your option) any later
|
||||||
|
version.
|
||||||
|
|
||||||
|
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. 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., 59 Temple
|
||||||
|
Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "common/option.h"
|
||||||
|
#include "common/debug.h"
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
struct OptionGroup
|
||||||
|
{
|
||||||
|
const char * module;
|
||||||
|
struct Option ** options;
|
||||||
|
int count;
|
||||||
|
int pad;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct State
|
||||||
|
{
|
||||||
|
struct Option * options;
|
||||||
|
int oCount;
|
||||||
|
struct OptionGroup * groups;
|
||||||
|
int gCount;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct State state =
|
||||||
|
{
|
||||||
|
.options = NULL,
|
||||||
|
.oCount = 0,
|
||||||
|
.groups = NULL,
|
||||||
|
.gCount = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
bool option_register(struct Option options[])
|
||||||
|
{
|
||||||
|
int new = 0;
|
||||||
|
for(int i = 0; options[i].value.type != OPTION_TYPE_NONE; ++i)
|
||||||
|
++new;
|
||||||
|
|
||||||
|
state.options = realloc(
|
||||||
|
state.options,
|
||||||
|
sizeof(struct Option) * (state.oCount + new)
|
||||||
|
);
|
||||||
|
|
||||||
|
for(int i = 0; options[i].value.type != OPTION_TYPE_NONE; ++i)
|
||||||
|
{
|
||||||
|
struct Option * o = &state.options[state.oCount + i];
|
||||||
|
memcpy(o, &options[i], sizeof(struct Option));
|
||||||
|
|
||||||
|
// ensure the string is locally allocated
|
||||||
|
if (o->value.type == OPTION_TYPE_STRING)
|
||||||
|
o->value.v.x_string = strdup(o->value.v.x_string);
|
||||||
|
|
||||||
|
// add the option to the correct group for help printout
|
||||||
|
bool found = false;
|
||||||
|
for(int g = 0; g < state.gCount; ++g)
|
||||||
|
{
|
||||||
|
struct OptionGroup * group = &state.groups[g];
|
||||||
|
if (strcmp(group->module, o->module) != 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
found = true;
|
||||||
|
group->options = realloc(
|
||||||
|
group->options,
|
||||||
|
sizeof(struct Option *) * (group->count + 1)
|
||||||
|
);
|
||||||
|
group->options[group->count] = o;
|
||||||
|
|
||||||
|
int len = strlen(o->name);
|
||||||
|
if (len > group->pad)
|
||||||
|
group->pad = len;
|
||||||
|
|
||||||
|
++group->count;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found)
|
||||||
|
{
|
||||||
|
state.groups = realloc(
|
||||||
|
state.groups,
|
||||||
|
sizeof(struct OptionGroup) * (state.gCount + 1)
|
||||||
|
);
|
||||||
|
|
||||||
|
struct OptionGroup * group = &state.groups[state.gCount];
|
||||||
|
++state.gCount;
|
||||||
|
|
||||||
|
group->module = o->module;
|
||||||
|
group->options = malloc(sizeof(struct Option *));
|
||||||
|
group->options[0] = o;
|
||||||
|
group->count = 1;
|
||||||
|
group->pad = strlen(o->name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
state.oCount += new;
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
void option_free()
|
||||||
|
{
|
||||||
|
for(int i = 0; i < state.oCount; ++i)
|
||||||
|
{
|
||||||
|
struct Option * o = &state.options[i];
|
||||||
|
if (o->value.type == OPTION_TYPE_STRING)
|
||||||
|
free(o->value.v.x_string);
|
||||||
|
}
|
||||||
|
free(state.options);
|
||||||
|
state.options = NULL;
|
||||||
|
state.oCount = 0;
|
||||||
|
|
||||||
|
free(state.groups);
|
||||||
|
state.groups = NULL;
|
||||||
|
state.gCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool option_parse(int argc, char * argv[])
|
||||||
|
{
|
||||||
|
for(int a = 1; a < argc; ++a)
|
||||||
|
{
|
||||||
|
if (strcmp(argv[a], "-h") == 0 || strcmp(argv[a], "--help") == 0)
|
||||||
|
{
|
||||||
|
option_print();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
char * arg = strdup(argv[a]);
|
||||||
|
char * module = strtok(arg , ":");
|
||||||
|
char * name = strtok(NULL, "=");
|
||||||
|
char * value = strtok(NULL, "" );
|
||||||
|
|
||||||
|
if (!module || !name || !value)
|
||||||
|
{
|
||||||
|
DEBUG_WARN("Ignored invalid argument: %s", argv[a]);
|
||||||
|
free(arg);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool found = false;
|
||||||
|
struct Option * o;
|
||||||
|
for(int i = 0; i < state.oCount; ++i)
|
||||||
|
{
|
||||||
|
o = &state.options[i];
|
||||||
|
if ((strcmp(o->module, module) != 0) || (strcmp(o->name, name) != 0))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found)
|
||||||
|
{
|
||||||
|
DEBUG_WARN("Ignored unknown argument: %s", argv[a]);
|
||||||
|
free(arg);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(o->value.type)
|
||||||
|
{
|
||||||
|
case OPTION_TYPE_INT:
|
||||||
|
o->value.v.x_int = atol(value);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OPTION_TYPE_STRING:
|
||||||
|
free(o->value.v.x_string);
|
||||||
|
o->value.v.x_string = strdup(value);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OPTION_TYPE_BOOL:
|
||||||
|
o->value.v.x_bool =
|
||||||
|
strcmp(value, "1" ) == 0 ||
|
||||||
|
strcmp(value, "yes" ) == 0 ||
|
||||||
|
strcmp(value, "true") == 0 ||
|
||||||
|
strcmp(value, "on" ) == 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
DEBUG_ERROR("BUG: Invalid option type, this should never happen");
|
||||||
|
assert(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (o->validator)
|
||||||
|
if (!o->validator(&o->value))
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("Invalid value provided to option: %s", argv[a]);
|
||||||
|
|
||||||
|
if (o->printHelp)
|
||||||
|
o->printHelp();
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void option_print()
|
||||||
|
{
|
||||||
|
printf(
|
||||||
|
"The following is a complete list of options accepted by this application\n\n"
|
||||||
|
);
|
||||||
|
|
||||||
|
for(int g = 0; g < state.gCount; ++g)
|
||||||
|
{
|
||||||
|
for(int i = 0; i < state.groups[g].count; ++i)
|
||||||
|
{
|
||||||
|
struct Option * o = state.groups[g].options[i];
|
||||||
|
printf(" %s:%-*s - %s\n", o->module, state.groups[g].pad, o->name, o->description);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct OptionValue * option_get(const char * module, const char * name)
|
||||||
|
{
|
||||||
|
for(int i = 0; i < state.oCount; ++i)
|
||||||
|
{
|
||||||
|
struct Option * o = &state.options[i];
|
||||||
|
if ((strcmp(o->module, module) == 0) || (strcmp(o->name, name) == 0))
|
||||||
|
return &o->value;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int option_get_int(const char * module, const char * name)
|
||||||
|
{
|
||||||
|
struct OptionValue * o = option_get(module, name);
|
||||||
|
if (!o)
|
||||||
|
return -1;
|
||||||
|
assert(o->type == OPTION_TYPE_INT);
|
||||||
|
return o->v.x_int;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char * option_get_string(const char * module, const char * name)
|
||||||
|
{
|
||||||
|
struct OptionValue * o = option_get(module, name);
|
||||||
|
if (!o)
|
||||||
|
return NULL;
|
||||||
|
assert(o->type == OPTION_TYPE_STRING);
|
||||||
|
return o->v.x_string;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool option_get_bool(const char * module, const char * name)
|
||||||
|
{
|
||||||
|
struct OptionValue * o = option_get(module, name);
|
||||||
|
if (!o)
|
||||||
|
return false;
|
||||||
|
assert(o->type == OPTION_TYPE_BOOL);
|
||||||
|
return o->v.x_bool;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user