summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDennis Camera <skonfig@dtnr.ch>2022-08-03 17:42:18 +0200
committerDennis Camera <skonfig@dtnr.ch>2022-08-03 17:58:19 +0200
commitfdccc8b948a4df3b6a2c3dcd6990c05bf8afdb27 (patch)
tree0f42c4d95649ba3e6a181659a0bd053604b499ec /src
parent26f555c974cf49def75120dada596f421e15f1d4 (diff)
downloadskonfig-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.c320
-rw-r--r--src/config.h128
-rw-r--r--src/skonfig.c51
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;
}