diff options
| author | Dennis Camera <skonfig@dtnr.ch> | 2022-08-03 17:42:18 +0200 |
|---|---|---|
| committer | Dennis Camera <skonfig@dtnr.ch> | 2022-08-03 17:58:19 +0200 |
| commit | fdccc8b948a4df3b6a2c3dcd6990c05bf8afdb27 (patch) | |
| tree | 0f42c4d95649ba3e6a181659a0bd053604b499ec /src | |
| parent | 26f555c974cf49def75120dada596f421e15f1d4 (diff) | |
| download | skonfig-c-fdccc8b948a4df3b6a2c3dcd6990c05bf8afdb27.tar.gz skonfig-c-fdccc8b948a4df3b6a2c3dcd6990c05bf8afdb27.zip | |
[src/config.c] Add config module (does not parse INI files, yet)
Diffstat (limited to 'src')
| -rw-r--r-- | src/config.c | 320 | ||||
| -rw-r--r-- | src/config.h | 128 | ||||
| -rw-r--r-- | src/skonfig.c | 51 |
3 files changed, 499 insertions, 0 deletions
diff --git a/src/config.c b/src/config.c new file mode 100644 index 0000000..1d369c4 --- /dev/null +++ b/src/config.c @@ -0,0 +1,320 @@ +#include "config.h" + +#include "util/fs.h" +#include "util/string.h" + +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + + +/******************************************************************************/ +/* Type conversions */ + +inline const char *archiving_to_string(enum sk_archiving_mode a) { + switch (a) { + case SK_ARCHIVING_NONE: + return "none"; + case SK_ARCHIVING_TAR: + return "tar"; + case SK_ARCHIVING_TGZ: + return "tgz"; + case SK_ARCHIVING_TBZ2: + return "tbz2"; + case SK_ARCHIVING_TXZ: + return "txz"; + default: + return NULL; + } +} + +inline enum sk_colour_output string_to_colour_output(const char *in) { + if (!strcmp(in, "always")) { + return SK_COLOUR_ALWAYS; + } else if (!strcmp(in, "never")) { + return SK_COLOUR_NEVER; + } else if (!strcmp(in, "auto")) { + return SK_COLOUR_AUTO; + } else { + return SK_COLOUR_NEVER; + } +} + +inline const char *colour_output_to_string(enum sk_colour_output c) { + switch (c) { + case SK_COLOUR_NEVER: + return "never"; + case SK_COLOUR_ALWAYS: + return "always"; + case SK_COLOUR_AUTO: + return "auto"; + default: + return NULL; + } +} + +inline const char *verbosity_to_string(enum sk_log_verb v) { + switch (v) { + case LOG_OFF: + return "off"; + case LOG_ERROR: + return "error"; + case LOG_WARNING: + return "warning"; + case LOG_INFO: + return "info"; + case LOG_VERBOSE: + return "verbose"; + case LOG_DEBUG: + return "debug"; + case LOG_TRACE: + return "trace"; + default: + return NULL; + } +} + + +/******************************************************************************/ + +static struct sk_config sk_config_defaults = { + .global = { + .local_shell = "/bin/sh", + .remote_shell = "/bin/sh", + .cache_path_pattern = "%h", + .conf_dir = NULL, + .coloured_output = SK_COLOUR_AUTO, + .init_manifest = "manifest/init", + .out_path = NULL, /* NULL is mkdtemp() */ + .remote_out_path = "/var/lib/skonfig", + .remote_exec = "ssh -o User=root", + .inventory_dir = "manifest/inventory", + .jobs = 1, + .verbosity_level = LOG_INFO, + .archiving = SK_ARCHIVING_NONE, + .save_output_streams = true, + .log_timestamp = false + } +}; + +struct sk_config *sk_config_alloc() { + struct sk_config *new = malloc(sizeof(struct sk_config)); + + if (new != NULL) { + sk_config_cpy(new, &sk_config_defaults); + } + + return new; +} + +void sk_config_cpy(struct sk_config *restrict dest, const struct sk_config *restrict src) { + memcpy(dest, src, sizeof(struct sk_config)); + dest->global.local_shell = strdup(src->global.local_shell); + dest->global.remote_shell = strdup(src->global.remote_shell); + dest->global.cache_path_pattern = strdup(src->global.cache_path_pattern); + dest->global.init_manifest = strdup(src->global.init_manifest); + dest->global.out_path = strdup(src->global.out_path); + dest->global.remote_out_path = strdup(src->global.remote_out_path); + dest->global.remote_exec = strdup(src->global.remote_exec); + dest->global.inventory_dir = strdup(src->global.inventory_dir); + + if (src->global.conf_dir) { + size_t last = 0; + while (src->global.conf_dir[last] != NULL) last++; + last--; + char *end = src->global.conf_dir[last] + strlen(src->global.conf_dir[last]); + size_t bufsz = (void *)end - (void *)src->global.conf_dir; + void *dbuf = malloc(bufsz); + memcpy(dbuf, src->global.conf_dir, bufsz); + + dest->global.conf_dir = (searchpath_t)dbuf; + } +} + +void sk_config_print(struct sk_config *config, FILE *outstream) { + char *conf_dir_str = strajoin(',', (const char **)config->global.conf_dir); + + fprintf( + outstream, + "sk_config {\n" + " global {\n" + " local_shell = \"%s\"\n" + " remote_shell = \"%s\"\n" + " cache_path_pattern = \"%s\"\n" + " conf_dir = {%s}\n" + " coloured_output = %s\n" + " init_manifest = \"%s\"\n" + " out_path = %s\n" + " remote_out_path = \"%s\"\n" + " remote_exec = \"%s\"\n" + " inventory_dir = \"%s\"\n" + " jobs = %u\n" + " verbosity_level = %s\n" + " archiving = %s\n" + " save_output_streams = %s\n" + " log_timestamp = %s\n" + " }\n" + "}\n", + config->global.local_shell, + config->global.remote_shell, + config->global.cache_path_pattern, + conf_dir_str, + colour_output_to_string(config->global.coloured_output), + config->global.init_manifest, + config->global.out_path, + config->global.remote_out_path, + config->global.remote_exec, + config->global.inventory_dir, + config->global.jobs, + verbosity_to_string(config->global.verbosity_level), + archiving_to_string(config->global.archiving), + boolstr(config->global.save_output_streams), + boolstr(config->global.log_timestamp) + ); + + free(conf_dir_str); +} + +const char *sk_config_find() { + static const char *conffilename = "config"; + const char *confdir; + char *conffile; + + /* first, try project config file */ + if ((confdir = myhome())) { + conffile = pathjoin(confdir, ".skonfig", conffilename); + } else { + goto user; + } + +#if DEBUG + printf("checking %s\n", conffile); +#endif + if (!access(conffile, R_OK)) { + return conffile; + } + free(conffile); + + user: + /* then, try user config file */ + + if ((confdir = getenv("XDG_CONFIG_HOME"))) { + conffile = pathjoin(confdir, "skonfig", conffilename); + } else if ((confdir = myhome())) { + conffile = pathjoin(confdir, ".config", "skonfig", conffilename); + } else { + goto global; + } + +#if DEBUG + printf("checking %s\n", conffile); +#endif + if (!access(conffile, R_OK)) { + return conffile; + } + free(conffile); + + global: + /* finally, try global config file */ + +#ifdef SYSCONFDIR + confdir = SYSCONFDIR; +#else + /* fallback */ + confdir = stringize(DIRSEP) "etc"; +#endif + + conffile = pathjoin(confdir, conffilename); + +#if DEBUG + printf("checking %s\n", conffile); +#endif + if (!access(conffile, R_OK)) { + return conffile; + } + + free(conffile); + + /* no config found */ + return NULL; +} + +/******************************************************************************/ +/* Reading config */ + +#define _str_conf(s) strdup(s) + +#define _command_conf(c) _str_conf(c) + +inline dir_t _dir_conf(const char *in) { + /* FIXME: handle relative paths */ + if (is_dir(in)) { + return (dir_t)strdup(in); + } else { + return NULL; + } +} + +inline searchpath_t _searchpath_conf(const char *in) { + size_t bufsz = strsplit(in, stringize(PATHSEP), NULL); + void *buf = malloc(bufsz); + strsplit(in, stringize(PATHSEP), buf); + + return (searchpath_t)buf; +} + +#define _colour_conf(c) string_to_colour_output(c) + +/* if the envonment variable is set, always disable coloured output, not matter + * its value */ +#define _nocolor_conf(c) SK_COLOUR_NEVER + +int sk_config_parse_file(struct sk_config *config, FILE *fh) { + /* TODO: Implement */ + + /* char buf[BUFSIZ]; */ + /* (void)fread(buf, sizeof(char), BUFSIZ, fh); */ + + return 1; +} + + +#define _sk_config_parse_env_map(config, envvar, tf_func, confopt) \ + { \ + char *envval = getenv(envvar); \ + if (envval) { \ + config->confopt = tf_func(envval); \ + } \ + } + +void sk_config_parse_env(struct sk_config *config) { + _sk_config_parse_env_map( + config, "SK_PATH", _searchpath_conf, global.conf_dir); + _sk_config_parse_env_map( + config, "SK_LOCAL_SHELL", _command_conf, global.local_shell); + _sk_config_parse_env_map( + config, "SK_REMOTE_SHELL", _command_conf, global.remote_shell); + _sk_config_parse_env_map( + config, "SK_REMOTE_EXEC", _command_conf, global.remote_exec); + _sk_config_parse_env_map( + config, "SK_INVENTORY_DIR", _dir_conf, global.inventory_dir); + _sk_config_parse_env_map( + config, "SK_CACHE_PATH_PATTERN", _str_conf, global.cache_path_pattern); + _sk_config_parse_env_map( + config, "SK_COLORED_OUTPUT", _colour_conf, global.coloured_output); + _sk_config_parse_env_map( + config, "NOCOLOR", _nocolor_conf, global.coloured_output); +} + +void sk_config_free(struct sk_config *config) { + free(config->global.local_shell); + free(config->global.remote_shell); + free(config->global.cache_path_pattern); + free(config->global.conf_dir); + free(config->global.init_manifest); + free(config->global.out_path); + free(config->global.remote_out_path); + free(config->global.remote_exec); + free(config->global.inventory_dir); + free(config); +} diff --git a/src/config.h b/src/config.h new file mode 100644 index 0000000..bfd7029 --- /dev/null +++ b/src/config.h @@ -0,0 +1,128 @@ +#ifndef _SK_CONFIG_H +#define _SK_CONFIG_H + +#include "../config.h" + +#include <stdio.h> + +#if HAVE_STDBOOL_H +#include <stdbool.h> +#endif + +typedef char * command_t; +typedef char * path_t; +typedef char * dir_t; +typedef char ** searchpath_t; + +enum sk_log_verb { + LOG_OFF = 0, + LOG_ERROR, + LOG_WARNING, + LOG_INFO, + LOG_VERBOSE, + LOG_DEBUG, + LOG_TRACE, + + SK_LOG_MIN = LOG_OFF, + SK_LOG_MAX = LOG_TRACE +}; + +enum sk_colour_output { + SK_COLOUR_NEVER = 0, + SK_COLOUR_ALWAYS, + SK_COLOUR_AUTO +}; + +enum sk_archiving_mode { + SK_ARCHIVING_NONE = 0, + SK_ARCHIVING_TAR, + SK_ARCHIVING_TGZ, + SK_ARCHIVING_TBZ2, + SK_ARCHIVING_TXZ +}; + +struct sk_config_global { + command_t local_shell; + command_t remote_shell; + char *cache_path_pattern; + searchpath_t conf_dir; + enum sk_colour_output coloured_output; + path_t init_manifest; + path_t out_path; + command_t remote_out_path; + command_t remote_exec; + dir_t inventory_dir; + unsigned int jobs; + enum sk_log_verb verbosity_level; + enum sk_archiving_mode archiving; + bool save_output_streams; + bool log_timestamp; +}; + +struct sk_config { + struct sk_config_global global; +}; + +/* functions */ + +/** + * sk_config_alloc: + * Allocates a new sk_config struct filled with defaults. + * + * @return pointer to a new sk_config struct + * (or NULL if it ran out of memory.) + */ +struct sk_config *sk_config_alloc(); + +/** + * sk_config_print: + * Prints a sk_config struct. + * + * @param config: the sk_config struct to print + * @param outstream: the stream to print to + */ +void sk_config_print(struct sk_config *config, FILE *outstream); + +/** + * sk_config_find: + * Finds the .skonfig/config file to use. + * + * The caller must free() the returned pointer. + * + * @return path to the .skonfig/config file to use, + * or NULL if none was found or an error occurred. + */ +const char *sk_config_find(); + +/** + * sk_config_cpy: + * Make a deep copy of the config struct. + * + * i.e. pointer values will be copied. + * + * @param dest: pointer to a newly allocated config struct + * @param src: pointer to the struct to copy to dest + */ +void sk_config_cpy(struct sk_config *restrict dest, const struct sk_config *restrict src); + +int sk_config_parse_file(struct sk_config *config, FILE *fh); + +/** + * sk_config_parse_env: + * Update an sk_config struct from environment variables. + * + * @param config: the struct to update + */ +void sk_config_parse_env(struct sk_config *config); + +/** + * sk_config_free: + * Frees an sk_config struct allocated using sk_config_alloc. + * + * Pointer type struct fields will also be freed! + * + * @param config: the struct to free + */ +void sk_config_free(struct sk_config *); + +#endif diff --git a/src/skonfig.c b/src/skonfig.c index 08169fd..1b97adf 100644 --- a/src/skonfig.c +++ b/src/skonfig.c @@ -160,6 +160,39 @@ static int sk_get_global_opts( return true; } +/* config */ + +void sk_init_config(struct sk_config *config, const char *config_file) { + FILE *fh; + + if (config_file) { + /* Parse config file (as given by option) */ + printf("processing config file (opt): %s\n", config_file); + + fh = fopen(config_file, "r"); + if (fh) { + sk_config_parse_file(config, fh); + fclose(fh); + } else { + fprintf(stderr, "invalid config file: %s\n", config_file); + exit(1); + } + } else if ((config_file = sk_config_find())) { + /* found a config file in default location */ + printf("processing config file (found): %s\n", config_file); + + fh = fopen(config_file, "r"); + sk_config_parse_file(config, fh); + fclose(fh); + + free((void *)config_file); + } + + sk_config_parse_env(config); +} + + + int main(int argc, char *argv[]) { EXECNAME = argv[0]; @@ -193,8 +226,26 @@ int main(int argc, char *argv[]) { return EXIT_FAILURE; } + /* Read in skonfig config */ + struct sk_config *config = sk_config_alloc(); + sk_init_config(config, NULL); + + /* update config from command line arguments */ + config->global.init_manifest = strdup(options.init_manifest); + config->global.jobs = options.jobs; + + config->global.verbosity_level += options.verbosity; + if (config->global.verbosity_level > SK_LOG_MAX) + config->global.verbosity_level = SK_LOG_MAX; + +#if DEBUG + sk_config_print(config, stdout); /* DEBUG */ +#endif + printf("conf dir: %s\n", options.conf_root); printf("target host: %s\n", options.target_host); + sk_config_free(config); + return EXIT_SUCCESS; } |
