diff --git a/include/libeconf.h b/include/libeconf.h index f85e12b..6fdaf9b 100644 --- a/include/libeconf.h +++ b/include/libeconf.h @@ -120,6 +120,9 @@ typedef enum econf_err econf_err; char**: econf_freeArray)) \ (value)) +#define DEFAULT_RUN_SUBDIR "/run" +#define DEFAULT_ETC_SUBDIR "/etc" + typedef struct econf_file econf_file; /** @brief Process the file of the given file_name and save its contents into key_file object. @@ -151,7 +154,7 @@ typedef struct econf_file econf_file; * will be joined to one single entry. */ extern econf_err econf_readFile(econf_file **result, const char *file_name, - const char *delim, const char *comment); + const char *delim, const char *comment); /** @brief Process the file of the given file_name and save its contents into key_file object. @@ -228,13 +231,206 @@ extern econf_err econf_readFileWithCallback(econf_file **result, const char *fil extern econf_err econf_mergeFiles(econf_file **merged_file, econf_file *usr_file, econf_file *etc_file); + +/** @brief Evaluating key/values of a given configuration by reading and merging all + * needed/available files from different directories. Order is: + * + * /etc/$project/$config_name.$config_suffix does exist: + * + * - /etc/$project/$config_name.$config_suffix + * - /run/$project/$config_name.$config_suffix.d/ *.$config_suffix + * - $usr_subdir/$project/$config_name.$config_suffix.d/ *.$config_suffix + * - /etc/$project/$config_name.$config_suffix.d/ *.$config_suffix + * + * /etc/$project/$config_name.$config_suffix does NOT exist: + * + * /run/$project/$config_name.$config_suffix does exist: + * + * - /run/$project/$config_name.$config_suffix + * - /run/$project/$config_name.$config_suffix.d/ *.$config_suffix + * - $usr_subdir/$project/$config_name.$config_suffix.d/ *.$config_suffix + * - /etc/$project/$config_name.$config_suffix.d/ *.$config_suffix + * + * /run/$project/$config_name.$config_suffix does NOT exist: + * + * - $usr_subdir/$project/$config_name.$config_suffix + * - /run/$project/$config_name.$config_suffix.d/ *.$config_suffix + * - $usr_subdir/$project/$config_name.$config_suffix.d/ *.$config_suffix + * - /etc/$project/$config_name.$config_suffix.d/ *.$config_suffix + * + * + * @param key_file content of parsed file(s) + * @param project name of the project used as subdirectory, can be NULL + * @param usr_subdir absolute path of the first directory (often "/usr/lib") + * @param config_name basename of the configuration file + * @param config_suffix suffix of the configuration file. Can also be NULL. + * @param delim delimiters of key/value e.g. "\t =" + * If delim contains space characters AND none space characters, + * multiline values are not parseable. + * @param comment array of characters which define the start of a comment + * @return econf_err ECONF_SUCCESS or error code + * + * Example: Reading content in different cases in following order: + * + * /etc/foo/example.conf does exist: + * + * - /etc/foo/example.conf + * - /run/foo/example.conf.d/ *.conf + * - /usr/lib/foo/example.conf.d/ *.conf + * - /etc/foo/example.conf.d/ *.conf + * + * /etc/foo/example.conf does NOT exist: + * + * /run/foo/example.conf does exist: + * + * - /run/foo/example.conf + * - /run/foo/example.conf.d/ *.conf + * - /usr/lib/foo/example.conf.d/ *.conf + * - /etc/foo/example.conf.d/ *.conf + * + * /run/foo/example.conf does NOT exist: + * + * - /usr/lib/foo/example.conf + * - /run/foo/example.conf.d/ *.conf + * - /usr/lib/foo/example.conf.d/ *.conf + * - /etc/foo/example.conf.d/ *.conf + * + * @code + * #include "libeconf.h" + * + * econf_file *key_file = NULL; + * econf_err error; + * + * error = econf_readConfig (&key_file, + * "foo", + * "/usr/lib", + * "example", + * "conf", + * "=", "#"); + * + * econf_free (key_file); + * @endcode + * + */ +extern econf_err econf_readConfig (econf_file **key_file, + const char *project, + const char *usr_subdir, + const char *config_name, + const char *config_suffix, + const char *delim, + const char *comment); + +/** @brief Evaluating key/values of a given configuration by reading and merging all + * needed/available files from different directories. Order is: + * + * /etc/$project/$config_name.$config_suffix does exist: + * + * - /etc/$project/$config_name.$config_suffix + * - /run/$project/$config_name.$config_suffix.d/ *.$config_suffix + * - $usr_subdir/$project/$config_name.$config_suffix.d/ *.$config_suffix + * - /etc/$project/$config_name.$config_suffix.d/ *.$config_suffix + * + * /etc/$project/$config_name.$config_suffix does NOT exist: + * + * /run/$project/$config_name.$config_suffix does exist: + * + * - /run/$project/$config_name.$config_suffix + * - /run/$project/$config_name.$config_suffix.d/ *.$config_suffix + * - $usr_subdir/$project/$config_name.$config_suffix.d/ *.$config_suffix + * - /etc/$project/$config_name.$config_suffix.d/ *.$config_suffix + * + * /run/$project/$config_name.$config_suffix does NOT exist: + * + * - $usr_subdir/$project/$config_name.$config_suffix + * - /run/$project/$config_name.$config_suffix.d/ *.$config_suffix + * - $usr_subdir/$project/$config_name.$config_suffix.d/ *.$config_suffix + * - /etc/$project/$config_name.$config_suffix.d/ *.$config_suffix + * + * For each parsed file the user defined function will be called in order + * e.g. to check the correct file permissions. + * + * @param key_file content of parsed file(s) + * @param project name of the project used as subdirectory, can be NULL + * @param usr_subdir absolute path of the first directory (often "/usr/lib") + * @param config_name basename of the configuration file + * @param config_suffix suffix of the configuration file. Can also be NULL. + * @param delim delimiters of key/value e.g. "\t =" + * If delim contains space characters AND none space characters, + * multiline values are not parseable. + * @param comment array of characters which define the start of a comment + * @param callback function which will be called for each file. This user defined function has the + * pathname as paramter and returns true if this file can be parsed. If not, the parsing of + * all files will be aborted and ECONF_PARSING_CALLBACK_FAILED will be returned. + * @param callback_data pointer which will be given to the callback function. + * @return econf_err ECONF_SUCCESS or error code + * + * Example: Reading content in different cases in following order: + * + * /etc/foo/example.conf does exist: + * + * - /etc/foo/example.conf + * - /run/foo/example.conf.d/ *.conf + * - /usr/lib/foo/example.conf.d/ *.conf + * - /etc/foo/example.conf.d/ *.conf + * + * /etc/foo/example.conf does NOT exist: + * + * /run/foo/example.conf does exist: + * + * - /run/foo/example.conf + * - /run/foo/example.conf.d/ *.conf + * - /usr/lib/foo/example.conf.d/ *.conf + * - /etc/foo/example.conf.d/ *.conf + * + * /run/foo/example.conf does NOT exist: + * + * - /usr/lib/foo/example.conf + * - /run/foo/example.conf.d/ *.conf + * - /usr/lib/foo/example.conf.d/ *.conf + * - /etc/foo/example.conf.d/ *.conf + * + * @code + * #include "libeconf.h" + * + * bool checkFile(const char *filename, const void *data) { + * - checking code which returns true or false - + * return true; + * } + * + * econf_file *key_file = NULL; + * econf_err error; + * + * error = econf_readConfigWithCallback (&key_file, + * "foo", + * "/usr/lib", + * "example", + * "conf", + * "=", "#", + * checkFile, + * NULL); + * + * econf_free (key_file); + * @endcode + * + */ +extern econf_err econf_readConfigWithCallback(econf_file **key_file, + const char *project, + const char *usr_subdir, + const char *config_name, + const char *config_suffix, + const char *delim, + const char *comment, + bool (*callback)(const char *filename, const void *data), + const void *callback_data); + + /** @brief Evaluating key/values of a given configuration by reading and merging all * needed/available files in two different directories (normally in /usr/etc and /etc). * * @param key_file content of parsed file(s) * @param usr_conf_dir absolute path of the first directory (normally "/usr/etc") * @param etc_conf_dir absolute path of the second directory (normally "/etc") - * @param project_name basename of the configuration file + * @param config_name basename of the configuration file * @param config_suffix suffix of the configuration file. Can also be NULL. * @param delim delimiters of key/value e.g. "\t =" * If delim contains space characters AND none space characters, @@ -263,7 +459,7 @@ extern econf_err econf_mergeFiles(econf_file **merged_file, extern econf_err econf_readDirs(econf_file **key_file, const char *usr_conf_dir, const char *etc_conf_dir, - const char *project_name, + const char *config_name, const char *config_suffix, const char *delim, const char *comment); @@ -277,7 +473,7 @@ extern econf_err econf_readDirs(econf_file **key_file, * @param size Size of the evaluated key_files list. * @param usr_conf_dir absolute path of the first directory (normally "/usr/etc") * @param etc_conf_dir absolute path of the second directory (normally "/etc") - * @param project_name basename of the configuration file + * @param config_name basename of the configuration file * @param config_suffix suffix of the configuration file. Can also be NULL. * @param delim delimiters of key/value e.g. "\t =" * If delim contains space characters AND none space characters, @@ -317,7 +513,7 @@ extern econf_err econf_readDirs(econf_file **key_file, extern econf_err econf_readDirsWithCallback(econf_file **key_file, const char *usr_conf_dir, const char *etc_conf_dir, - const char *project_name, + const char *config_name, const char *config_suffix, const char *delim, const char *comment, @@ -333,7 +529,7 @@ extern econf_err econf_readDirs(econf_file **key_file, * @param size Size of the evaluated key_files list. * @param usr_conf_dir absolute path of the first directory (normally "/usr/etc") * @param etc_conf_dir absolute path of the second directory (normally "/etc") - * @param project_name basename of the configuration file + * @param config_name basename of the configuration file * @param config_suffix suffix of the configuration file. Can also be NULL. * @param delim delimiters of key/value e.g. "\t =" * If delim contains space characters AND none space characters, @@ -346,7 +542,7 @@ extern econf_err econf_readDirsHistory(econf_file ***key_files, size_t *size, const char *usr_conf_dir, const char *etc_conf_dir, - const char *project_name, + const char *config_name, const char *config_suffix, const char *delim, const char *comment); @@ -361,7 +557,7 @@ extern econf_err econf_readDirsHistory(econf_file ***key_files, * @param size Size of the evaluated key_files list. * @param usr_conf_dir absolute path of the first directory (normally "/usr/etc") * @param etc_conf_dir absolute path of the second directory (normally "/etc") - * @param project_name basename of the configuration file + * @param config_name basename of the configuration file * @param config_suffix suffix of the configuration file. Can also be NULL. * @param delim delimiters of key/value e.g. "\t =" * If delim contains space characters AND none space characters, @@ -378,7 +574,7 @@ extern econf_err econf_readDirsHistoryWithCallback(econf_file ***key_files, size_t *size, const char *usr_conf_dir, const char *etc_conf_dir, - const char *project_name, + const char *config_name, const char *config_suffix, const char *delim, const char *comment, @@ -880,11 +1076,11 @@ econf_reset_security_settings(void); * @param dir_postfix_list list of directory structures. * E.G. with the given list: {"/conf.d/", ".d/", "/", NULL} files in following * directories will be parsed: - * "/..d/" - * "//conf.d/" - * "/.d/" - * "//" - * The entry "/..d/" will be added + * "/..d/" + * "//conf.d/" + * "/.d/" + * "//" + * The entry "/..d/" will be added * automatically. * * @return econf_err ECONF_SUCCESS or error code diff --git a/lib/libeconf.c b/lib/libeconf.c index ece4187..fe69447 100644 --- a/lib/libeconf.c +++ b/lib/libeconf.c @@ -271,26 +271,26 @@ econf_err econf_mergeFiles(econf_file **merged_file, econf_file *usr_file, econf return ECONF_SUCCESS; } - -econf_err econf_readDirsHistoryWithCallback(econf_file ***key_files, - size_t *size, - const char *dist_conf_dir, - const char *etc_conf_dir, - const char *project_name, - const char *config_suffix, - const char *delim, - const char *comment, - bool (*callback)(const char *filename, const void *data), - const void *callback_data) +econf_err readConfigHistoryWithCallback(econf_file ***key_files, + size_t *size, + const char *dist_conf_dir, + const char *run_conf_dir, + const char *etc_conf_dir, + const char *config_name, + const char *config_suffix, + const char *delim, + const char *comment, + bool (*callback)(const char *filename, const void *data), + const void *callback_data) { - const char *suffix, *default_dirs[3] = {NULL, NULL, NULL}; + const char *suffix, *default_dirs[4] = {NULL, NULL, NULL, NULL}; char *distfile, *etcfile, *cp; econf_file *key_file; econf_err error; *size = 0; - if (project_name == NULL || strlen (project_name) == 0 || delim == NULL) + if (config_name == NULL || strlen (config_name) == 0 || delim == NULL) return ECONF_ERROR; if (config_suffix != NULL && strlen (config_suffix) > 0) @@ -312,12 +312,12 @@ econf_err econf_readDirsHistoryWithCallback(econf_file ***key_files, /* create file names for etc and distribution config */ if (dist_conf_dir != NULL) { - distfile = alloca(strlen (dist_conf_dir) + strlen (project_name) + + distfile = alloca(strlen (dist_conf_dir) + strlen (config_name) + strlen (suffix) + 2); cp = stpcpy (distfile, dist_conf_dir); *cp++ = '/'; - cp = stpcpy (cp, project_name); + cp = stpcpy (cp, config_name); stpcpy (cp, suffix); } else @@ -325,12 +325,12 @@ econf_err econf_readDirsHistoryWithCallback(econf_file ***key_files, if (etc_conf_dir != NULL) { - etcfile = alloca(strlen (etc_conf_dir) + strlen (project_name) + + etcfile = alloca(strlen (etc_conf_dir) + strlen (config_name) + strlen (suffix) + 2); cp = stpcpy (etcfile, etc_conf_dir); *cp++ = '/'; - cp = stpcpy (cp, project_name); + cp = stpcpy (cp, config_name); stpcpy (cp, suffix); } else @@ -345,10 +345,10 @@ econf_err econf_readDirsHistoryWithCallback(econf_file ***key_files, } if (etcfile && !error) { - /* /etc/. does exist, ignore /usr/etc/. */ + /* /etc/. does exist, ignore /usr/etc/. */ *size = 1; } else { - /* /etc/. does not exist, so read /usr/etc */ + /* /etc/. does not exist, so read /usr/etc */ if (distfile) { error = econf_readFileWithCallback(&key_file, distfile, delim, comment, @@ -357,7 +357,7 @@ econf_err econf_readDirsHistoryWithCallback(econf_file ***key_files, return error; } - if (distfile && !error) /* /usr/etc/. does exist */ + if (distfile && !error) /* /usr/etc/. does exist */ *size = 1; } @@ -379,12 +379,12 @@ econf_err econf_readDirsHistoryWithCallback(econf_file ***key_files, /* Indicate which directories to look for. The order is: - "default_dirs/project_name.suffix.d/" + "default_dirs/config_name.suffix.d/" AND all other directories which has been set by econf_set_conf_dirs. E.G.: - "default_dirs/project_name/conf.d/" - "default_dirs/project_name.d/" - "default_dirs/project_name/" + "default_dirs/config_name/conf.d/" + "default_dirs/config_name.d/" + "default_dirs/config_name/" */ char *suffix_d = malloc (strlen(suffix) + 4); /* + strlen(".d/") */ if (suffix_d == NULL) @@ -408,9 +408,14 @@ econf_err econf_readDirsHistoryWithCallback(econf_file ***key_files, int i = 0; /* merge all files in e.g. /usr/etc and /etc */ default_dirs[0] = dist_conf_dir; - default_dirs[1] = etc_conf_dir; + if (run_conf_dir == NULL ) { + default_dirs[1] = etc_conf_dir; + } else { + default_dirs[1] = run_conf_dir; + default_dirs[2] = etc_conf_dir; + } while (default_dirs[i]) { - char *project_path = combine_strings(default_dirs[i], project_name, '/'); + char *project_path = combine_strings(default_dirs[i], config_name, '/'); error = traverse_conf_dirs(key_files, configure_dirs, size, project_path, suffix, delim, comment); free(project_path); @@ -439,64 +444,164 @@ econf_err econf_readDirsHistoryWithCallback(econf_file ***key_files, return ECONF_SUCCESS; } + +econf_err readConfigWithCallback(econf_file **result, + const char *dist_conf_dir, + const char *run_conf_dir, + const char *etc_conf_dir, + const char *config_name, + const char *config_suffix, + const char *delim, + const char *comment, + bool (*callback)(const char *filename, const void *data), + const void *callback_data) +{ + size_t size = 0; + econf_file **key_files; + econf_err error; + + error = readConfigHistoryWithCallback(&key_files, + &size, + dist_conf_dir, + run_conf_dir, + etc_conf_dir, + config_name, + config_suffix, + delim, + comment, + callback, + callback_data); + if (error != ECONF_SUCCESS) + return error; + + // Merge the list of acquired key_files into merged_file + error = merge_econf_files(key_files, result); + free(key_files); + + return error; +} + + +extern econf_err econf_readConfigWithCallback(econf_file **key_file, + const char *project, + const char *usr_subdir, + const char *config_name, + const char *config_suffix, + const char *delim, + const char *comment, + bool (*callback)(const char *filename, const void *data), + const void *callback_data) +{ + char *run_dir = DEFAULT_RUN_SUBDIR; + char *etc_dir = DEFAULT_ETC_SUBDIR; + if (project != NULL) { + if (asprintf(&run_dir, "%s/%s", DEFAULT_RUN_SUBDIR, project) < 0) { + return ECONF_NOMEM; + } + if (asprintf(&etc_dir, "%s/%s", DEFAULT_ETC_SUBDIR, project) < 0) { + free(run_dir); + return ECONF_NOMEM; + } + } + + econf_err ret = readConfigWithCallback(key_file, + usr_sbubdir, + run_dir, + etc_dir, + config_name, + config_suffix, + delim, + comment, + callback, + callback_data); + free(run_dir); + free(etc_dir); + return ret; +} + + +extern econf_err econf_readConfig (econf_file **key_file, + const char *project, + const char *usr_subdir, + const char *config_name, + const char *config_suffix, + const char *delim, + const char *comment) +{ + return econf_readConfigWithCallback(key_file, + project, + usr_subdir, + config_name, + config_suffix, + delim, + comment, + NULL, + NULL); +} + +econf_err econf_readDirsHistoryWithCallback(econf_file ***key_files, + size_t *size, + const char *dist_conf_dir, + const char *etc_conf_dir, + const char *config_name, + const char *config_suffix, + const char *delim, + const char *comment, + bool (*callback)(const char *filename, const void *data), + const void *callback_data) +{ + return readConfigHistoryWithCallback(key_files, + size, + dist_conf_dir, + NULL, + etc_conf_dir, + config_name, + config_suffix, + delim, + comment, + callback, + callback_data); +} + econf_err econf_readDirsHistory(econf_file ***key_files, size_t *size, const char *dist_conf_dir, const char *etc_conf_dir, - const char *project_name, + const char *config_name, const char *config_suffix, const char *delim, const char *comment) { - return econf_readDirsHistoryWithCallback(key_files, size, dist_conf_dir, - etc_conf_dir, project_name, - config_suffix, delim, comment, NULL, NULL); + return readConfigHistoryWithCallback(key_files, size, dist_conf_dir, NULL, + etc_conf_dir, config_name, + config_suffix, delim, comment, NULL, NULL); } econf_err econf_readDirsWithCallback(econf_file **result, const char *dist_conf_dir, const char *etc_conf_dir, - const char *project_name, + const char *config_name, const char *config_suffix, const char *delim, const char *comment, bool (*callback)(const char *filename, const void *data), const void *callback_data) { - size_t size = 0; - econf_file **key_files; - econf_err error; - - error = econf_readDirsHistoryWithCallback(&key_files, - &size, - dist_conf_dir, - etc_conf_dir, - project_name, - config_suffix, - delim, - comment, - callback, - callback_data); - if (error != ECONF_SUCCESS) - return error; - - // Merge the list of acquired key_files into merged_file - error = merge_econf_files(key_files, result); - free(key_files); - - return error; + return readConfigWithCallback(result, dist_conf_dir, NULL, + etc_conf_dir, config_name, + config_suffix, delim, comment, callback, callback_data); } econf_err econf_readDirs(econf_file **result, const char *dist_conf_dir, const char *etc_conf_dir, - const char *project_name, + const char *config_name, const char *config_suffix, const char *delim, const char *comment) { - return econf_readDirsWithCallback(result, dist_conf_dir, etc_conf_dir, - project_name, config_suffix, delim, - comment, NULL, NULL); + return readConfigWithCallback(result, dist_conf_dir, NULL, + etc_conf_dir, config_name, + config_suffix, delim, comment, NULL, NULL); } // Write content of a econf_file struct to specified location