From a5c69b0d0cc9efbf824621c99c607305a7842123 Mon Sep 17 00:00:00 2001 From: Johannes Marbach Date: Sun, 12 Mar 2023 20:24:28 +0000 Subject: [PATCH] Override config from conf.d --- CHANGELOG.md | 1 + README.md | 29 ++++----- command_line.c | 27 +++------ config.c | 155 ++++++++++++++++++++++++++++++++++++++++--------- config.h | 27 ++++++++- main.c | 5 +- 6 files changed, 180 insertions(+), 64 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 476fade..1df9fa4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ Nothing at the moment - feat!: Handle input device connection/disconnection at runtime (#38) - feat: Update lv_drivers to git master (2023-03-11) - feat: Update lvgl to v8.3 (2023-03-08) +- feat!: Deprecate -c CLI option and add support for reading from /etc/unl0kr.conf.d/ ## 0.3.0 (2022-11-13) diff --git a/README.md b/README.md index cd90c88..5d95493 100644 --- a/README.md +++ b/README.md @@ -39,20 +39,23 @@ A man page is planned to be added with #6. For the time being, you can get an ov $ unl0kr --help Usage: unl0kr [OPTION] +Unl0kr values the CRYPTTAB_TRIED variable. Upon completion, the entered +password is printed to STDOUT. All other output happens on STDERR. + Mandatory arguments to long options are mandatory for short options too. - -c, --config=PATH Locaton of the main config file. Defaults to - /etc/unl0kr.conf. - -C, --config-override Location of the config override file. Values in - this file override values for the same keys in the - main config file. If specified multiple times, the - values from consecutive files will be merged in - order. - -g, --geometry=NxM Force a display size of N horizontal times M - vertical pixels - -d --dpi=N Overrides the DPI - -h, --help Print this message and exit - -v, --verbose Enable more detailed logging output on STDERR - -V, --version Print the unl0kr version and exit + -C, --config-override Path to a config override file. Can be supplied + multiple times. Config files are merged in the + following order: + * /etc/unl0kr.conf + * /etc/unl0kr.conf.d/* (alphabetically) + * Override files (in supplied order) + -g, --geometry=NxM[@X,Y] Force a display size of N horizontal times M + vertical pixels, offset horizontally by X + pixels and vertically by Y pixels + -d --dpi=N Override the display's DPI value + -h, --help Print this message and exit + -v, --verbose Enable more detailed logging output on STDERR + -V, --version Print the unl0kr version and exit ``` For an example configuration file, see [unl0kr.conf]. diff --git a/command_line.c b/command_line.c index e34936d..fa3388d 100644 --- a/command_line.c +++ b/command_line.c @@ -50,14 +50,8 @@ static void print_usage(); */ static void init_opts(ul_cli_opts *opts) { - opts->num_config_files = 1; - - opts->config_files = malloc(sizeof(char *)); - if (!opts->config_files) { - ul_log(UL_LOG_LEVEL_ERROR, "Could not allocate memory for config file paths"); - exit(EXIT_FAILURE); - } - opts->config_files[0] = "/etc/unl0kr.conf"; + opts->num_config_files = 0; + opts->config_files = NULL; opts->hor_res = -1; opts->ver_res = -1; @@ -75,13 +69,12 @@ static void print_usage() { "password is printed to STDOUT. All other output happens on STDERR.\n" "\n" "Mandatory arguments to long options are mandatory for short options too.\n" - " -c, --config=PATH Locaton of the main config file. Defaults to\n" - " /etc/unl0kr.conf.\n" - " -C, --config-override Location of the config override file. Values in\n" - " this file override values for the same keys in\n" - " the main config file. If specified multiple\n" - " times, the values from consecutive files will be\n" - " merged in order.\n" + " -C, --config-override Path to a config override file. Can be supplied\n" + " multiple times. Config files are merged in the\n" + " following order:\n" + " * /etc/unl0kr.conf\n" + " * /etc/unl0kr.conf.d/* (alphabetically)\n" + " * Override files (in supplied order)\n" " -g, --geometry=NxM[@X,Y] Force a display size of N horizontal times M\n" " vertical pixels, offset horizontally by X\n" " pixels and vertically by Y pixels\n" @@ -101,7 +94,6 @@ void ul_cli_parse_opts(int argc, char *argv[], ul_cli_opts *opts) { init_opts(opts); struct option long_opts[] = { - { "config", required_argument, NULL, 'c' }, { "config-override", required_argument, NULL, 'C' }, { "geometry", required_argument, NULL, 'g' }, { "dpi", required_argument, NULL, 'd' }, @@ -115,9 +107,6 @@ void ul_cli_parse_opts(int argc, char *argv[], ul_cli_opts *opts) { while ((opt = getopt_long(argc, argv, "c:C:g:d:hvV", long_opts, &index)) != -1) { switch (opt) { - case 'c': - opts->config_files[0] = optarg; - break; case 'C': opts->config_files = realloc(opts->config_files, (opts->num_config_files + 1) * sizeof(char *)); if (!opts->config_files) { diff --git a/config.c b/config.c index 22a0163..3860bda 100644 --- a/config.c +++ b/config.c @@ -24,6 +24,7 @@ #include "lvgl/lvgl.h" +#include #include #include @@ -35,19 +36,31 @@ */ /** - * Initialise a config options struct with default values. - * - * @param opts pointer to the options struct + * Compares two strings from opaque types. + * + * @param a first string as void pointer + * @param b second string as void pointer + * @return a positive integer if a > b, a negative integer if a < b and 0 otherwise */ -static void init_opts(ul_config_opts *opts); +static int compare_strings(const void* a, const void* b); /** - * Parse options from a configuration file. - * - * @param path path to configuration file - * @param opts pointer for writing the parsed options into + * Checks whether a string ends with a suffix + * + * @param string string to check + * @param suffix suffix to compare to + * @return true if the suffix matches at the end of the string, false otherwise */ -static void parse_file(const char *path, ul_config_opts *opts); +static bool string_ends_with(const char *string, const char *suffix); + +/** + * Non-recursively searches a directory for configuration files. + * + * @param path folder to search in + * @param found pointer to write found configuration file names into (to be freed by the caller) + * @param num_found pointer to write number of found files into + */ +static void find_files(const char *path, char ***found, int *num_found); /** * Handle parsing events from INIH. @@ -74,26 +87,71 @@ static bool parse_bool(const char *value, bool *result); * Static functions */ -static void init_opts(ul_config_opts *opts) { - opts->general.animations = false; - opts->general.backend = ul_backends_backends[0] == NULL ? UL_BACKENDS_BACKEND_NONE : 0; - opts->general.timeout = 0; - opts->keyboard.autohide = true; - opts->keyboard.layout_id = SQ2LV_LAYOUT_US; - opts->keyboard.popovers = true; - opts->textarea.obscured = true; - opts->textarea.bullet = LV_SYMBOL_BULLET; - opts->theme.default_id = UL_THEMES_THEME_BREEZY_DARK; - opts->theme.alternate_id = UL_THEMES_THEME_BREEZY_LIGHT; - opts->input.keyboard = true; - opts->input.pointer = true; - opts->input.touchscreen = true; +static int compare_strings(const void* a, const void* b) { + return strcmp(*(const char**)a, *(const char**)b); } -static void parse_file(const char *path, ul_config_opts *opts) { - if (ini_parse(path, parsing_handler, opts) != 0) { - ul_log(UL_LOG_LEVEL_ERROR, "Ignoring invalid config file %s", path); +static bool string_ends_with(const char *string, const char *suffix) { + if (!string || !suffix || strlen(suffix) > strlen(string)) { + return false; } + return strncmp(string + strlen(string) - strlen(suffix), suffix, strlen(suffix)) == 0; +} + +static void find_files(const char *path, char ***found, int *num_found) { + /* Initialise output variables */ + *found = NULL; + *num_found = 0; + + /* Count length of directory path */ + const int path_length = strlen(path); + + /* Open directory */ + DIR *d = opendir(path); + if (!d) { + ul_log(UL_LOG_LEVEL_WARNING, "Could not read contents of folder %s", path); + return; + } + + /* Loop over directory contents */ + struct dirent *dir; + while ((dir = readdir(d)) != NULL) { + /* Ignore anything except for .conf files */ + if (dir->d_type != DT_REG || !string_ends_with(dir->d_name, ".conf")) { + continue; + } + + /* Grow output array */ + char **tmp = realloc(*found, (*num_found + 1) * sizeof(char *)); + if (!tmp) { + ul_log(UL_LOG_LEVEL_ERROR, "Could not reallocate memory for configuration file paths"); + break; + } + *found = tmp; + + /* Extract file name and length */ + char *name = dir->d_name; + int name_length = strlen(name); + + /* Allocate memory for full path */ + char *found_path = malloc(path_length + name_length + 2); /* +1 for path separator and null terminator, respectively */ + if (!found_path) { + ul_log(UL_LOG_LEVEL_ERROR, "Could not allocate memory for configuration file path"); + break; + } + + /* Build full path */ + memcpy(found_path, path, path_length); + found_path[path_length] = '/'; + memcpy(found_path + path_length + 1, dir->d_name, name_length + 1); /* +1 for path separator and null terminator, respectively */ + + /* Store file path */ + (*found)[*num_found] = found_path; + *num_found += 1; + } + + /* Close directory */ + closedir(d); } static int parsing_handler(void* user_data, const char* section, const char* key, const char* value) { @@ -196,9 +254,48 @@ static bool parse_bool(const char *value, bool *result) { * Public functions */ -void ul_config_parse(const char **files, int num_files, ul_config_opts *opts) { - init_opts(opts); +void ul_config_init_opts(ul_config_opts *opts) { + opts->general.animations = false; + opts->general.backend = ul_backends_backends[0] == NULL ? UL_BACKENDS_BACKEND_NONE : 0; + opts->general.timeout = 0; + opts->keyboard.autohide = true; + opts->keyboard.layout_id = SQ2LV_LAYOUT_US; + opts->keyboard.popovers = true; + opts->textarea.obscured = true; + opts->textarea.bullet = LV_SYMBOL_BULLET; + opts->theme.default_id = UL_THEMES_THEME_BREEZY_DARK; + opts->theme.alternate_id = UL_THEMES_THEME_BREEZY_LIGHT; + opts->input.keyboard = true; + opts->input.pointer = true; + opts->input.touchscreen = true; +} + +void ul_config_parse_directory(const char *path, ul_config_opts *opts) { + /* Find files in directory */ + char **found = NULL; + int num_found = 0; + find_files(path, &found, &num_found); + + /* Sort and parse files */ + qsort(found, num_found, sizeof(char *), compare_strings); + ul_config_parse_files((const char **)found, num_found, opts); + + /* Free memory */ + for (int i = 0; i < num_found; ++i) { + free(found[i]); + } + free(found); +} + +void ul_config_parse_files(const char **files, int num_files, ul_config_opts *opts) { for (int i = 0; i < num_files; ++i) { - parse_file(files[i], opts); + ul_config_parse_file(files[i], opts); + } +} + +void ul_config_parse_file(const char *path, ul_config_opts *opts) { + ul_log(UL_LOG_LEVEL_VERBOSE, "Parsing config file %s", path); + if (ini_parse(path, parsing_handler, opts) != 0) { + ul_log(UL_LOG_LEVEL_ERROR, "Ignoring invalid config file %s", path); } } diff --git a/config.h b/config.h index ac6c16e..fc94fb8 100644 --- a/config.h +++ b/config.h @@ -103,12 +103,35 @@ typedef struct { } ul_config_opts; /** - * Parse options from one or more configuration files. + * Initialise a config options struct with default values. + * + * @param opts pointer to the options struct + */ +void ul_config_init_opts(ul_config_opts *opts); + +/** + * Find configuration files in a directory and parse them in alphabetic order. + * + * @param path directory path + * @param opts pointer for writing the parsed options into + */ +void ul_config_parse_directory(const char *path, ul_config_opts *opts); + +/** + * Parse one or more configuration files. * * @param files paths to configuration files * @param num_files number of configuration files * @param opts pointer for writing the parsed options into */ -void ul_config_parse(const char **files, int num_files, ul_config_opts *opts); +void ul_config_parse_files(const char **files, int num_files, ul_config_opts *opts); + +/** + * Parse a configuration file. + * + * @param path path to configuration file + * @param opts pointer for writing the parsed options into + */ +void ul_config_parse_file(const char *path, ul_config_opts *opts); #endif /* UL_CONFIG_H */ diff --git a/main.c b/main.c index de2d26f..461dc50 100644 --- a/main.c +++ b/main.c @@ -351,7 +351,10 @@ int main(int argc, char *argv[]) { ul_log(UL_LOG_LEVEL_VERBOSE, "unl0kr %s", UL_VERSION); /* Parse config files */ - ul_config_parse(cli_opts.config_files, cli_opts.num_config_files, &conf_opts); + ul_config_init_opts(&conf_opts); + ul_config_parse_file("/etc/unl0kr.conf", &conf_opts); + ul_config_parse_directory("/etc/unl0kr.conf.d", &conf_opts); + ul_config_parse_files(cli_opts.config_files, cli_opts.num_config_files, &conf_opts); /* Prepare current TTY and clean up on termination */ ul_terminal_prepare_current_terminal();