config_export_by_filter(ctx, &filter);
config_export_get_output(ctx, &output);
+ if (output.specific_services != NULL) {
+ const char *const *s;
+
+ for (s = output.specific_services; *s != NULL; s++) {
+ o_stream_send_str(conn->output,
+ t_strdup_printf("service=%s\t", *s));
+ }
+ }
if (output.service_uses_local)
o_stream_send_str(conn->output, "service-uses-local\t");
if (output.service_uses_remote)
return -config_filter_parser_cmp(p1, p2);
}
+static bool str_array_contains(ARRAY_TYPE(const_string) *arr, const char *str)
+{
+ const char *const *p;
+
+ array_foreach(arr, p) {
+ if (strcmp(*p, str) == 0)
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static bool have_changed_settings(const struct config_filter_parser *parser,
+ const char *module)
+{
+ const unsigned char *changes;
+ unsigned int i, j, size;
+
+ for (i = 0; parser->parsers[i].root != NULL; i++) {
+ if (*module != '\0' &&
+ !config_module_want_parser(module, parser->parsers[i].root))
+ continue;
+
+ changes = settings_parser_get_changes(parser->parsers[i].parser);
+ size = parser->parsers[i].root->struct_size;
+ for (j = 0; j < size; j++) {
+ if (changes[j] != 0)
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
static struct config_filter_parser *const *
-config_filter_find_all(struct config_filter_context *ctx,
+config_filter_find_all(struct config_filter_context *ctx, const char *module,
const struct config_filter *filter,
struct master_service_settings_output *output_r)
{
ARRAY_TYPE(config_filter_parsers) matches;
+ ARRAY_TYPE(const_string) service_names;
unsigned int i;
memset(output_r, 0, sizeof(*output_r));
t_array_init(&matches, 8);
+ t_array_init(&service_names, 8);
for (i = 0; ctx->parsers[i] != NULL; i++) {
const struct config_filter *mask = &ctx->parsers[i]->filter;
- if (!config_filter_match_service(mask, filter))
+ if (!config_filter_match_service(mask, filter)) {
+ if (!str_array_contains(&service_names, mask->service) &&
+ have_changed_settings(ctx->parsers[i], module))
+ array_append(&service_names, &mask->service, 1);
continue;
+ }
if (mask->local_bits > 0)
output_r->service_uses_local = TRUE;
array_append(&matches, &ctx->parsers[i], 1);
}
}
+ if (filter->service == NULL) {
+ (void)array_append_space(&service_names);
+ output_r->specific_services = array_idx(&service_names, 0);
+ }
+
array_sort(&matches, config_filter_parser_cmp);
(void)array_append_space(&matches);
return array_idx(&matches, 0);
}
int config_filter_parsers_get(struct config_filter_context *ctx, pool_t pool,
+ const char *module,
const struct config_filter *filter,
struct config_module_parser **parsers_r,
struct master_service_settings_output *output_r,
const char *error, **error_p;
unsigned int i, count;
- src = config_filter_find_all(ctx, filter, output_r);
+ src = config_filter_find_all(ctx, module, filter, output_r);
/* all of them should have the same number of parsers.
duplicate our initial parsers from the first match */
/* Build new parsers from all existing ones matching the given filter. */
int config_filter_parsers_get(struct config_filter_context *ctx, pool_t pool,
+ const char *module,
const struct config_filter *filter,
struct config_module_parser **parsers_r,
struct master_service_settings_output *output_r,
i_assert(count > 0 && parsers[count-1] == NULL);
count--;
for (i = 0; i < count && ret == 0; i++) {
- if (config_filter_parsers_get(new_filter, tmp_pool,
+ if (config_filter_parsers_get(new_filter, tmp_pool, "",
&parsers[i]->filter,
&tmp_parsers, &output,
error_r) < 0) {
*default_services = new_services;
}
}
+
+static bool parsers_are_connected(const struct setting_parser_info *root,
+ const struct setting_parser_info *info)
+{
+ const struct setting_parser_info *p;
+ const struct setting_parser_info *const *dep;
+
+ /* we're trying to find info or its parents from root's dependencies. */
+
+ for (p = info; p != NULL; p = p->parent) {
+ if (p == root)
+ return TRUE;
+ }
+
+ if (root->dependencies != NULL) {
+ for (dep = root->dependencies; *dep != NULL; dep++) {
+ if (parsers_are_connected(*dep, info))
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+bool config_module_want_parser(const char *module,
+ const struct setting_parser_info *root)
+{
+ struct config_module_parser *l;
+
+ if (strcmp(root->module_name, module) == 0)
+ return TRUE;
+ if (root == &master_service_setting_parser_info) {
+ /* everyone wants master service settings */
+ return TRUE;
+ }
+
+ for (l = config_module_parsers; l->root != NULL; l++) {
+ if (strcmp(l->root->module_name, module) != 0)
+ continue;
+
+ /* see if we can find a way to get from the original parser
+ to this parser */
+ if (parsers_are_connected(l->root, root))
+ return TRUE;
+ }
+ return FALSE;
+}
void config_parse_load_modules(void);
+bool config_module_want_parser(const char *module,
+ const struct setting_parser_info *root);
+
#endif
bool failed;
};
-static bool parsers_are_connected(const struct setting_parser_info *root,
- const struct setting_parser_info *info)
-{
- const struct setting_parser_info *p;
- const struct setting_parser_info *const *dep;
-
- /* we're trying to find info or its parents from root's dependencies. */
-
- for (p = info; p != NULL; p = p->parent) {
- if (p == root)
- return TRUE;
- }
-
- if (root->dependencies != NULL) {
- for (dep = root->dependencies; *dep != NULL; dep++) {
- if (parsers_are_connected(*dep, info))
- return TRUE;
- }
- }
- return FALSE;
-}
-
-static bool
-config_module_parser_is_in_service(const struct config_module_parser *list,
- const char *module)
-{
- struct config_module_parser *l;
-
- if (strcmp(list->root->module_name, module) == 0)
- return TRUE;
- if (list->root == &master_service_setting_parser_info) {
- /* everyone wants master service settings */
- return TRUE;
- }
-
- for (l = config_module_parsers; l->root != NULL; l++) {
- if (strcmp(l->root->module_name, module) != 0)
- continue;
-
- /* see if we can find a way to get from the original parser
- to this parser */
- if (parsers_are_connected(l->root, list->root))
- return TRUE;
- }
- return FALSE;
-}
-
bool config_export_type(string_t *str, const void *value,
const void *default_value,
enum setting_type type, bool dump_default,
struct config_export_context *ctx;
pool_t pool;
+ i_assert(module != NULL);
+
pool = pool_alloconly_create("config export", 1024*64);
ctx = p_new(pool, struct config_export_context, 1);
ctx->pool = pool;
{
const char *error;
- if (config_filter_parsers_get(config_filter, ctx->pool, filter,
+ if (config_filter_parsers_get(config_filter, ctx->pool,
+ ctx->module, filter,
&ctx->dup_parsers, &ctx->output,
&error) < 0) {
i_error("%s", error);
for (i = 0; ctx->parsers[i].root != NULL; i++) {
parser = &ctx->parsers[i];
if (*ctx->module != '\0' &&
- !config_module_parser_is_in_service(parser, ctx->module))
+ !config_module_want_parser(ctx->module, parser->root))
continue;
settings_export(ctx, parser->root, FALSE,
}
static int
-config_read_reply_header(struct istream *input, const char *path,
+config_read_reply_header(struct istream *istream, const char *path, pool_t pool,
+ const struct master_service_settings_input *input,
struct master_service_settings_output *output_r,
const char **error_r)
{
const char *line;
ssize_t ret;
- while ((ret = i_stream_read(input)) > 0) {
- line = i_stream_next_line(input);
+ while ((ret = i_stream_read(istream)) > 0) {
+ line = i_stream_next_line(istream);
if (line != NULL)
break;
}
if (ret <= 0) {
if (ret == 0)
return 1;
- *error_r = input->stream_errno != 0 ?
+ *error_r = istream->stream_errno != 0 ?
t_strdup_printf("read(%s) failed: %m", path) :
t_strdup_printf("read(%s) failed: EOF", path);
return -1;
T_BEGIN {
const char *const *arg = t_strsplit(line, "\t");
+ ARRAY_TYPE(const_string) services;
+ p_array_init(&services, pool, 8);
for (; *arg != NULL; arg++) {
if (strcmp(*arg, "service-uses-local") == 0)
output_r->service_uses_local = TRUE;
output_r->used_local = TRUE;
else if (strcmp(*arg, "used-remote") == 0)
output_r->used_remote = TRUE;
+ else if (strncmp(*arg, "service=", 8) == 0) {
+ const char *name = p_strdup(pool, *arg + 8);
+ array_append(&services, &name, 1);
+ }
+ }
+ if (input->service == NULL) {
+ (void)array_append_space(&services);
+ output_r->specific_services = array_idx(&services, 0);
}
} T_END;
return 0;
do {
alarm(timeout - now);
ret = config_read_reply_header(istream, path,
+ service->set_pool, input,
output_r, error_r);
if (ret == 0) {
ret = settings_parse_stream_read(parser,
};
struct master_service_settings_output {
- /* some settings for this service contain local/remote ip/host
- specific settings. */
+ /* if service was not given for lookup, this contains names of services
+ that have more specific settings */
+ const char *const *specific_services;
+
+ /* some settings for this service (or if service was not given,
+ all services) contain local/remote ip/host specific settings
+ (but this lookup didn't necessarily return any of them). */
unsigned int service_uses_local:1;
unsigned int service_uses_remote:1;
/* returned settings contain settings specific to given