From a78d5bd9772681a232de56b3dd6acefee66cc71b Mon Sep 17 00:00:00 2001 From: Timo Sirainen Date: Sun, 26 Jul 2009 20:59:40 -0400 Subject: [PATCH] dovecot.conf: Added support for !include globs. Based on patch by Thomas Guthmann. --HG-- branch : HEAD --- configure.in | 4 +- dovecot-example.conf | 5 ++ src/config/config-parser.c | 106 ++++++++++++++++++++++++++--------- src/lib-settings/settings.c | 107 +++++++++++++++++++++++++++--------- 4 files changed, 170 insertions(+), 52 deletions(-) diff --git a/configure.in b/configure.in index fa1f048362..c36ee7627b 100644 --- a/configure.in +++ b/configure.in @@ -292,7 +292,7 @@ AC_CHECK_HEADERS(strings.h stdint.h unistd.h dirent.h malloc.h inttypes.h \ sys/quota.h sys/fs/ufs_quota.h ufs/ufs/quota.h jfs/quota.h sys/fs/quota_common.h \ mntent.h sys/mnttab.h sys/event.h sys/time.h sys/mkdev.h linux/dqblk_xfs.h \ xfs/xqm.h execinfo.h ucontext.h malloc_np.h sys/utsname.h sys/vmount.h \ - sys/utsname.h) + sys/utsname.h glob.h) dnl * gcc specific options if test "x$ac_cv_c_compiler_gnu" = "xyes"; then @@ -351,7 +351,7 @@ AC_CHECK_FUNCS(fcntl flock lockf inet_aton sigaction getpagesize madvise \ setrlimit setproctitle seteuid setreuid setegid setresgid \ strtoull strtoll strtouq strtoq \ setpriority quotactl getmntent kqueue kevent backtrace_symbols \ - walkcontext dirfd clearenv malloc_usable_size) + walkcontext dirfd clearenv malloc_usable_size glob) AC_CHECK_LIB(rt, clock_gettime, [ AC_DEFINE(HAVE_CLOCK_GETTIME,, Define if you have the clock_gettime function) diff --git a/dovecot-example.conf b/dovecot-example.conf index eaa1da7dc7..ff25253c38 100644 --- a/dovecot-example.conf +++ b/dovecot-example.conf @@ -1213,3 +1213,8 @@ plugin { # size and vsize are available only for expunge and copy events. #mail_log_fields = uid box msgid size } + +# Config files can also be included: +#!include = /etc/dovecot/conf.d/*.conf +# Optional configurations, don't give an error if it's not found: +#!include_try = /etc/dovecot/extra.conf diff --git a/src/config/config-parser.c b/src/config/config-parser.c index 2d92227b1a..d2cd8fa925 100644 --- a/src/config/config-parser.c +++ b/src/config/config-parser.c @@ -12,6 +12,9 @@ #include #include +#ifdef HAVE_GLOB_H +# include +#endif #define IS_WHITE(c) ((c) == ' ' || (c) == '\t') @@ -265,11 +268,84 @@ str_append_file(string_t *str, const char *key, const char *path, (void)close(fd); } +static int settings_add_include(struct parser_context *ctx, const char *path, + bool ignore_errors, const char **error_r) +{ + struct input_stack *tmp, *new_input; + int fd; + + for (tmp = ctx->cur_input; tmp != NULL; tmp = tmp->prev) { + if (strcmp(tmp->path, path) == 0) + break; + } + if (tmp != NULL) { + *error_r = t_strdup_printf("Recursive include file: %s", path); + return -1; + } + + if ((fd = open(path, O_RDONLY)) == -1) { + if (ignore_errors) + return 0; + + *error_r = t_strdup_printf("Couldn't open include file %s: %m", + path); + return -1; + } + + new_input = t_new(struct input_stack, 1); + new_input->prev = ctx->cur_input; + new_input->path = t_strdup(path); + new_input->input = i_stream_create_fd(fd, 2048, TRUE); + i_stream_set_return_partial_line(new_input->input, TRUE); + ctx->cur_input = new_input; + return 0; +} + +static int +settings_include(struct parser_context *ctx, const char *pattern, + bool ignore_errors, const char **error_r) +{ +#ifdef HAVE_GLOB + glob_t globbers; + unsigned int i; + + switch (glob(pattern, GLOB_BRACE, NULL, &globbers)) { + case 0: + break; + case GLOB_NOSPACE: + *error_r = "glob() failed: Not enough memory"; + return -1; + case GLOB_ABORTED: + *error_r = "glob() failed: Read error"; + return -1; + case GLOB_NOMATCH: + if (ignore_errors) + return 0; + *error_r = "No matches"; + return -1; + default: + *error_r = "glob() failed: Unknown error"; + return -1; + } + + /* iterate throuth the different files matching the globbing */ + for (i = 0; i < globbers.gl_pathc; i++) { + if (settings_add_include(ctx, globbers.gl_pathv[i], + ignore_errors, error_r) < 0) + return -1; + } + globfree(&globbers); + return 0; +#else + return settings_add_include(ctx, pattern, ignore_errors, error_r); +#endif +} + void config_parse_file(const char *path, bool expand_files) { enum settings_parser_flags parser_flags = SETTINGS_PARSER_FLAG_IGNORE_UNKNOWN_KEYS; - struct input_stack root, *new_input; + struct input_stack root; ARRAY_TYPE(const_string) auth_defaults; struct config_setting_parser_list *l, *const *parsers; struct parser_context ctx; @@ -314,7 +390,6 @@ void config_parse_file(const char *path, bool expand_files) str = t_str_new(256); full_line = t_str_new(512); errormsg = NULL; -newfile: ctx.cur_input->input = i_stream_create_fd(fd, (size_t)-1, TRUE); i_stream_set_return_partial_line(ctx.cur_input->input, TRUE); prevfile: @@ -378,29 +453,10 @@ prevfile: ret = 1; if (strcmp(key, "!include_try") == 0 || strcmp(key, "!include") == 0) { - struct input_stack *tmp; - const char *path; - - path = fix_relative_path(line, ctx.cur_input); - for (tmp = ctx.cur_input; tmp != NULL; tmp = tmp->prev) { - if (strcmp(tmp->path, path) == 0) - break; - } - if (tmp != NULL) { - errormsg = "Recursive include"; - } else if ((fd = open(path, O_RDONLY)) != -1) { - new_input = t_new(struct input_stack, 1); - new_input->prev = ctx.cur_input; - new_input->path = t_strdup(path); - ctx.cur_input = new_input; - goto newfile; - } else { - /* failed, but ignore failures with include_try. */ - if (strcmp(key, "!include") == 0) { - errormsg = t_strdup_printf( - "Couldn't open include file %s: %m", line); - } - } + if (settings_include(&ctx, fix_relative_path(line, ctx.cur_input), + strcmp(key, "!include_try") == 0, + &errormsg) == 0) + goto prevfile; } else if (*line == '=') { /* a) */ *line++ = '\0'; diff --git a/src/lib-settings/settings.c b/src/lib-settings/settings.c index 305f6be464..575b2c9e86 100644 --- a/src/lib-settings/settings.c +++ b/src/lib-settings/settings.c @@ -8,6 +8,9 @@ #include #include +#ifdef HAVE_GLOB_H +# include +#endif #define SECTION_ERRORMSG "%s (section changed in %s at line %d)" @@ -85,6 +88,79 @@ fix_relative_path(const char *path, struct input_stack *input) return t_strconcat(t_strdup_until(input->path, p+1), path, NULL); } +static int settings_add_include(const char *path, struct input_stack **inputp, + bool ignore_errors, const char **error_r) +{ + struct input_stack *tmp, *new_input; + int fd; + + for (tmp = *inputp; tmp != NULL; tmp = tmp->prev) { + if (strcmp(tmp->path, path) == 0) + break; + } + if (tmp != NULL) { + *error_r = t_strdup_printf("Recursive include file: %s", path); + return -1; + } + + if ((fd = open(path, O_RDONLY)) == -1) { + if (ignore_errors) + return 0; + + *error_r = t_strdup_printf("Couldn't open include file %s: %m", + path); + return -1; + } + + new_input = t_new(struct input_stack, 1); + new_input->prev = *inputp; + new_input->path = t_strdup(path); + new_input->input = i_stream_create_fd(fd, 2048, TRUE); + i_stream_set_return_partial_line(new_input->input, TRUE); + *inputp = new_input; + return 0; +} + +static int +settings_include(const char *pattern, struct input_stack **inputp, + bool ignore_errors, const char **error_r) +{ +#ifdef HAVE_GLOB + glob_t globbers; + unsigned int i; + + switch (glob(pattern, GLOB_BRACE, NULL, &globbers)) { + case 0: + break; + case GLOB_NOSPACE: + *error_r = "glob() failed: Not enough memory"; + return -1; + case GLOB_ABORTED: + *error_r = "glob() failed: Read error"; + return -1; + case GLOB_NOMATCH: + if (ignore_errors) + return 0; + *error_r = "No matches"; + return -1; + default: + *error_r = "glob() failed: Unknown error"; + return -1; + } + + /* iterate throuth the different files matching the globbing */ + for (i = 0; i < globbers.gl_pathc; i++) { + if (settings_add_include(globbers.gl_pathv[i], inputp, + ignore_errors, error_r) < 0) + return -1; + } + globfree(&globbers); + return 0; +#else + return settings_add_include(pattern, inputp, ignore_errors, error_r); +#endif +} + #define IS_WHITE(c) ((c) == ' ' || (c) == '\t') static bool @@ -93,7 +169,7 @@ settings_read_real(const char *path, const char *section, settings_section_callback_t *sect_callback, void *context) { /* pretty horrible code, but v2.0 will have this rewritten anyway.. */ - struct input_stack root, *input, *new_input; + struct input_stack root, *input; const char *errormsg, *next_section, *name, *last_section_path = NULL; char *line, *key, *p, quote; string_t *full_line; @@ -120,7 +196,6 @@ settings_read_real(const char *path, const char *section, full_line = t_str_new(512); sections = 0; root_section = 0; errormsg = NULL; -newfile: input->input = i_stream_create_fd(fd, 2048, TRUE); i_stream_set_return_partial_line(input->input, TRUE); prevfile: @@ -183,29 +258,11 @@ prevfile: if (strcmp(key, "!include_try") == 0 || strcmp(key, "!include") == 0) { - struct input_stack *tmp; - const char *path; - - path = fix_relative_path(line, input); - for (tmp = input; tmp != NULL; tmp = tmp->prev) { - if (strcmp(tmp->path, path) == 0) - break; - } - if (tmp != NULL) { - errormsg = "Recursive include"; - } else if ((fd = open(path, O_RDONLY)) != -1) { - new_input = t_new(struct input_stack, 1); - new_input->prev = input; - new_input->path = t_strdup(path); - input = new_input; - goto newfile; - } else { - /* failed, but ignore failures with include_try. */ - if (strcmp(key, "!include") == 0) { - errormsg = t_strdup_printf( - "Couldn't open include file %s: %m", line); - } - } + if (settings_include(fix_relative_path(line, input), + &input, + strcmp(key, "!include_try") == 0, + &errormsg) == 0) + goto prevfile; } else if (*line == '=') { /* a) */ *line++ = '\0'; -- 2.47.3