#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 [