#include "config.h" #include "util/fs.h" #include "util/string.h" #include #include #include #include #include /******************************************************************************/ /* Type conversions */ const char *sk_archiving_mode_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; } } enum sk_archiving_mode sk_archiving_mode_of_string(const char *s) { for (enum sk_archiving_mode a = SK_ARCHIVING_MIN; a <= SK_ARCHIVING_MAX; ++a) { if (!strcasecmp(s, sk_archiving_mode_to_string(a))) return a; } return SK_ARCHIVING_NONE; } const char *sk_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; } } enum sk_colour_output sk_colour_output_of_string(const char *s) { if (!strcasecmp(s, "always")) { return SK_COLOUR_ALWAYS; } else if (!strcasecmp(s, "never")) { return SK_COLOUR_NEVER; } else if (!strcasecmp(s, "auto")) { return SK_COLOUR_AUTO; } else { return SK_COLOUR_NEVER; } } /******************************************************************************/ 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 = SK_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)); if (NULL != src->global.local_shell) dest->global.local_shell = strdup(src->global.local_shell); if (NULL != src->global.remote_shell) dest->global.remote_shell = strdup(src->global.remote_shell); if (NULL != src->global.cache_path_pattern) dest->global.cache_path_pattern = strdup(src->global.cache_path_pattern); if (NULL != src->global.init_manifest) dest->global.init_manifest = strdup(src->global.init_manifest); if (NULL != src->global.out_path) dest->global.out_path = strdup(src->global.out_path); if (NULL != src->global.remote_out_path) dest->global.remote_out_path = strdup(src->global.remote_out_path); if (NULL != src->global.remote_exec) dest->global.remote_exec = strdup(src->global.remote_exec); if (NULL != src->global.inventory_dir) 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--; size_t bufsz = ((uintptr_t)src->global.conf_dir[last] - (uintptr_t)src->global.conf_dir) + strlen(src->global.conf_dir[last]) * sizeof(char); 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, sk_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, sk_log_level_to_string(config->global.verbosity_level), sk_archiving_mode_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) sk_colour_output_of_string(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); }