From 390576c3839ee7abb845e27b7267de45495e6b2f Mon Sep 17 00:00:00 2001 From: Nathanael Sensfelder Date: Mon, 23 Dec 2019 15:44:19 +0100 Subject: Starting to turn relabsd into a proper daemon... --- src/config/config_file.c | 821 +++++++++++++++++++++++++++++++++++++++++++++++ src/config/parameters.c | 287 +++++++++++++++++ 2 files changed, 1108 insertions(+) create mode 100644 src/config/config_file.c create mode 100644 src/config/parameters.c (limited to 'src/config') diff --git a/src/config/config_file.c b/src/config/config_file.c new file mode 100644 index 0000000..ad060a5 --- /dev/null +++ b/src/config/config_file.c @@ -0,0 +1,821 @@ +#include +#include +#include +#include +#include + +#include + +#include "error.h" +#include "pervasive.h" +#include "axis.h" +#include "config.h" + +#ifndef RELABSD_OPTION_MAX_SIZE + #define RELABSD_OPTION_MAX_SIZE 64 +#endif +/* + * "errno is never set to zero by any system call or library function." + * This file makes use of this, by setting it to zero and checking if + * it was modified after calling an function (I'm guessing this is common + * practice, but I think it's worth explaining). + * Following the principle of least astonishment, if a function sets errno to + * zero, it will not return before setting it back either to its previous + * value or to a arbitrary nonzero value. + */ + +/* + * Returns -1 on (fatal) error, + * 0 on EOF, + * 1 on newline. + */ +static int reach_next_line_or_eof (FILE * const f) +{ + int prev_errno; + char c; + + prev_errno = errno; + + errno = 0; + + c = (char) getc(f); + + while ((c != '\n') && c != EOF) + { + c = (char) getc(f); + } + + if (errno != 0) + { + RELABSD_FATAL + ( + "[CONFIG] Error while attempting to reach EOF or next line: %s.", + strerror(errno) + ); + + errno = prev_errno; + + return -1; + } + + errno = prev_errno; + + if (c == EOF) + { + return 0; + } + + return 1; +} + +/* + * Returns -1 if the option was discarded (an error has been reported), + * 0 if the option was successfully parsed. + * + * ('length' - 1) is the number of relevant characters in 'name'. + * 'name' must support 'length' characters. + * name[length] will be set to \0, so it does not need to be when calling + * this function. + */ +static int parse_option +( + struct relabsd_config * const conf, + enum relabsd_axis const axis, + char * const name, + int const length +) +{ + name[length] = '\0'; + + if (strcmp(name, "direct") == 0) + { + conf->axis[axis].option[RELABSD_DIRECT_OPTION] = 1; + + RELABSD_DEBUG + ( + RELABSD_DEBUG_CONFIG, + "Axis '%s' enabled option 'direct'.", + relabsd_axis_to_name(axis) + ); + + if (conf->axis[axis].option[RELABSD_FRAMED_OPTION]) + { + RELABSD_WARNING + ( + "[CONFIG] Axis '%s': using option 'direct' discards option" + "'framed'.", + relabsd_axis_to_name(axis) + ); + } + } + else if (strcmp(name, "real_fuzz") == 0) + { + conf->axis[axis].option[RELABSD_REAL_FUZZ_OPTION] = 1; + + RELABSD_DEBUG + ( + RELABSD_DEBUG_CONFIG, + "Axis '%s' enabled option 'real_fuzz'.", + relabsd_axis_to_name(axis) + ); + } + else if (strcmp(name, "framed") == 0) + { + conf->axis[axis].option[RELABSD_FRAMED_OPTION] = 1; + + RELABSD_DEBUG + ( + RELABSD_DEBUG_CONFIG, + "Axis '%s' enabled option 'framed'.", + relabsd_axis_to_name(axis) + ); + + if (conf->axis[axis].option[RELABSD_DIRECT_OPTION]) + { + RELABSD_WARNING + ( + "[CONFIG] Axis '%s': using option 'direct' discards option" + "'framed'.", + relabsd_axis_to_name(axis) + ); + } + } + else + { + RELABSD_ERROR + ( + "[CONFIG] Unknown option '%s' for axis '%s'.", + name, + relabsd_axis_to_name(axis) + ); + + return -1; + } + + return 0; +} + +/* + * Returns -1 on error, + * 0 on EOF, + * 1 on newline. + */ +static int read_axis_options +( + struct relabsd_config * const conf, + FILE * const f, + enum relabsd_axis const axis +) +{ + char option[(RELABSD_OPTION_MAX_SIZE + 1)]; + int i, prev_errno; + char c; + + option[RELABSD_OPTION_MAX_SIZE] = '\0'; + + prev_errno = errno; + + errno = 0; + + memset(conf->axis[axis].option, 0, RELABSD_OPTIONS_COUNT * sizeof(int)); + + i = 0; + + while (i <= RELABSD_OPTION_MAX_SIZE) + { + c = (char) getc(f); + + if ((errno != 0) && (c == EOF)) + { + RELABSD_FATAL + ( + "[CONFIG] Reading error while parsing option name (axis '%s'): %s.", + relabsd_axis_to_name(axis), + strerror(errno) + ); + + errno = prev_errno; + + return -1; + } + + switch (c) + { + case ' ': + case '\t': + break; + + case ',': + /* We parsed a new option and there is a least another. */ + parse_option(conf, axis, option, i); + + i = 0; + + break; + + case '\n': + parse_option(conf, axis, option, i); + errno = prev_errno; + + return 1; + + case EOF: + parse_option(conf, axis, option, i); + errno = prev_errno; + + return 0; + + default: + option[i] = c; + i++; + + break; + } + } + + RELABSD_FATAL + ( + "[CONFIG] Option name '%s[...]' (axis '%s') is too long (%d chars max).", + option, + relabsd_axis_to_name(axis), + RELABSD_OPTION_MAX_SIZE + ); + + return -1; +} + +static int parse_timeout_option +( + struct relabsd_config * const conf, + const char * const param +) +{ + int timeout_msec; + const int prev_errno = errno; + + conf->enable_timeout = 1; + + errno = 0; + + timeout_msec = atoi(param); + + if (timeout_msec <= 0) + { + RELABSD_FATAL + ( + "Illegal value for timeout \"%d\": accepted range is [1, %d].", + timeout_msec, + INT_MAX + ); + + return -1; + } + + memset((void *) &(conf->timeout), 0, sizeof(struct timeval)); + + conf->timeout.tv_sec = (time_t) (timeout_msec / 1000); + + conf->timeout.tv_usec = + ( + ((suseconds_t) timeout_msec) + * ((suseconds_t) 1000) + ); + + return 0; +} + +/* + * Returns -1 on (fatal) error, + * 0 on succes. + */ +static int parse_axis_configuration_line +( + struct relabsd_config * const conf, + FILE * const f, + const char * const buffer +) +{ + int valc, prev_errno; + enum relabsd_axis axis; + + axis = relabsd_axis_from_name(buffer); + + if (axis == RELABSD_UNKNOWN) + { + RELABSD_FATAL + ( + "[CONFIG] Unknown axis '%s'.", + buffer + ); + + return -1; + } + + prev_errno = errno; + errno = 0; + + valc = + fscanf + ( + f, + "%d %d %d %d %d", + &(conf->axis[axis].min), + &(conf->axis[axis].max), + &(conf->axis[axis].fuzz), + &(conf->axis[axis].flat), + &(conf->axis[axis].resolution) + ); + + if (valc == EOF) + { + if (errno == 0) + { + RELABSD_FATAL + ( + "[CONFIG] Unexpected end of file while reading axis '%s'.", + buffer + ); + } + else + { + RELABSD_FATAL + ( + "[CONFIG] An error occured while reading axis '%s': %s.", + buffer, + strerror(errno) + ); + } + + errno = prev_errno; + + return -1; + } + else if (valc < 5) + { + RELABSD_FATAL + ( + "[CONFIG] Invalid parameter count for axis '%s'.", + buffer + ); + + errno = prev_errno; + + return -1; + } + + RELABSD_DEBUG + ( + RELABSD_DEBUG_CONFIG, + "Axis '%s': {min = %d; max = %d; fuzz = %d; flat = %d; resolution = %d}", + buffer, + conf->axis[axis].min, + conf->axis[axis].max, + conf->axis[axis].fuzz, + conf->axis[axis].flat, + conf->axis[axis].resolution + ); + + errno = prev_errno; + + conf->axis[axis].enabled = 1; + conf->axis[axis].previous_value = 0; + + return read_axis_options(conf, f, axis); +} + +/* + * Returns -1 on (fatal) error, + * 0 on EOF, + * 1 on newline. + */ +static int read_config_line +( + struct relabsd_config * const conf, + FILE * const f, + const char * const prefix +) +{ + if (!RELABSD_IS_PREFIX("#", prefix)) + { + return parse_axis_configuration_line(conf, f, prefix); + } + + return reach_next_line_or_eof(f); +} + +/* + * Returns -1 on (fatal) error, + * 0 on success. + */ +static int read_config_file +( + struct relabsd_config * const conf, + char * const filename +) +{ + FILE * f; + char buffer[(RELABSD_CONF_AXIS_CODE_SIZE + 1)]; + int continue_reading, prev_errno; + + buffer[RELABSD_CONF_AXIS_CODE_SIZE] = '\0'; + + f = fopen(filename, "r"); + + if (f == (FILE *) NULL) + { + RELABSD_FATAL + ( + "[CONFIG] Could not open file: %s.", + strerror(errno) + ); + + return -1; + } + + + prev_errno = errno; + errno = 0; + + continue_reading = 1; + + while + ( + (continue_reading == 1) + && + ( + fscanf + ( + f, + "%" RELABSD_TO_STRING(RELABSD_CONF_AXIS_CODE_SIZE) "s", + buffer + ) + != EOF + ) + ) + { + switch (read_config_line(conf, f, buffer)) + { + case 1: + /* Everything is going well. */ + break; + + case 0: + /* EOF reached. */ + continue_reading = 0; + break; + + case -1: + /* A fatal error occured. */ + errno = prev_errno; + + fclose(f); + return -1; + } + } + + if (errno != 0) + { + /* An error happened in the while loop condition. */ + RELABSD_FATAL + ( + "[CONFIG] Error while reading file: %s, last read '%s'.", + strerror(errno), + buffer + ); + + errno = prev_errno; + + fclose(f); + + return -1; + } + + errno = prev_errno; + + fclose(f); + + return 0; +} + +static void print_usage +( + const char * const exec +) +{ + RELABSD_FATAL + ( + "USAGE: %s input_device config_file [