/src/dovecot/src/lib-settings/settings.c
Line | Count | Source |
1 | | /* Copyright (c) Dovecot authors, see top-level COPYING file */ |
2 | | |
3 | | #include "lib.h" |
4 | | #include "array.h" |
5 | | #include "hash.h" |
6 | | #include "llist.h" |
7 | | #include "str.h" |
8 | | #include "strescape.h" |
9 | | #include "event-filter-private.h" |
10 | | #include "wildcard-match.h" |
11 | | #include "mmap-util.h" |
12 | | #include "dns-util.h" |
13 | | #include "settings.h" |
14 | | #include "var-expand.h" |
15 | | |
16 | | #include <ctype.h> |
17 | | #include <sys/stat.h> |
18 | | |
19 | | enum set_seen_type { |
20 | | /* Setting has not been changed */ |
21 | | SET_SEEN_NO, |
22 | | /* Setting has been changed */ |
23 | | SET_SEEN_YES, |
24 | | /* Setting has been changed with SETTINGS_OVERRIDE_TYPE_2ND_DEFAULT. |
25 | | If SETTINGS_OVERRIDE_TYPE_DEFAULT override is still found, use that |
26 | | instead. */ |
27 | | SET_SEEN_DEFAULT, |
28 | | }; |
29 | | |
30 | | struct settings_mmap_pool { |
31 | | struct pool pool; |
32 | | int refcount; |
33 | | |
34 | | struct settings_mmap_pool *prev, *next; |
35 | | |
36 | | const char *source_filename; |
37 | | unsigned int source_linenum; |
38 | | |
39 | | pool_t parent_pool; |
40 | | struct settings_mmap *mmap; /* NULL for unit tests */ |
41 | | struct settings_root *root; |
42 | | }; |
43 | | |
44 | | struct settings_override { |
45 | | pool_t pool; |
46 | | ARRAY_TYPE(settings_override) *overrides_array; |
47 | | enum settings_override_type type; |
48 | | |
49 | | /* Number of named list filter elements in this override */ |
50 | | unsigned int filter_array_element_count; |
51 | | /* Number of named (non-list) filter elements in this override */ |
52 | | unsigned int filter_element_count; |
53 | | /* key += value is used, i.e. append this value to existing value */ |
54 | | bool append; |
55 | | bool array_default_added; |
56 | | /* Original key for the overridden setting, e.g. |
57 | | namespace/inbox/mailbox/Sent/mail_attribute/dict_driver */ |
58 | | const char *orig_key; |
59 | | /* Final key for the overridden setting, e.g. dict_driver for the above |
60 | | orig_key example. */ |
61 | | const char *key; |
62 | | /* Value for the overridden setting */ |
63 | | const char *value; |
64 | | |
65 | | /* Event filter being generated from the key as it's being processed. */ |
66 | | struct event_filter *filter; |
67 | | /* Last filter element's key, and for list filters the value. For |
68 | | example: |
69 | | - namespace/inbox/key: "namespace", "inbox" |
70 | | - mail_attribute/key: "mail_attribute", NULL |
71 | | */ |
72 | | const char *last_filter_key, *last_filter_value; |
73 | | |
74 | | /* NULL-terminated list of block names (setting_parser_info names) |
75 | | which contain this setting key. */ |
76 | | const char *const *block_names; |
77 | | }; |
78 | | ARRAY_DEFINE_TYPE(settings_override, struct settings_override); |
79 | | |
80 | | struct settings_group { |
81 | | const char *label; |
82 | | const char *name; |
83 | | }; |
84 | | ARRAY_DEFINE_TYPE(settings_group, struct settings_group); |
85 | | |
86 | | struct settings_mmap_block { |
87 | | const char *name; |
88 | | size_t block_end_offset; |
89 | | |
90 | | uint32_t filter_count; |
91 | | const uint64_t *filter_offsets; |
92 | | const uint32_t *filter_indexes; |
93 | | |
94 | | uint32_t settings_count; |
95 | | size_t settings_keys_offset; |
96 | | /* TRUE if settings have been validated against setting_parser_info */ |
97 | | bool settings_validated; |
98 | | }; |
99 | | |
100 | | struct settings_mmap_event_filter { |
101 | | struct event_filter *filter; |
102 | | bool is_group; |
103 | | |
104 | | uint32_t named_list_filter_count; |
105 | | }; |
106 | | |
107 | | struct settings_mmap { |
108 | | int refcount; |
109 | | pool_t pool; |
110 | | struct settings_root *root; |
111 | | |
112 | | void *mmap_base; |
113 | | size_t mmap_size; |
114 | | |
115 | | uint32_t all_keys_hash_key_prefix; |
116 | | uint32_t all_keys_hash_count; |
117 | | const uint32_t *all_keys_hash; |
118 | | const unsigned char *all_keys_base; |
119 | | |
120 | | uint32_t block_names_count; |
121 | | const uint32_t *block_names_rel_offsets; |
122 | | |
123 | | struct settings_mmap_event_filter *event_filters; |
124 | | unsigned int event_filters_count; |
125 | | |
126 | | HASH_TABLE(const char *, struct settings_mmap_block *) blocks; |
127 | | }; |
128 | | |
129 | | struct settings_root { |
130 | | pool_t pool; |
131 | | const char *protocol_name; |
132 | | struct settings_mmap *mmap; |
133 | | ARRAY_TYPE(settings_override) overrides; |
134 | | |
135 | | struct settings_mmap_pool *settings_pools; |
136 | | }; |
137 | | |
138 | | struct settings_instance { |
139 | | pool_t pool; |
140 | | struct settings_root *root; |
141 | | struct settings_mmap *mmap; |
142 | | ARRAY_TYPE(settings_override) overrides; |
143 | | }; |
144 | | |
145 | | struct settings_apply_override { |
146 | | struct settings_override *set; |
147 | | unsigned int order; |
148 | | }; |
149 | | |
150 | | struct settings_apply_ctx { |
151 | | struct event *event; |
152 | | struct settings_root *root; |
153 | | struct settings_instance *instance; |
154 | | const struct setting_parser_info *info; |
155 | | enum settings_get_flags flags; |
156 | | pool_t temp_pool; |
157 | | |
158 | | var_expand_escape_func_t *escape_func; |
159 | | void *escape_context; |
160 | | |
161 | | const char *filter_key; |
162 | | const char *filter_value; |
163 | | const char *filter_name; |
164 | | bool filter_name_required; |
165 | | bool seen_filter; |
166 | | |
167 | | struct setting_parser_context *parser; |
168 | | struct settings_mmap_pool *mpool; |
169 | | void *set_struct; |
170 | | ARRAY(enum set_seen_type) set_seen; |
171 | | ARRAY(struct settings_apply_override) overrides; |
172 | | ARRAY_TYPE(settings_group) include_groups; |
173 | | |
174 | | string_t *str; |
175 | | struct var_expand_params var_params; |
176 | | }; |
177 | | |
178 | | static ARRAY(const struct setting_parser_info *) set_registered_infos; |
179 | | |
180 | | static const char *settings_override_type_names[] = { |
181 | | "default", "userdb", "-o parameter", |
182 | | "2nd default", "2nd parameter", "hardcoded" |
183 | | }; |
184 | | static_assert_array_size(settings_override_type_names, |
185 | | SETTINGS_OVERRIDE_TYPE_COUNT); |
186 | | |
187 | | static struct event_filter event_filter_match_never, event_filter_match_always; |
188 | 0 | #define EVENT_FILTER_MATCH_ALWAYS (&event_filter_match_always) |
189 | 0 | #define EVENT_FILTER_MATCH_NEVER (&event_filter_match_never) |
190 | | |
191 | | static int |
192 | | settings_instance_override(struct settings_apply_ctx *ctx, |
193 | | struct settings_mmap_event_filter *set_filter, |
194 | | bool *defaults, const char **error_r); |
195 | | |
196 | | static bool settings_local_name_cmp(const char *value, const char *wanted_value) |
197 | 0 | { |
198 | 0 | return dns_match_wildcard(value, wanted_value) == 0; |
199 | 0 | } |
200 | | |
201 | | static void settings_override_free(struct settings_override *override) |
202 | 0 | { |
203 | 0 | if (override->filter != EVENT_FILTER_MATCH_ALWAYS && |
204 | 0 | override->filter != EVENT_FILTER_MATCH_NEVER) |
205 | 0 | event_filter_unref(&override->filter); |
206 | 0 | } |
207 | | |
208 | | static int |
209 | | settings_block_read_uint64(struct settings_mmap *mmap, |
210 | | size_t *offset, size_t end_offset, |
211 | | const char *name, uint64_t *num_r, |
212 | | const char **error_r) |
213 | 0 | { |
214 | 0 | if (*offset + sizeof(*num_r) > end_offset) { |
215 | 0 | *error_r = t_strdup_printf( |
216 | 0 | "Area too small when reading uint of '%s' " |
217 | 0 | "(offset=%zu, end_offset=%zu, file_size=%zu)", name, |
218 | 0 | *offset, end_offset, mmap->mmap_size); |
219 | 0 | return -1; |
220 | 0 | } |
221 | 0 | memcpy(num_r, CONST_PTR_OFFSET(mmap->mmap_base, *offset), |
222 | 0 | sizeof(*num_r)); |
223 | 0 | *offset += sizeof(*num_r); |
224 | 0 | return 0; |
225 | 0 | } |
226 | | |
227 | | static int |
228 | | settings_block_read_uint32(struct settings_mmap *mmap, |
229 | | size_t *offset, size_t end_offset, |
230 | | const char *name, uint32_t *num_r, |
231 | | const char **error_r) |
232 | 0 | { |
233 | 0 | if (*offset + sizeof(*num_r) > end_offset) { |
234 | 0 | *error_r = t_strdup_printf( |
235 | 0 | "Area too small when reading uint of '%s' " |
236 | 0 | "(offset=%zu, end_offset=%zu, file_size=%zu)", name, |
237 | 0 | *offset, end_offset, mmap->mmap_size); |
238 | 0 | return -1; |
239 | 0 | } |
240 | 0 | memcpy(num_r, CONST_PTR_OFFSET(mmap->mmap_base, *offset), |
241 | 0 | sizeof(*num_r)); |
242 | 0 | *offset += sizeof(*num_r); |
243 | 0 | return 0; |
244 | 0 | } |
245 | | |
246 | | static int |
247 | | settings_block_read_size(struct settings_mmap *mmap, |
248 | | size_t *offset, size_t end_offset, |
249 | | const char *name, uint64_t *size_r, |
250 | | const char **error_r) |
251 | 0 | { |
252 | 0 | if (*offset + sizeof(*size_r) > end_offset) { |
253 | 0 | *error_r = t_strdup_printf( |
254 | 0 | "Area too small when reading size of '%s' " |
255 | 0 | "(offset=%zu, end_offset=%zu, file_size=%zu)", name, |
256 | 0 | *offset, end_offset, mmap->mmap_size); |
257 | 0 | return -1; |
258 | 0 | } |
259 | 0 | memcpy(size_r, CONST_PTR_OFFSET(mmap->mmap_base, *offset), |
260 | 0 | sizeof(*size_r)); |
261 | 0 | if (*size_r > end_offset - *offset - sizeof(*size_r)) { |
262 | 0 | *error_r = t_strdup_printf( |
263 | 0 | "'%s' points outside area " |
264 | 0 | "(offset=%zu, size=%"PRIu64", end_offset=%zu, file_size=%zu)", |
265 | 0 | name, *offset, *size_r, end_offset, |
266 | 0 | mmap->mmap_size); |
267 | 0 | return -1; |
268 | 0 | } |
269 | 0 | *offset += sizeof(*size_r); |
270 | 0 | return 0; |
271 | 0 | } |
272 | | |
273 | | static int |
274 | | settings_block_read_str(struct settings_mmap *mmap, |
275 | | size_t *offset, size_t end_offset, const char *name, |
276 | | const char **str_r, const char **error_r) |
277 | 0 | { |
278 | 0 | *str_r = (const char *)mmap->mmap_base + *offset; |
279 | 0 | *offset += strlen(*str_r) + 1; |
280 | 0 | if (*offset > end_offset) { |
281 | 0 | *error_r = t_strdup_printf("'%s' points outside area " |
282 | 0 | "(offset=%zu, end_offset=%zu, file_size=%zu)", |
283 | 0 | name, *offset, end_offset, mmap->mmap_size); |
284 | 0 | return -1; |
285 | 0 | } |
286 | 0 | return 0; |
287 | 0 | } |
288 | | |
289 | | static bool |
290 | | settings_filter_node_match_service(struct event_filter_node *node, |
291 | | const char *service_name, bool op_not) |
292 | 0 | { |
293 | 0 | const char *node_service; |
294 | | |
295 | | /* Not perfect logic, but enough for the settings filters */ |
296 | 0 | switch (node->op) { |
297 | 0 | case EVENT_FILTER_OP_CMP_EQ: |
298 | 0 | if (node->type == EVENT_FILTER_NODE_TYPE_EVENT_FIELD_EXACT && |
299 | 0 | strcmp(node->field.key, SETTINGS_EVENT_FILTER_NAME) == 0 && |
300 | 0 | node->field.value_type == EVENT_FIELD_VALUE_TYPE_STR && |
301 | 0 | str_begins(node->field.value.str, "service/", &node_service)) { |
302 | 0 | bool match = strcmp(node_service, service_name) == 0; |
303 | 0 | return match != op_not; |
304 | 0 | } |
305 | 0 | break; |
306 | 0 | case EVENT_FILTER_OP_AND: |
307 | 0 | if (!settings_filter_node_match_service(node->children[0], |
308 | 0 | service_name, op_not) || |
309 | 0 | !settings_filter_node_match_service(node->children[1], |
310 | 0 | service_name, op_not)) |
311 | 0 | return FALSE; |
312 | 0 | break; |
313 | 0 | case EVENT_FILTER_OP_OR: |
314 | 0 | if (!settings_filter_node_match_service(node->children[0], |
315 | 0 | service_name, op_not) && |
316 | 0 | !settings_filter_node_match_service(node->children[1], |
317 | 0 | service_name, op_not)) |
318 | 0 | return FALSE; |
319 | 0 | break; |
320 | 0 | case EVENT_FILTER_OP_NOT: |
321 | 0 | return settings_filter_node_match_service(node->children[0], |
322 | 0 | service_name, !op_not); |
323 | 0 | default: |
324 | 0 | break; |
325 | 0 | } |
326 | 0 | return TRUE; |
327 | 0 | } |
328 | | |
329 | | static bool |
330 | | settings_filter_match_service(struct event_filter *filter, |
331 | | const char *service_name) |
332 | 0 | { |
333 | 0 | struct event_filter_node *node = event_filter_get_root_node(filter, 0); |
334 | 0 | i_assert(node != NULL); |
335 | 0 | i_assert(event_filter_get_root_node(filter, 1) == NULL); |
336 | 0 | return settings_filter_node_match_service(node, service_name, FALSE); |
337 | 0 | } |
338 | | |
339 | | static bool |
340 | | settings_event_filter_node_name_find(struct event_filter_node *node, |
341 | | const char *filter_name, bool op_not) |
342 | 0 | { |
343 | | /* Not perfect logic, but enough for the settings filters */ |
344 | 0 | switch (node->op) { |
345 | 0 | case EVENT_FILTER_OP_CMP_EQ: |
346 | 0 | if (!op_not && |
347 | 0 | node->type == EVENT_FILTER_NODE_TYPE_EVENT_FIELD_EXACT && |
348 | 0 | strcmp(node->field.key, SETTINGS_EVENT_FILTER_NAME) == 0 && |
349 | 0 | node->field.value_type == EVENT_FIELD_VALUE_TYPE_STR && |
350 | 0 | strcmp(node->field.value.str, filter_name) == 0) |
351 | 0 | return TRUE; |
352 | 0 | break; |
353 | 0 | case EVENT_FILTER_OP_AND: |
354 | 0 | case EVENT_FILTER_OP_OR: |
355 | 0 | if (settings_event_filter_node_name_find(node->children[0], |
356 | 0 | filter_name, op_not)) |
357 | 0 | return TRUE; |
358 | 0 | if (settings_event_filter_node_name_find(node->children[1], |
359 | 0 | filter_name, op_not)) |
360 | 0 | return TRUE; |
361 | 0 | break; |
362 | 0 | case EVENT_FILTER_OP_NOT: |
363 | 0 | return settings_event_filter_node_name_find(node->children[0], |
364 | 0 | filter_name, !op_not); |
365 | 0 | default: |
366 | 0 | break; |
367 | 0 | } |
368 | 0 | return FALSE; |
369 | 0 | } |
370 | | |
371 | | static bool |
372 | | settings_event_filter_name_find(struct event_filter *filter, |
373 | | const char *filter_name) |
374 | 0 | { |
375 | | /* NOTE: The event filter is using EVENT_FIELD_EXACT, so the value has |
376 | | already removed wildcard escapes. */ |
377 | 0 | struct event_filter_node *node = event_filter_get_root_node(filter, 0); |
378 | 0 | i_assert(node != NULL); |
379 | 0 | i_assert(event_filter_get_root_node(filter, 1) == NULL); |
380 | 0 | return settings_event_filter_node_name_find(node, filter_name, FALSE); |
381 | 0 | } |
382 | | |
383 | | static int |
384 | | settings_read_config_paths(struct settings_mmap *mmap, |
385 | | enum settings_read_flags flags, |
386 | | size_t *offset, const char **error_r) |
387 | 0 | { |
388 | 0 | uint32_t count; |
389 | 0 | if (settings_block_read_uint32(mmap, offset, mmap->mmap_size, |
390 | 0 | "config paths count", &count, |
391 | 0 | error_r) < 0) |
392 | 0 | return -1; |
393 | | |
394 | 0 | for (uint32_t i = 0; i < count; i++) { |
395 | 0 | const char *config_path; |
396 | 0 | if (settings_block_read_str(mmap, offset, mmap->mmap_size, |
397 | 0 | "config path", &config_path, |
398 | 0 | error_r) < 0) |
399 | 0 | return -1; |
400 | 0 | uint64_t inode, size; |
401 | 0 | if (settings_block_read_uint64(mmap, offset, mmap->mmap_size, |
402 | 0 | "config path inode", |
403 | 0 | &inode, error_r) < 0) |
404 | 0 | return -1; |
405 | 0 | if (settings_block_read_uint64(mmap, offset, mmap->mmap_size, |
406 | 0 | "config path size", |
407 | 0 | &size, error_r) < 0) |
408 | 0 | return -1; |
409 | 0 | uint32_t mtime_sec, mtime_nsec, ctime_sec, ctime_nsec; |
410 | 0 | if (settings_block_read_uint32(mmap, offset, mmap->mmap_size, |
411 | 0 | "config path mtime sec", |
412 | 0 | &mtime_sec, error_r) < 0) |
413 | 0 | return -1; |
414 | 0 | if (settings_block_read_uint32(mmap, offset, mmap->mmap_size, |
415 | 0 | "config path mtime nsec", |
416 | 0 | &mtime_nsec, error_r) < 0) |
417 | 0 | return -1; |
418 | | |
419 | 0 | if (settings_block_read_uint32(mmap, offset, mmap->mmap_size, |
420 | 0 | "config path ctime sec", |
421 | 0 | &ctime_sec, error_r) < 0) |
422 | 0 | return -1; |
423 | 0 | if (settings_block_read_uint32(mmap, offset, mmap->mmap_size, |
424 | 0 | "config path ctime nsec", |
425 | 0 | &ctime_nsec, error_r) < 0) |
426 | 0 | return -1; |
427 | | |
428 | 0 | if ((flags & SETTINGS_READ_CHECK_CACHE_TIMESTAMPS) == 0) |
429 | 0 | continue; |
430 | | |
431 | 0 | struct stat st; |
432 | 0 | if (stat(config_path, &st) < 0) { |
433 | 0 | if (errno == ENOENT || ENOACCESS(errno)) |
434 | 0 | return 0; |
435 | 0 | *error_r = t_strdup_printf("stat(%s) failed: %m", |
436 | 0 | config_path); |
437 | 0 | return -1; |
438 | 0 | } |
439 | 0 | if (st.st_ino != inode || (uoff_t)st.st_size != size || |
440 | 0 | st.st_mtime != mtime_sec || ST_MTIME_NSEC(st) != mtime_nsec || |
441 | 0 | st.st_ctime != ctime_sec || ST_CTIME_NSEC(st) != ctime_nsec) |
442 | 0 | return 0; |
443 | 0 | } |
444 | 0 | return 1; |
445 | 0 | } |
446 | | |
447 | | static int |
448 | | settings_read_all_keys(struct settings_mmap *mmap, |
449 | | size_t *offset, const char **error_r) |
450 | 0 | { |
451 | 0 | uint32_t size; |
452 | 0 | if (settings_block_read_uint32(mmap, offset, mmap->mmap_size, |
453 | 0 | "all keys size", &size, |
454 | 0 | error_r) < 0) |
455 | 0 | return -1; |
456 | 0 | mmap->all_keys_base = CONST_PTR_OFFSET(mmap->mmap_base, *offset); |
457 | 0 | size_t end_offset = *offset + size; |
458 | |
|
459 | 0 | if (*offset % sizeof(uint32_t) != 0) |
460 | 0 | *offset += sizeof(uint32_t) - *offset % sizeof(uint32_t); |
461 | | /* all keys */ |
462 | 0 | if (settings_block_read_uint32(mmap, offset, end_offset, |
463 | 0 | "all keys hash key prefix", |
464 | 0 | &mmap->all_keys_hash_key_prefix, |
465 | 0 | error_r) < 0) |
466 | 0 | return -1; |
467 | 0 | if (settings_block_read_uint32(mmap, offset, end_offset, |
468 | 0 | "all keys hash nodes count", |
469 | 0 | &mmap->all_keys_hash_count, |
470 | 0 | error_r) < 0) |
471 | 0 | return -1; |
472 | 0 | mmap->all_keys_hash = CONST_PTR_OFFSET(mmap->mmap_base, *offset); |
473 | | |
474 | | /* block names */ |
475 | 0 | *offset += sizeof(uint32_t) * mmap->all_keys_hash_count; |
476 | 0 | if (settings_block_read_uint32(mmap, offset, mmap->mmap_size, |
477 | 0 | "block names count", |
478 | 0 | &mmap->block_names_count, |
479 | 0 | error_r) < 0) |
480 | 0 | return -1; |
481 | 0 | mmap->block_names_rel_offsets = |
482 | 0 | CONST_PTR_OFFSET(mmap->mmap_base, *offset); |
483 | |
|
484 | 0 | *offset = end_offset; |
485 | 0 | return 0; |
486 | 0 | } |
487 | | |
488 | | static int |
489 | | settings_read_filters(struct settings_mmap *mmap, const char *service_name, |
490 | | enum settings_read_flags flags, size_t *offset, |
491 | | ARRAY_TYPE(const_string) *protocols, const char **error_r) |
492 | 0 | { |
493 | 0 | const char *filter_string, *error; |
494 | |
|
495 | 0 | if (settings_block_read_uint32(mmap, offset, mmap->mmap_size, |
496 | 0 | "filters count", |
497 | 0 | &mmap->event_filters_count, |
498 | 0 | error_r) < 0) |
499 | 0 | return -1; |
500 | | |
501 | 0 | mmap->event_filters = mmap->event_filters_count == 0 ? NULL : |
502 | 0 | p_new(mmap->pool, struct settings_mmap_event_filter, |
503 | 0 | mmap->event_filters_count); |
504 | |
|
505 | 0 | for (uint32_t i = 0; i < mmap->event_filters_count; i++) { |
506 | 0 | struct settings_mmap_event_filter *set_filter = |
507 | 0 | &mmap->event_filters[i]; |
508 | 0 | if (settings_block_read_str(mmap, offset, mmap->mmap_size, |
509 | 0 | "filter string", &filter_string, |
510 | 0 | error_r) < 0) |
511 | 0 | return -1; |
512 | 0 | if (settings_block_read_uint32(mmap, offset, mmap->mmap_size, |
513 | 0 | "named list filter element count", |
514 | 0 | &set_filter->named_list_filter_count, |
515 | 0 | error_r) < 0) |
516 | 0 | return -1; |
517 | | |
518 | 0 | if (filter_string[0] == '\0') { |
519 | 0 | set_filter->filter = EVENT_FILTER_MATCH_ALWAYS; |
520 | 0 | continue; |
521 | 0 | } |
522 | | |
523 | 0 | struct event_filter *tmp_filter = event_filter_create(); |
524 | 0 | if (event_filter_parse_case_sensitive(filter_string, |
525 | 0 | tmp_filter, &error) < 0) { |
526 | 0 | *error_r = t_strdup_printf( |
527 | 0 | "Received invalid filter '%s' at index %u: %s", |
528 | 0 | filter_string, i, error); |
529 | 0 | event_filter_unref(&tmp_filter); |
530 | 0 | return -1; |
531 | 0 | } |
532 | 0 | bool op_not; |
533 | 0 | const char *value = |
534 | 0 | event_filter_find_field_exact(tmp_filter, "protocol", &op_not); |
535 | 0 | if (value != NULL) { |
536 | 0 | if (op_not) |
537 | 0 | value = t_strconcat("!", value, NULL); |
538 | 0 | if (array_lsearch(protocols, &value, i_strcmp_p) == NULL) { |
539 | 0 | value = t_strdup(value); |
540 | 0 | array_push_back(protocols, &value); |
541 | 0 | } |
542 | |
|
543 | 0 | if (mmap->root->protocol_name != NULL && |
544 | 0 | (strcmp(mmap->root->protocol_name, value) == 0) == op_not && |
545 | 0 | (flags & SETTINGS_READ_NO_PROTOCOL_FILTER) == 0) { |
546 | | /* protocol doesn't match */ |
547 | 0 | set_filter->filter = EVENT_FILTER_MATCH_NEVER; |
548 | 0 | event_filter_unref(&tmp_filter); |
549 | 0 | continue; |
550 | 0 | } |
551 | 0 | } |
552 | 0 | if (service_name != NULL && |
553 | 0 | !settings_filter_match_service(tmp_filter, service_name)) { |
554 | | /* service name doesn't match */ |
555 | 0 | set_filter->filter = EVENT_FILTER_MATCH_NEVER; |
556 | 0 | event_filter_unref(&tmp_filter); |
557 | 0 | continue; |
558 | 0 | } |
559 | 0 | set_filter->is_group = |
560 | 0 | event_filter_has_field_prefix(tmp_filter, |
561 | 0 | SETTINGS_EVENT_FILTER_NAME, |
562 | 0 | SETTINGS_INCLUDE_GROUP_PREFIX_S); |
563 | |
|
564 | 0 | set_filter->filter = event_filter_create_with_pool(mmap->pool); |
565 | 0 | event_filter_register_cmp(set_filter->filter, "local_name", |
566 | 0 | settings_local_name_cmp); |
567 | 0 | pool_ref(mmap->pool); |
568 | 0 | event_filter_merge(set_filter->filter, tmp_filter, |
569 | 0 | EVENT_FILTER_MERGE_OP_OR); |
570 | 0 | event_filter_unref(&tmp_filter); |
571 | 0 | } |
572 | 0 | return 0; |
573 | 0 | } |
574 | | |
575 | | static int |
576 | | settings_block_read(struct settings_mmap *mmap, size_t *_offset, |
577 | | const char **error_r) |
578 | 0 | { |
579 | 0 | size_t offset = *_offset; |
580 | 0 | size_t block_size_offset = offset; |
581 | 0 | const char *key, *error; |
582 | | |
583 | | /* <block size> */ |
584 | 0 | uint64_t block_size; |
585 | 0 | if (settings_block_read_size(mmap, &offset, mmap->mmap_size, |
586 | 0 | "block size", &block_size, error_r) < 0) |
587 | 0 | return -1; |
588 | 0 | size_t block_end_offset = offset + block_size; |
589 | | |
590 | | /* Verify that block ends with NUL. This way we can safely use strlen() |
591 | | later on and we know it won't read past the mmaped memory area and |
592 | | cause a crash. The NUL is either from the last settings value or |
593 | | from the last error string. */ |
594 | 0 | if (((const char *)mmap->mmap_base)[block_end_offset-1] != '\0') { |
595 | 0 | *error_r = t_strdup_printf( |
596 | 0 | "Settings block doesn't end with NUL at offset %zu", |
597 | 0 | block_end_offset-1); |
598 | 0 | return -1; |
599 | 0 | } |
600 | | |
601 | | /* <block name> */ |
602 | 0 | const char *block_name; |
603 | 0 | if (settings_block_read_str(mmap, &offset, block_end_offset, |
604 | 0 | "block name", &block_name, error_r) < 0) |
605 | 0 | return -1; |
606 | | |
607 | 0 | struct settings_mmap_block *block = |
608 | 0 | hash_table_lookup(mmap->blocks, block_name); |
609 | 0 | if (block != NULL) { |
610 | 0 | *error_r = t_strdup_printf( |
611 | 0 | "Duplicate block name '%s' (offset=%zu)", |
612 | 0 | block_name, block_size_offset); |
613 | 0 | return -1; |
614 | 0 | } |
615 | 0 | block = p_new(mmap->pool, struct settings_mmap_block, 1); |
616 | 0 | block->name = block_name; |
617 | 0 | block->block_end_offset = block_end_offset; |
618 | 0 | hash_table_insert(mmap->blocks, block->name, block); |
619 | | |
620 | | /* <settings count> */ |
621 | 0 | if (settings_block_read_uint32(mmap, &offset, block_end_offset, |
622 | 0 | "settings count", &block->settings_count, |
623 | 0 | error_r) < 0) |
624 | 0 | return -1; |
625 | 0 | block->settings_keys_offset = offset; |
626 | | /* skip over the settings keys for now - they will be validated later */ |
627 | 0 | for (uint32_t i = 0; i < block->settings_count; i++) { |
628 | 0 | if (settings_block_read_str(mmap, &offset, block_end_offset, |
629 | 0 | "setting key", &key, error_r) < 0) |
630 | 0 | return -1; |
631 | 0 | } |
632 | | |
633 | | /* <filter count> */ |
634 | 0 | if (settings_block_read_uint32(mmap, &offset, block_end_offset, |
635 | 0 | "filter count", &block->filter_count, |
636 | 0 | error_r) < 0) |
637 | 0 | return -1; |
638 | | |
639 | | /* filters */ |
640 | 0 | unsigned int filter_idx; |
641 | 0 | for (filter_idx = 0; filter_idx < block->filter_count; filter_idx++) { |
642 | | /* <filter settings size> */ |
643 | 0 | uint64_t filter_settings_size; |
644 | 0 | if (settings_block_read_size(mmap, &offset, |
645 | 0 | block_end_offset, "filter settings size", |
646 | 0 | &filter_settings_size, error_r) < 0) |
647 | 0 | return -1; |
648 | | |
649 | | /* <error string> */ |
650 | 0 | size_t tmp_offset = offset; |
651 | 0 | size_t filter_end_offset = offset + filter_settings_size; |
652 | 0 | if (settings_block_read_str(mmap, &tmp_offset, |
653 | 0 | filter_end_offset, |
654 | 0 | "filter error string", &error, |
655 | 0 | error_r) < 0) |
656 | 0 | return -1; |
657 | | |
658 | 0 | uint32_t include_count; |
659 | 0 | if (settings_block_read_uint32(mmap, &tmp_offset, |
660 | 0 | filter_end_offset, |
661 | 0 | "include group count", |
662 | 0 | &include_count, error_r) < 0) |
663 | 0 | return -1; |
664 | | |
665 | 0 | for (uint32_t i = 0; i < include_count; i++) { |
666 | | /* <group label> */ |
667 | 0 | const char *group_label; |
668 | 0 | if (settings_block_read_str(mmap, &tmp_offset, |
669 | 0 | filter_end_offset, |
670 | 0 | "group label string", |
671 | 0 | &group_label, error_r) < 0) |
672 | 0 | return -1; |
673 | | |
674 | | /* <group name> */ |
675 | 0 | const char *group_name; |
676 | 0 | if (settings_block_read_str(mmap, &tmp_offset, |
677 | 0 | filter_end_offset, |
678 | 0 | "group name string", |
679 | 0 | &group_name, error_r) < 0) |
680 | 0 | return -1; |
681 | 0 | } |
682 | | |
683 | | /* skip over the filter contents for now */ |
684 | 0 | offset += filter_settings_size; |
685 | 0 | } |
686 | | |
687 | 0 | if (offset % sizeof(uint64_t) != 0) |
688 | 0 | offset += sizeof(uint64_t) - offset % sizeof(uint64_t); |
689 | 0 | block->filter_offsets = CONST_PTR_OFFSET(mmap->mmap_base, offset); |
690 | 0 | offset += sizeof(uint64_t) * block->filter_count; |
691 | 0 | block->filter_indexes = CONST_PTR_OFFSET(mmap->mmap_base, offset); |
692 | 0 | offset += sizeof(uint32_t) * block->filter_count; |
693 | 0 | offset++; /* safety NUL */ |
694 | |
|
695 | 0 | if (offset != block_end_offset) { |
696 | 0 | *error_r = t_strdup_printf( |
697 | 0 | "Filter end offset mismatch (%zu != %zu)", |
698 | 0 | offset, block_end_offset); |
699 | 0 | return -1; |
700 | 0 | } |
701 | 0 | *_offset = offset; |
702 | 0 | return 0; |
703 | 0 | } |
704 | | |
705 | | static int |
706 | | settings_mmap_parse(struct settings_mmap *mmap, const char *service_name, |
707 | | enum settings_read_flags flags, |
708 | | const char *const **specific_protocols_r, |
709 | | const char **error_r) |
710 | 0 | { |
711 | | /* |
712 | | See ../config/config-dump-full.c for the binary config file format |
713 | | description. |
714 | | |
715 | | Settings are read until the blob size is reached. There is no |
716 | | padding/alignment. */ |
717 | 0 | const unsigned char *mmap_base = mmap->mmap_base; |
718 | 0 | size_t mmap_size = mmap->mmap_size; |
719 | 0 | ARRAY_TYPE(const_string) protocols; |
720 | |
|
721 | 0 | t_array_init(&protocols, 8); |
722 | |
|
723 | 0 | const char *magic_prefix = "DOVECOT-CONFIG\t"; |
724 | 0 | const unsigned int magic_prefix_len = strlen(magic_prefix); |
725 | 0 | const unsigned char *eol = memchr(mmap_base, '\n', mmap_size); |
726 | 0 | if (mmap_size < magic_prefix_len || |
727 | 0 | memcmp(magic_prefix, mmap_base, magic_prefix_len) != 0 || |
728 | 0 | eol == NULL) { |
729 | 0 | *error_r = "File header doesn't begin with DOVECOT-CONFIG line"; |
730 | 0 | return -1; |
731 | 0 | } |
732 | 0 | if (mmap_base[magic_prefix_len] != '1' || |
733 | 0 | mmap_base[magic_prefix_len+1] != '.') { |
734 | 0 | *error_r = t_strdup_printf( |
735 | 0 | "Unsupported config file version '%s'", |
736 | 0 | t_strdup_until(mmap_base + magic_prefix_len, eol)); |
737 | 0 | return -1; |
738 | 0 | } |
739 | | |
740 | | /* <settings full size> */ |
741 | 0 | size_t full_size_offset = eol - mmap_base + 1; |
742 | 0 | uint64_t settings_full_size; |
743 | 0 | memcpy(&settings_full_size, mmap_base + full_size_offset, |
744 | 0 | sizeof(settings_full_size)); |
745 | 0 | if (full_size_offset + sizeof(settings_full_size) + |
746 | 0 | settings_full_size != mmap_size) { |
747 | 0 | *error_r = t_strdup_printf("Full size mismatch: " |
748 | 0 | "Expected %zu + %zu + %"PRIu64", but file size is %zu", |
749 | 0 | full_size_offset, sizeof(settings_full_size), |
750 | 0 | settings_full_size, mmap_size); |
751 | 0 | return -1; |
752 | 0 | } |
753 | | |
754 | 0 | size_t offset = full_size_offset + sizeof(settings_full_size); |
755 | 0 | int ret; |
756 | 0 | if ((ret = settings_read_config_paths(mmap, flags, &offset, error_r)) <= 0) |
757 | 0 | return ret; |
758 | 0 | if (settings_read_all_keys(mmap, &offset, error_r) < 0) |
759 | 0 | return -1; |
760 | 0 | if (settings_read_filters(mmap, service_name, flags, &offset, |
761 | 0 | &protocols, error_r) < 0) |
762 | 0 | return -1; |
763 | | |
764 | 0 | do { |
765 | 0 | if (settings_block_read(mmap, &offset, error_r) < 0) |
766 | 0 | return -1; |
767 | 0 | } while (offset < mmap_size); |
768 | | |
769 | 0 | if (array_count(&protocols) > 0) { |
770 | 0 | array_append_zero(&protocols); |
771 | 0 | *specific_protocols_r = array_front(&protocols); |
772 | 0 | } else { |
773 | 0 | *specific_protocols_r = NULL; |
774 | 0 | } |
775 | 0 | if ((flags & SETTINGS_READ_CHECK_CACHE_TIMESTAMPS) != 0) |
776 | 0 | return 1; |
777 | 0 | return 0; |
778 | 0 | } |
779 | | |
780 | | static const char * |
781 | | get_invalid_setting_error(struct settings_apply_ctx *ctx, const char *prefix, |
782 | | const char *key, |
783 | | const char *value, const char *orig_value) |
784 | 0 | { |
785 | | /* Config process will preprocess var-expand templates when it sees them |
786 | | and puts them in front of the actual value, separated by LF. |
787 | | */ |
788 | 0 | if (*orig_value == '\1') { |
789 | 0 | orig_value = strchr(orig_value, '\n'); |
790 | 0 | i_assert(orig_value != NULL); |
791 | 0 | orig_value++; |
792 | 0 | } |
793 | 0 | string_t *str = t_str_new(64); |
794 | 0 | str_printfa(str, "%s %s=%s", prefix, key, value); |
795 | 0 | if (strcmp(value, orig_value) != 0) |
796 | 0 | str_printfa(str, " (before expansion: %s)", orig_value); |
797 | 0 | str_printfa(str, ": %s", settings_parser_get_error(ctx->parser)); |
798 | 0 | return str_c(str); |
799 | 0 | } |
800 | | |
801 | | static int |
802 | | settings_var_expand(struct settings_apply_ctx *ctx, unsigned int key_idx, |
803 | | const char **value, const char **error_r) |
804 | 0 | { |
805 | 0 | struct settings_file file = { NULL, NULL }; |
806 | 0 | const char *orig_value = *value; |
807 | 0 | bool changed; |
808 | 0 | bool want_expand = FALSE; |
809 | | |
810 | | /* check which one to use */ |
811 | 0 | if (*orig_value == '\1') { |
812 | 0 | *value = strchr(orig_value, '\n'); |
813 | 0 | if (*value == NULL) { |
814 | 0 | *error_r = "Missing template termination LF"; |
815 | 0 | return -1; |
816 | 0 | } |
817 | 0 | want_expand = TRUE; |
818 | | /* Move past newline */ |
819 | 0 | (*value)++; |
820 | 0 | } |
821 | | |
822 | 0 | if ((ctx->flags & SETTINGS_GET_FLAG_NO_EXPAND) != 0) |
823 | 0 | return 0; |
824 | | |
825 | 0 | if (ctx->info->defines[key_idx].type == SET_STR_NOVARS || |
826 | 0 | ctx->info->defines[key_idx].type == SET_FILTER_ARRAY) |
827 | 0 | return 0; |
828 | | |
829 | 0 | if (!want_expand && ctx->info->defines[key_idx].type == SET_FILE) { |
830 | 0 | settings_file_get(*value, &ctx->mpool->pool, &file); |
831 | | /* Make sure only the file path is var-expanded. */ |
832 | 0 | orig_value = file.path; |
833 | 0 | } |
834 | |
|
835 | 0 | enum setting_type set_type = ctx->info->defines[key_idx].type; |
836 | 0 | if (set_type == SET_PATH_FILE || set_type == SET_PATH_DIR) { |
837 | | /* Note: for these types *value points to the raw value both |
838 | | with and without a config-exported template. */ |
839 | 0 | const char *path = *value; |
840 | 0 | size_t path_len = strlen(path); |
841 | 0 | bool path_changed = FALSE; |
842 | |
|
843 | 0 | if (set_type == SET_PATH_DIR && |
844 | 0 | path_len > 0 && path[path_len-1] == '/') { |
845 | | /* drop trailing '/' */ |
846 | 0 | path = t_strndup(path, path_len - 1); |
847 | 0 | path_changed = TRUE; |
848 | 0 | } |
849 | 0 | if (str_begins_with(path, "~/") || strcmp(path, "~") == 0) { |
850 | | /* Convert the ~/ prefix to the %{home} variable and |
851 | | let it be expanded like the other %variables. */ |
852 | 0 | path = t_strconcat("%{home}", path + 1, NULL); |
853 | 0 | path_changed = TRUE; |
854 | 0 | } |
855 | 0 | if (path_changed) { |
856 | | /* This replaces any config-exported var_expand |
857 | | template, since the template was compiled from the |
858 | | original value. */ |
859 | 0 | orig_value = path; |
860 | 0 | if (!want_expand && strchr(path, '%') == NULL) { |
861 | | /* no %variables - only the path changed */ |
862 | 0 | *value = p_strdup(&ctx->mpool->pool, path); |
863 | 0 | return 1; |
864 | 0 | } |
865 | 0 | want_expand = TRUE; |
866 | 0 | } |
867 | 0 | } |
868 | | |
869 | | /* Ensure we don't potentially have %{ values that we missed, |
870 | | and since this will be quite often called, just check for |
871 | | % and run it through var-expand. |
872 | | |
873 | | Most the misses will come from default settings and overrides |
874 | | that are not processed by config process. |
875 | | */ |
876 | 0 | if (!want_expand && strchr(orig_value, '%') != NULL) |
877 | 0 | want_expand = TRUE; |
878 | |
|
879 | 0 | if (!want_expand) { |
880 | | /* fast path: No %variables in the value */ |
881 | 0 | changed = FALSE; |
882 | 0 | } else { |
883 | 0 | struct var_expand_program *program; |
884 | 0 | if (*orig_value == '\1') { |
885 | 0 | i_assert(*value != NULL); |
886 | | /* import after \1 to terminating newline, exclusive */ |
887 | 0 | if (var_expand_program_import_sized(orig_value + 1, |
888 | 0 | *value - orig_value - 2, |
889 | 0 | &program, error_r) < 0) |
890 | 0 | return -1; |
891 | 0 | } else if (var_expand_program_create(orig_value, &program, |
892 | 0 | error_r) < 0) { |
893 | 0 | return -1; |
894 | 0 | } |
895 | | |
896 | 0 | str_truncate(ctx->str, 0); |
897 | 0 | int ret = var_expand_program_execute(ctx->str, program, |
898 | 0 | &ctx->var_params, error_r); |
899 | 0 | var_expand_program_free(&program); |
900 | 0 | if (ret < 0 && (ctx->flags & SETTINGS_GET_FLAG_FAKE_EXPAND) == 0) |
901 | 0 | return -1; |
902 | 0 | changed = strcmp(*value, str_c(ctx->str)) != 0; |
903 | 0 | } |
904 | | |
905 | 0 | if (!changed) { |
906 | 0 | return 0; |
907 | 0 | } else if (ctx->info->defines[key_idx].type == SET_FILE) { |
908 | 0 | file.path = str_c(ctx->str); |
909 | 0 | if (settings_parse_read_file(file.path, file.path, |
910 | 0 | &ctx->mpool->pool, NULL, |
911 | 0 | "", value, error_r) < 0) |
912 | 0 | return -1; |
913 | 0 | } else { |
914 | 0 | *value = p_strdup(&ctx->mpool->pool, str_c(ctx->str)); |
915 | 0 | } |
916 | 0 | return 1; |
917 | 0 | } |
918 | | |
919 | | static int |
920 | | settings_mmap_apply_key(struct settings_apply_ctx *ctx, unsigned int key_idx, |
921 | | const char *list_key, const char *value, |
922 | | const char **error_r) |
923 | 0 | { |
924 | 0 | const char *key = ctx->info->defines[key_idx].key; |
925 | 0 | const char *error, *orig_value = value; |
926 | 0 | if (list_key != NULL) |
927 | 0 | key = t_strdup_printf("%s/%s", key, list_key); |
928 | | |
929 | | /* call settings_apply() before variable expansion */ |
930 | 0 | enum setting_apply_flags apply_flags = |
931 | 0 | (ctx->flags & SETTINGS_GET_FLAG_NO_EXPAND) == 0 ? 0 : |
932 | 0 | SETTING_APPLY_FLAG_NO_EXPAND; |
933 | |
|
934 | 0 | const char *orig_ptr = value; |
935 | |
|
936 | 0 | if (*value == '\1') { |
937 | 0 | value = strchr(value, '\n'); |
938 | 0 | if (value == NULL) { |
939 | 0 | *error_r = "Missing template termination LF"; |
940 | 0 | return -1; |
941 | 0 | } |
942 | 0 | value++; |
943 | 0 | orig_ptr = value; |
944 | 0 | } |
945 | | |
946 | | /* Provide the non-exported template here */ |
947 | 0 | if (ctx->info->setting_apply != NULL && |
948 | 0 | !ctx->info->setting_apply(ctx->event, ctx->set_struct, key, &value, |
949 | 0 | apply_flags, error_r)) { |
950 | 0 | *error_r = t_strdup_printf("Invalid setting %s=%s: %s", |
951 | 0 | key, orig_value, *error_r); |
952 | 0 | return -1; |
953 | 0 | } |
954 | | |
955 | | /* If the value didn't change, provide the actual original value |
956 | | here to make sure settings_var_expand() can import the exported |
957 | | var_expand template. */ |
958 | 0 | if (value == orig_ptr) |
959 | 0 | value = orig_value; |
960 | |
|
961 | 0 | if (settings_var_expand(ctx, key_idx, &value, &error) < 0) { |
962 | 0 | *error_r = t_strdup_printf( |
963 | 0 | "Failed to expand %s setting variables: %s", |
964 | 0 | key, error); |
965 | 0 | return -1; |
966 | 0 | } |
967 | | /* value points to mmap()ed memory, which is kept |
968 | | referenced by the set_pool for the life time of the |
969 | | settings struct. */ |
970 | 0 | if (settings_parse_keyidx_value_nodup(ctx->parser, key_idx, |
971 | 0 | key, value) < 0) { |
972 | 0 | *error_r = get_invalid_setting_error(ctx, "Invalid setting", |
973 | 0 | key, value, orig_value); |
974 | 0 | return -1; |
975 | 0 | } |
976 | 0 | return 0; |
977 | 0 | } |
978 | | |
979 | | static int |
980 | | settings_mmap_apply_defaults(struct settings_apply_ctx *ctx, |
981 | | const char **error_r) |
982 | 0 | { |
983 | 0 | enum setting_apply_flags apply_flags = SETTING_APPLY_FLAG_OVERRIDE | |
984 | 0 | ((ctx->flags & SETTINGS_GET_FLAG_NO_EXPAND) == 0 ? 0 : |
985 | 0 | SETTING_APPLY_FLAG_NO_EXPAND); |
986 | 0 | const char *error; |
987 | 0 | unsigned int key_idx; |
988 | |
|
989 | 0 | for (key_idx = 0; ctx->info->defines[key_idx].key != NULL; key_idx++) { |
990 | 0 | enum set_seen_type *set_seenp = |
991 | 0 | array_idx_get_space(&ctx->set_seen, key_idx); |
992 | 0 | if (*set_seenp == SET_SEEN_YES) |
993 | 0 | continue; |
994 | | |
995 | 0 | void *set = PTR_OFFSET(ctx->info->defaults, |
996 | 0 | ctx->info->defines[key_idx].offset); |
997 | 0 | if (!setting_type_is_str_vars(ctx->info->defines[key_idx].type)) |
998 | 0 | continue; /* not needed for now */ |
999 | 0 | const char *key = ctx->info->defines[key_idx].key; |
1000 | 0 | const char *const *valuep = set; |
1001 | 0 | const char *value = *valuep; |
1002 | 0 | if (value == NULL) |
1003 | 0 | continue; |
1004 | | |
1005 | 0 | if (ctx->info->setting_apply != NULL && |
1006 | 0 | !ctx->info->setting_apply(ctx->event, ctx->set_struct, key, |
1007 | 0 | &value, apply_flags, &error)) |
1008 | 0 | i_panic("BUG: Failed to apply default setting %s=%s: %s", |
1009 | 0 | key, value, error); |
1010 | | |
1011 | 0 | int ret = settings_var_expand(ctx, key_idx, &value, &error); |
1012 | 0 | if (ret < 0) { |
1013 | 0 | *error_r = t_strdup_printf( |
1014 | 0 | "Failed to expand default setting %s=%s variables: %s", |
1015 | 0 | key, value, error); |
1016 | 0 | return -1; |
1017 | 0 | } |
1018 | 0 | if (ret > 0 && |
1019 | 0 | settings_parse_keyidx_value(ctx->parser, key_idx, |
1020 | 0 | key, value) < 0) { |
1021 | 0 | *error_r = get_invalid_setting_error(ctx, |
1022 | 0 | "Invalid default setting", |
1023 | 0 | key, value, value); |
1024 | 0 | return -1; |
1025 | 0 | } |
1026 | 0 | } |
1027 | 0 | return 0; |
1028 | 0 | } |
1029 | | |
1030 | | static int |
1031 | | settings_mmap_apply_blob(struct settings_apply_ctx *ctx, |
1032 | | struct settings_mmap_block *block, |
1033 | | size_t start_offset, size_t end_offset, |
1034 | | const char **error_r) |
1035 | 0 | { |
1036 | 0 | struct settings_mmap *mmap = ctx->instance->mmap; |
1037 | 0 | size_t offset = start_offset; |
1038 | | |
1039 | | /* list of settings: key, value, ... */ |
1040 | 0 | while (offset < end_offset) { |
1041 | | /* We already checked that settings blob ends with NUL, so |
1042 | | strlen() can be used safely. */ |
1043 | 0 | uint32_t key_idx; |
1044 | 0 | memcpy(&key_idx, CONST_PTR_OFFSET(mmap->mmap_base, offset), |
1045 | 0 | sizeof(key_idx)); |
1046 | 0 | if (key_idx >= block->settings_count) { |
1047 | 0 | *error_r = t_strdup_printf( |
1048 | 0 | "Settings key index too high (%u >= %u)", |
1049 | 0 | key_idx, block->settings_count); |
1050 | 0 | return -1; |
1051 | 0 | } |
1052 | 0 | offset += sizeof(key_idx); |
1053 | |
|
1054 | 0 | bool set_apply = TRUE; |
1055 | 0 | const char *list_key = NULL; |
1056 | |
|
1057 | 0 | enum set_seen_type *set_seenp = |
1058 | 0 | array_idx_get_space(&ctx->set_seen, key_idx); |
1059 | 0 | if (*set_seenp != SET_SEEN_NO) { |
1060 | | /* Already seen this setting - don't set it again. |
1061 | | This check is used also with boollists when the |
1062 | | whole list is replaced. */ |
1063 | 0 | set_apply = FALSE; |
1064 | 0 | } |
1065 | 0 | bool list_stop = FALSE, list_clear = FALSE; |
1066 | 0 | if (ctx->info->defines[key_idx].type == SET_BOOLLIST || |
1067 | 0 | ctx->info->defines[key_idx].type == SET_STRLIST) { |
1068 | 0 | const char *type = |
1069 | 0 | (const char *)mmap->mmap_base + offset; |
1070 | 0 | if (type[0] == SET_LIST_REPLACE[0]) |
1071 | 0 | list_stop = TRUE; |
1072 | 0 | else if (type[0] == SET_LIST_CLEAR[0]) { |
1073 | 0 | list_stop = TRUE; |
1074 | 0 | list_clear = TRUE; |
1075 | 0 | } else if (type[0] != SET_LIST_APPEND[0]) { |
1076 | 0 | *error_r = t_strdup_printf( |
1077 | 0 | "List type is invalid (offset=%zu)", |
1078 | 0 | offset); |
1079 | 0 | return -1; |
1080 | 0 | } |
1081 | 0 | offset++; |
1082 | 0 | } |
1083 | | |
1084 | 0 | if (ctx->info->defines[key_idx].type == SET_STRLIST || |
1085 | 0 | ctx->info->defines[key_idx].type == SET_BOOLLIST) { |
1086 | 0 | list_key = (const char *)mmap->mmap_base + offset; |
1087 | 0 | offset += strlen(list_key)+1; |
1088 | | /* Keys from the config are trusted, so expand their |
1089 | | %variables the same way the values are expanded. This |
1090 | | is done before the has_key() check so deduplication |
1091 | | operates on the final key. */ |
1092 | 0 | const char *error; |
1093 | 0 | if (settings_var_expand(ctx, key_idx, &list_key, |
1094 | 0 | &error) < 0) { |
1095 | 0 | *error_r = t_strdup_printf( |
1096 | 0 | "Failed to expand %s list key variables: %s", |
1097 | 0 | ctx->info->defines[key_idx].key, error); |
1098 | 0 | return -1; |
1099 | 0 | } |
1100 | 0 | set_apply = set_apply && |
1101 | 0 | !settings_parse_list_has_key(ctx->parser, |
1102 | 0 | key_idx, settings_section_unescape(list_key)); |
1103 | 0 | } else if (ctx->info->defines[key_idx].type == SET_FILTER_ARRAY) |
1104 | 0 | set_apply = TRUE; |
1105 | 0 | else if (set_apply) |
1106 | 0 | *set_seenp = SET_SEEN_YES; |
1107 | | |
1108 | 0 | if (offset >= end_offset) { |
1109 | | /* if offset==end_offset, the value is missing. */ |
1110 | 0 | *error_r = t_strdup_printf( |
1111 | 0 | "Settings key/value points outside blob " |
1112 | 0 | "(offset=%zu, end_offset=%zu, file_size=%zu)", |
1113 | 0 | offset, end_offset, mmap->mmap_size); |
1114 | 0 | return -1; |
1115 | 0 | } |
1116 | 0 | const char *value = (const char *)mmap->mmap_base + offset; |
1117 | 0 | offset += strlen(value)+1; |
1118 | 0 | if (offset > end_offset) { |
1119 | 0 | *error_r = t_strdup_printf( |
1120 | 0 | "Settings value points outside blob " |
1121 | 0 | "(offset=%zu, end_offset=%zu, file_size=%zu)", |
1122 | 0 | offset, end_offset, mmap->mmap_size); |
1123 | 0 | return -1; |
1124 | 0 | } |
1125 | 0 | int ret = 0; |
1126 | 0 | T_BEGIN { |
1127 | 0 | if (set_apply && !list_clear) { |
1128 | 0 | ret = settings_mmap_apply_key(ctx, key_idx, |
1129 | 0 | list_key, value, error_r); |
1130 | 0 | } |
1131 | 0 | if (list_stop) { |
1132 | | /* The lists are filled in reverse order. |
1133 | | Mark it now as "stopped", so the following |
1134 | | list settings won't modify it. */ |
1135 | 0 | settings_parse_array_stop(ctx->parser, key_idx); |
1136 | 0 | } |
1137 | 0 | } T_END_PASS_STR_IF(ret < 0, error_r); |
1138 | 0 | if (ret < 0) |
1139 | 0 | return -1; |
1140 | 0 | } |
1141 | 0 | return 0; |
1142 | 0 | } |
1143 | | |
1144 | | static int settings_mmap_validate(struct settings_mmap *mmap, |
1145 | | struct settings_mmap_block *block, |
1146 | | const struct setting_parser_info *info, |
1147 | | const char **error_r) |
1148 | 0 | { |
1149 | 0 | size_t offset = block->settings_keys_offset; |
1150 | 0 | const char *key; |
1151 | 0 | uint32_t i; |
1152 | |
|
1153 | 0 | for (i = 0; info->defines[i].key != NULL; i++) { |
1154 | 0 | if (i >= block->settings_count) |
1155 | 0 | break; |
1156 | 0 | if (settings_block_read_str(mmap, &offset, |
1157 | 0 | block->block_end_offset, |
1158 | 0 | "setting key", &key, error_r) < 0) { |
1159 | | /* shouldn't happen - we already read it once */ |
1160 | 0 | return -1; |
1161 | 0 | } |
1162 | 0 | if (strcmp(info->defines[i].key, key) != 0) { |
1163 | 0 | *error_r = t_strdup_printf( |
1164 | 0 | "settings struct %s #%u key mismatch %s != %s", |
1165 | 0 | info->name, i, info->defines[i].key, key); |
1166 | 0 | return -1; |
1167 | 0 | } |
1168 | 0 | } |
1169 | 0 | if (i != block->settings_count) { |
1170 | 0 | *error_r = t_strdup_printf( |
1171 | 0 | "settings struct %s count mismatch %u != %u", |
1172 | 0 | info->name, i+1, block->settings_count); |
1173 | 0 | return -1; |
1174 | 0 | } |
1175 | 0 | return 0; |
1176 | 0 | } |
1177 | | |
1178 | | static int |
1179 | | settings_mmap_get_filter_idx(struct settings_mmap *mmap, |
1180 | | struct settings_mmap_block *block, |
1181 | | uint32_t filter_idx, uint32_t *event_filter_idx_r, |
1182 | | const char **error_r) |
1183 | 0 | { |
1184 | 0 | *event_filter_idx_r = block->filter_indexes[filter_idx]; |
1185 | 0 | if (*event_filter_idx_r >= mmap->event_filters_count) { |
1186 | 0 | *error_r = t_strdup_printf("event filter idx %u >= %u", |
1187 | 0 | *event_filter_idx_r, mmap->event_filters_count); |
1188 | 0 | return -1; |
1189 | 0 | } |
1190 | 0 | return 0; |
1191 | 0 | } |
1192 | | |
1193 | | static int |
1194 | | settings_mmap_apply_filter(struct settings_apply_ctx *ctx, |
1195 | | struct settings_mmap_block *block, |
1196 | | uint32_t filter_idx, |
1197 | | struct settings_mmap_event_filter *set_filter, |
1198 | | const char **error_r) |
1199 | 0 | { |
1200 | 0 | struct settings_mmap *mmap = ctx->instance->mmap; |
1201 | 0 | uint64_t filter_offset, filter_set_size; |
1202 | |
|
1203 | 0 | filter_offset = block->filter_offsets[filter_idx]; |
1204 | 0 | memcpy(&filter_set_size, |
1205 | 0 | CONST_PTR_OFFSET(mmap->mmap_base, filter_offset), |
1206 | 0 | sizeof(filter_set_size)); |
1207 | 0 | filter_offset += sizeof(filter_set_size); |
1208 | 0 | uint64_t filter_end_offset = filter_offset + filter_set_size; |
1209 | |
|
1210 | 0 | const char *filter_error = |
1211 | 0 | CONST_PTR_OFFSET(mmap->mmap_base, filter_offset); |
1212 | 0 | if (filter_error[0] != '\0') { |
1213 | 0 | *error_r = filter_error; |
1214 | 0 | return -1; |
1215 | 0 | } |
1216 | 0 | filter_offset += strlen(filter_error) + 1; |
1217 | |
|
1218 | 0 | uint32_t include_count; |
1219 | 0 | memcpy(&include_count, CONST_PTR_OFFSET(mmap->mmap_base, filter_offset), |
1220 | 0 | sizeof(include_count)); |
1221 | 0 | filter_offset += sizeof(include_count); |
1222 | |
|
1223 | 0 | if (ctx->filter_name != NULL && !ctx->seen_filter && |
1224 | 0 | set_filter->filter != EVENT_FILTER_MATCH_ALWAYS && |
1225 | 0 | settings_event_filter_name_find(set_filter->filter, ctx->filter_name)) |
1226 | 0 | ctx->seen_filter = TRUE; |
1227 | |
|
1228 | 0 | array_clear(&ctx->include_groups); |
1229 | 0 | for (uint32_t j = 0; j < include_count; j++) { |
1230 | 0 | struct settings_group *include_group = |
1231 | 0 | array_append_space(&ctx->include_groups); |
1232 | 0 | include_group->label = |
1233 | 0 | CONST_PTR_OFFSET(mmap->mmap_base, filter_offset); |
1234 | 0 | filter_offset += strlen(include_group->label) + 1; |
1235 | |
|
1236 | 0 | include_group->name = |
1237 | 0 | CONST_PTR_OFFSET(mmap->mmap_base, filter_offset); |
1238 | 0 | filter_offset += strlen(include_group->name) + 1; |
1239 | 0 | } |
1240 | | /* Apply overrides specific to this filter before the |
1241 | | filter settings themselves. For base settings the |
1242 | | filter is EVENT_FILTER_MATCH_ALWAYS, which applies |
1243 | | the rest of the overrides that weren't already |
1244 | | handled. This way global setting overrides don't |
1245 | | override named filters' settings, unless the |
1246 | | override is specifically using the filter name |
1247 | | as prefix. */ |
1248 | 0 | bool defaults = FALSE; |
1249 | 0 | int ret = settings_instance_override(ctx, set_filter, |
1250 | 0 | &defaults, error_r); |
1251 | 0 | if (ret < 0) |
1252 | 0 | return -1; |
1253 | | |
1254 | 0 | if (ret > 0) |
1255 | 0 | ctx->seen_filter = TRUE; |
1256 | |
|
1257 | 0 | if (settings_mmap_apply_blob(ctx, block, |
1258 | 0 | filter_offset, filter_end_offset, |
1259 | 0 | error_r) < 0) |
1260 | 0 | return -1; |
1261 | 0 | if (defaults) { |
1262 | 0 | ret = settings_instance_override(ctx, set_filter, |
1263 | 0 | &defaults, error_r); |
1264 | 0 | if (ret < 0) |
1265 | 0 | return -1; |
1266 | 0 | if (ret > 0) |
1267 | 0 | ctx->seen_filter = TRUE; |
1268 | 0 | } |
1269 | 0 | return 0; |
1270 | 0 | } |
1271 | | |
1272 | | static struct event *settings_group_event_create(struct settings_apply_ctx *ctx) |
1273 | 0 | { |
1274 | 0 | struct event *event = event_create(ctx->event); |
1275 | 0 | const struct settings_group *include_group; |
1276 | 0 | array_foreach(&ctx->include_groups, include_group) { |
1277 | | /* Add @<group label>/<group name> to matching filters and |
1278 | | restart the filter processing. */ |
1279 | 0 | const char *filter_value = t_strdup_printf( |
1280 | 0 | SETTINGS_INCLUDE_GROUP_PREFIX_S"%s/%s", |
1281 | 0 | include_group->label, include_group->name); |
1282 | 0 | event_strlist_append(event, SETTINGS_EVENT_FILTER_NAME, |
1283 | 0 | filter_value); |
1284 | 0 | } |
1285 | 0 | return event; |
1286 | 0 | } |
1287 | | |
1288 | | static int |
1289 | | settings_apply_groups(struct settings_apply_ctx *ctx, |
1290 | | struct settings_mmap *mmap, |
1291 | | struct settings_mmap_block *block, |
1292 | | uint32_t include_filter_idx, const char **error_r) |
1293 | 0 | { |
1294 | 0 | struct event *event = NULL; |
1295 | 0 | const struct failure_context failure_ctx = { |
1296 | 0 | .type = LOG_TYPE_DEBUG, |
1297 | 0 | }; |
1298 | |
|
1299 | 0 | if (array_is_empty(&ctx->include_groups)) |
1300 | 0 | return 0; |
1301 | | |
1302 | | /* All group filters are at the end. When we see a non-group filter, |
1303 | | we can stop. */ |
1304 | 0 | int ret = 0; |
1305 | 0 | for (uint32_t i = block->filter_count; i > 0; ) { |
1306 | 0 | i--; |
1307 | 0 | uint32_t event_filter_idx; |
1308 | 0 | if (settings_mmap_get_filter_idx(mmap, block, i, |
1309 | 0 | &event_filter_idx, error_r) < 0) { |
1310 | 0 | ret = -1; |
1311 | 0 | break; |
1312 | 0 | } |
1313 | | |
1314 | 0 | if (!mmap->event_filters[event_filter_idx].is_group) |
1315 | 0 | break; |
1316 | | |
1317 | 0 | i_assert(i > include_filter_idx); |
1318 | 0 | struct settings_mmap_event_filter *set_filter = |
1319 | 0 | &mmap->event_filters[event_filter_idx]; |
1320 | 0 | i_assert(set_filter->filter != EVENT_FILTER_MATCH_ALWAYS); |
1321 | 0 | if (set_filter->filter == EVENT_FILTER_MATCH_NEVER) |
1322 | 0 | continue; |
1323 | | |
1324 | 0 | if (event == NULL) T_BEGIN { |
1325 | 0 | event = settings_group_event_create(ctx); |
1326 | 0 | } T_END; |
1327 | 0 | if (event_filter_match(set_filter->filter, event, &failure_ctx)) { |
1328 | 0 | if (settings_mmap_apply_filter(ctx, block, i, |
1329 | 0 | set_filter, |
1330 | 0 | error_r) < 0) { |
1331 | 0 | ret = -1; |
1332 | 0 | break; |
1333 | 0 | } |
1334 | 0 | } |
1335 | 0 | } |
1336 | | |
1337 | 0 | event_unref(&event); |
1338 | 0 | return ret; |
1339 | 0 | } |
1340 | | |
1341 | | static int |
1342 | | settings_mmap_apply(struct settings_apply_ctx *ctx, const char **error_r) |
1343 | 0 | { |
1344 | 0 | struct settings_mmap *mmap = ctx->instance->mmap; |
1345 | 0 | struct settings_mmap_block *block = |
1346 | 0 | hash_table_lookup(mmap->blocks, ctx->info->name); |
1347 | 0 | if (block == NULL) { |
1348 | 0 | *error_r = t_strdup_printf( |
1349 | 0 | "BUG: Configuration has no settings struct named '%s'", |
1350 | 0 | ctx->info->name); |
1351 | 0 | return -1; |
1352 | 0 | } |
1353 | | |
1354 | 0 | if (!block->settings_validated && |
1355 | 0 | (ctx->flags & SETTINGS_GET_NO_KEY_VALIDATION) == 0) { |
1356 | 0 | if (settings_mmap_validate(mmap, block, ctx->info, error_r) < 0) |
1357 | 0 | return -1; |
1358 | 0 | block->settings_validated = TRUE; |
1359 | 0 | } |
1360 | | |
1361 | 0 | const struct failure_context failure_ctx = { |
1362 | 0 | .type = LOG_TYPE_DEBUG, |
1363 | 0 | }; |
1364 | | |
1365 | | /* Go through the filters in reverse sorted order, so we always set the |
1366 | | setting just once, never overriding anything. A filter for the base |
1367 | | settings is expected to always exist. */ |
1368 | 0 | struct event *event = ctx->event; |
1369 | 0 | for (uint32_t i = block->filter_count; i > 0; ) { |
1370 | 0 | i--; |
1371 | 0 | uint32_t event_filter_idx; |
1372 | 0 | if (settings_mmap_get_filter_idx(mmap, block, i, |
1373 | 0 | &event_filter_idx, error_r) < 0) |
1374 | 0 | return -1; |
1375 | 0 | struct settings_mmap_event_filter *set_filter = |
1376 | 0 | &mmap->event_filters[event_filter_idx]; |
1377 | 0 | if (set_filter->filter == EVENT_FILTER_MATCH_NEVER) |
1378 | 0 | ; |
1379 | 0 | else if (set_filter->filter == EVENT_FILTER_MATCH_ALWAYS || |
1380 | 0 | event_filter_match(set_filter->filter, event, &failure_ctx)) { |
1381 | 0 | i_assert(!set_filter->is_group); |
1382 | 0 | if (settings_mmap_apply_filter(ctx, block, i, |
1383 | 0 | set_filter, error_r) < 0) |
1384 | 0 | return -1; |
1385 | | |
1386 | | /* Apply all group includes */ |
1387 | 0 | if (settings_apply_groups(ctx, mmap, block, |
1388 | 0 | i, error_r) < 0) |
1389 | 0 | return -1; |
1390 | 0 | } |
1391 | 0 | } |
1392 | 0 | return ctx->seen_filter ? 1 : 0; |
1393 | |
|
1394 | 0 | } |
1395 | | |
1396 | | static void settings_mmap_ref(struct settings_mmap *mmap) |
1397 | 0 | { |
1398 | 0 | i_assert(mmap->refcount > 0); |
1399 | | |
1400 | 0 | mmap->refcount++; |
1401 | 0 | } |
1402 | | |
1403 | | static void settings_mmap_unref(struct settings_mmap **_mmap) |
1404 | 0 | { |
1405 | 0 | struct settings_mmap *mmap = *_mmap; |
1406 | 0 | if (mmap == NULL) |
1407 | 0 | return; |
1408 | 0 | i_assert(mmap->refcount > 0); |
1409 | | |
1410 | 0 | *_mmap = NULL; |
1411 | 0 | if (--mmap->refcount > 0) |
1412 | 0 | return; |
1413 | | |
1414 | 0 | for (unsigned int i = 0; i < mmap->event_filters_count; i++) { |
1415 | 0 | if (mmap->event_filters[i].filter != EVENT_FILTER_MATCH_ALWAYS && |
1416 | 0 | mmap->event_filters[i].filter != EVENT_FILTER_MATCH_NEVER) |
1417 | 0 | event_filter_unref(&mmap->event_filters[i].filter); |
1418 | 0 | } |
1419 | 0 | hash_table_destroy(&mmap->blocks); |
1420 | |
|
1421 | 0 | if (munmap(mmap->mmap_base, mmap->mmap_size) < 0) |
1422 | 0 | i_error("munmap(<config>) failed: %m"); |
1423 | 0 | pool_unref(&mmap->pool); |
1424 | 0 | } |
1425 | | |
1426 | | int settings_read(struct settings_root *root, int fd, const char *path, |
1427 | | const char *service_name, const char *protocol_name, |
1428 | | enum settings_read_flags flags, |
1429 | | const char *const **specific_protocols_r, |
1430 | | const char **error_r) |
1431 | 0 | { |
1432 | 0 | pool_t pool = pool_alloconly_create("settings mmap", 1024*16); |
1433 | 0 | struct settings_mmap *mmap = p_new(pool, struct settings_mmap, 1); |
1434 | 0 | mmap->refcount = 1; |
1435 | 0 | mmap->pool = pool; |
1436 | 0 | mmap->mmap_base = mmap_ro_file(fd, &mmap->mmap_size); |
1437 | 0 | if (mmap->mmap_base == MAP_FAILED) |
1438 | 0 | i_fatal("Failed to read config: mmap(%s) failed: %m", path); |
1439 | 0 | if (mmap->mmap_size == 0) |
1440 | 0 | i_fatal("Failed to read config: %s file size is empty", path); |
1441 | | /* Remember the protocol for following settings lookups */ |
1442 | 0 | root->protocol_name = p_strdup(root->pool, protocol_name); |
1443 | |
|
1444 | 0 | settings_mmap_unref(&root->mmap); |
1445 | 0 | mmap->root = root; |
1446 | 0 | root->mmap = mmap; |
1447 | 0 | hash_table_create(&mmap->blocks, mmap->pool, 0, str_hash, strcmp); |
1448 | |
|
1449 | 0 | int ret = settings_mmap_parse(root->mmap, service_name, flags, |
1450 | 0 | specific_protocols_r, error_r); |
1451 | 0 | if (ret < 0 || |
1452 | 0 | (ret == 0 && (flags & SETTINGS_READ_CHECK_CACHE_TIMESTAMPS) != 0)) |
1453 | 0 | settings_mmap_unref(&root->mmap); |
1454 | 0 | return ret; |
1455 | 0 | } |
1456 | | |
1457 | | bool settings_has_mmap(struct settings_root *root) |
1458 | 0 | { |
1459 | 0 | return root->mmap != NULL; |
1460 | 0 | } |
1461 | | |
1462 | | static const char *settings_mmap_pool_get_name(pool_t pool) |
1463 | 0 | { |
1464 | 0 | struct settings_mmap_pool *mpool = |
1465 | 0 | container_of(pool, struct settings_mmap_pool, pool); |
1466 | |
|
1467 | 0 | return pool_get_name(mpool->parent_pool); |
1468 | 0 | } |
1469 | | |
1470 | | static void settings_mmap_pool_ref(pool_t pool) |
1471 | 0 | { |
1472 | 0 | struct settings_mmap_pool *mpool = |
1473 | 0 | container_of(pool, struct settings_mmap_pool, pool); |
1474 | |
|
1475 | 0 | i_assert(mpool->refcount > 0); |
1476 | 0 | mpool->refcount++; |
1477 | 0 | } |
1478 | | |
1479 | | static void settings_mmap_pool_unref(pool_t *pool) |
1480 | 0 | { |
1481 | 0 | struct settings_mmap_pool *mpool = |
1482 | 0 | container_of(*pool, struct settings_mmap_pool, pool); |
1483 | |
|
1484 | 0 | i_assert(mpool->refcount > 0); |
1485 | 0 | *pool = NULL; |
1486 | 0 | if (--mpool->refcount > 0) |
1487 | 0 | return; |
1488 | | |
1489 | 0 | DLLIST_REMOVE(&mpool->root->settings_pools, mpool); |
1490 | |
|
1491 | 0 | settings_mmap_unref(&mpool->mmap); |
1492 | 0 | pool_external_refs_unref(&mpool->pool); |
1493 | 0 | pool_unref(&mpool->parent_pool); |
1494 | 0 | } |
1495 | | |
1496 | | static void *settings_mmap_pool_malloc(pool_t pool, size_t size) |
1497 | 0 | { |
1498 | 0 | struct settings_mmap_pool *mpool = |
1499 | 0 | container_of(pool, struct settings_mmap_pool, pool); |
1500 | |
|
1501 | 0 | return p_malloc(mpool->parent_pool, size); |
1502 | 0 | } |
1503 | | |
1504 | | static void settings_mmap_pool_free(pool_t pool, void *mem) |
1505 | 0 | { |
1506 | 0 | struct settings_mmap_pool *mpool = |
1507 | 0 | container_of(pool, struct settings_mmap_pool, pool); |
1508 | |
|
1509 | 0 | p_free(mpool->parent_pool, mem); |
1510 | 0 | } |
1511 | | |
1512 | | static void *settings_mmap_pool_realloc(pool_t pool, void *mem, |
1513 | | size_t old_size, size_t new_size) |
1514 | 0 | { |
1515 | 0 | struct settings_mmap_pool *mpool = |
1516 | 0 | container_of(pool, struct settings_mmap_pool, pool); |
1517 | |
|
1518 | 0 | return p_realloc(mpool->parent_pool, mem, old_size, new_size); |
1519 | 0 | } |
1520 | | |
1521 | | static void settings_mmap_pool_clear(pool_t pool ATTR_UNUSED) |
1522 | 0 | { |
1523 | 0 | i_panic("settings_mmap_pool_clear() must not be called"); |
1524 | 0 | } |
1525 | | |
1526 | | static size_t settings_mmap_pool_get_max_easy_alloc_size(pool_t pool) |
1527 | 0 | { |
1528 | 0 | struct settings_mmap_pool *mpool = |
1529 | 0 | container_of(pool, struct settings_mmap_pool, pool); |
1530 | |
|
1531 | 0 | return p_get_max_easy_alloc_size(mpool->parent_pool); |
1532 | 0 | } |
1533 | | |
1534 | | static struct pool_vfuncs static_settings_mmap_pool_vfuncs = { |
1535 | | settings_mmap_pool_get_name, |
1536 | | |
1537 | | settings_mmap_pool_ref, |
1538 | | settings_mmap_pool_unref, |
1539 | | |
1540 | | settings_mmap_pool_malloc, |
1541 | | settings_mmap_pool_free, |
1542 | | |
1543 | | settings_mmap_pool_realloc, |
1544 | | |
1545 | | settings_mmap_pool_clear, |
1546 | | settings_mmap_pool_get_max_easy_alloc_size |
1547 | | }; |
1548 | | |
1549 | | static struct settings_mmap_pool * |
1550 | | settings_mmap_pool_create(struct settings_root *root, |
1551 | | struct settings_mmap *mmap, |
1552 | | const char *source_filename, |
1553 | | unsigned int source_linenum) |
1554 | 0 | { |
1555 | 0 | struct settings_mmap_pool *mpool; |
1556 | 0 | pool_t parent_pool = |
1557 | 0 | pool_alloconly_create("settings mmap pool", 256); |
1558 | |
|
1559 | 0 | mpool = p_new(parent_pool, struct settings_mmap_pool, 1); |
1560 | 0 | mpool->pool.v = &static_settings_mmap_pool_vfuncs; |
1561 | 0 | mpool->pool.alloconly_pool = TRUE; |
1562 | 0 | mpool->refcount = 1; |
1563 | 0 | mpool->parent_pool = parent_pool; |
1564 | 0 | mpool->root = root; |
1565 | 0 | mpool->mmap = mmap; |
1566 | 0 | mpool->source_filename = source_filename; |
1567 | 0 | mpool->source_linenum = source_linenum; |
1568 | 0 | if (mmap != NULL) |
1569 | 0 | settings_mmap_ref(mmap); |
1570 | |
|
1571 | 0 | DLLIST_PREPEND(&root->settings_pools, mpool); |
1572 | 0 | return mpool; |
1573 | 0 | } |
1574 | | |
1575 | | struct settings_var_expand_init_ctx { |
1576 | | ARRAY(const struct var_expand_table *) tables; |
1577 | | ARRAY(const struct var_expand_provider *) providers; |
1578 | | ARRAY(void *) contexts; |
1579 | | |
1580 | | var_expand_escape_func_t *escape_func; |
1581 | | void *escape_context; |
1582 | | }; |
1583 | | |
1584 | | static void |
1585 | | settings_var_expand_init_add(struct settings_var_expand_init_ctx *init_ctx, |
1586 | | const struct var_expand_params *params) |
1587 | 0 | { |
1588 | 0 | unsigned int need_contexts = 1; |
1589 | 0 | static const struct var_expand_table empty_table[] = { |
1590 | 0 | VAR_EXPAND_TABLE_END |
1591 | 0 | }; |
1592 | 0 | static const struct var_expand_table *empty_table_ptr = empty_table; |
1593 | 0 | static const struct var_expand_provider empty_provider[] = { |
1594 | 0 | VAR_EXPAND_TABLE_END |
1595 | 0 | }; |
1596 | 0 | static const struct var_expand_provider *empty_provider_ptr = empty_provider; |
1597 | |
|
1598 | 0 | if (params->table != NULL) { |
1599 | 0 | i_assert(params->tables_arr == NULL); |
1600 | 0 | array_push_back(&init_ctx->tables, ¶ms->table); |
1601 | 0 | } else if (params->tables_arr == NULL && params->providers_arr == NULL) { |
1602 | 0 | array_push_back(&init_ctx->tables, &empty_table_ptr); |
1603 | 0 | } |
1604 | 0 | if (params->providers != NULL) { |
1605 | 0 | i_assert(params->providers_arr == NULL); |
1606 | 0 | array_push_back(&init_ctx->providers, ¶ms->providers); |
1607 | 0 | } else if (params->tables_arr == NULL && params->providers_arr == NULL) { |
1608 | 0 | array_push_back(&init_ctx->providers, &empty_provider_ptr); |
1609 | 0 | } |
1610 | 0 | if (params->table != NULL || params->providers != NULL) |
1611 | 0 | array_push_back(&init_ctx->contexts, ¶ms->context); |
1612 | |
|
1613 | 0 | if (params->tables_arr != NULL || params->providers_arr != NULL) { |
1614 | 0 | unsigned int table_count = 0, prov_count = 0; |
1615 | 0 | const struct var_expand_table *tbl; |
1616 | 0 | const struct var_expand_provider *prov; |
1617 | | |
1618 | | /* Ensure that we get a table and provider so that contexts line up */ |
1619 | 0 | do { |
1620 | 0 | tbl = params->tables_arr != NULL ? |
1621 | 0 | params->tables_arr[table_count] : NULL; |
1622 | 0 | prov = params->providers_arr != NULL ? |
1623 | 0 | params->providers_arr[prov_count] : NULL; |
1624 | | |
1625 | | /* for each provider and table, there needs to be |
1626 | | a correspoding entry in both arrays to make sure |
1627 | | that when we go through them, we get the correct |
1628 | | context too. |
1629 | | */ |
1630 | 0 | if (tbl != NULL) { |
1631 | 0 | array_push_back(&init_ctx->tables, &tbl); |
1632 | 0 | table_count++; |
1633 | 0 | } else if (prov != NULL) { |
1634 | 0 | array_push_back(&init_ctx->tables, &empty_table_ptr); |
1635 | 0 | } |
1636 | 0 | if (prov != NULL) { |
1637 | 0 | array_push_back(&init_ctx->providers, &prov); |
1638 | 0 | prov_count++; |
1639 | 0 | } else if (tbl != NULL) { |
1640 | 0 | array_push_back(&init_ctx->providers, &empty_provider_ptr); |
1641 | 0 | } |
1642 | 0 | if (prov == NULL && tbl == NULL) |
1643 | 0 | break; |
1644 | 0 | } while (tbl != NULL || prov != NULL); |
1645 | |
|
1646 | 0 | if (I_MAX(table_count, prov_count) > need_contexts) |
1647 | 0 | need_contexts = I_MAX(table_count, prov_count); |
1648 | 0 | } |
1649 | |
|
1650 | 0 | if (params->escape_func != NULL) |
1651 | 0 | init_ctx->escape_func = params->escape_func; |
1652 | 0 | if (params->escape_context != NULL) |
1653 | 0 | init_ctx->escape_context = params->escape_context; |
1654 | | |
1655 | | /* if we have contexts, then copy them here to array */ |
1656 | 0 | if (params->contexts != NULL) { |
1657 | 0 | unsigned int count = 0; |
1658 | 0 | for (void *const *ctx = params->contexts; *ctx != var_expand_contexts_end; ctx++) { |
1659 | 0 | count++; |
1660 | 0 | array_push_back(&init_ctx->contexts, ctx); |
1661 | 0 | } |
1662 | 0 | i_assert(count == need_contexts); |
1663 | 0 | } else { |
1664 | | /* Make sure we push some context enough to pad the context stack |
1665 | | to match the number of tables and providers. */ |
1666 | 0 | for (unsigned int i = 1; i < need_contexts; i++) |
1667 | 0 | array_push_back(&init_ctx->contexts, ¶ms->context); |
1668 | 0 | } |
1669 | | |
1670 | | /* ensure everything is still good */ |
1671 | 0 | unsigned int num_tables = array_count(&init_ctx->tables); |
1672 | 0 | unsigned int num_provs = array_count(&init_ctx->providers); |
1673 | 0 | unsigned int num_ctx = array_count(&init_ctx->contexts); |
1674 | 0 | i_assert(I_MAX(num_tables, num_provs) == num_ctx); |
1675 | 0 | } |
1676 | | |
1677 | | static void |
1678 | | settings_var_expand_init(struct settings_apply_ctx *ctx) |
1679 | 0 | { |
1680 | 0 | struct event *event = ctx->event; |
1681 | 0 | struct settings_var_expand_init_ctx init_ctx; |
1682 | 0 | struct var_expand_params tmp_params; |
1683 | |
|
1684 | 0 | i_zero(&init_ctx); |
1685 | 0 | t_array_init(&init_ctx.tables, 4); |
1686 | 0 | t_array_init(&init_ctx.providers, 4); |
1687 | 0 | t_array_init(&init_ctx.contexts, 4); |
1688 | |
|
1689 | 0 | while (event != NULL) { |
1690 | 0 | settings_var_expand_t *callback = |
1691 | 0 | event_get_ptr(event, SETTINGS_EVENT_VAR_EXPAND_CALLBACK); |
1692 | 0 | if (callback != NULL) { |
1693 | 0 | void *callback_context = event_get_ptr(event, |
1694 | 0 | SETTINGS_EVENT_VAR_EXPAND_CALLBACK_CONTEXT); |
1695 | 0 | i_zero(&tmp_params); |
1696 | 0 | callback(callback_context, &tmp_params); |
1697 | 0 | settings_var_expand_init_add(&init_ctx, &tmp_params); |
1698 | 0 | } else { |
1699 | 0 | const struct var_expand_params *params = |
1700 | 0 | event_get_ptr(event, SETTINGS_EVENT_VAR_EXPAND_PARAMS); |
1701 | 0 | if (params != NULL) |
1702 | 0 | settings_var_expand_init_add(&init_ctx, params); |
1703 | 0 | } |
1704 | |
|
1705 | 0 | event = event_get_parent(event); |
1706 | 0 | } |
1707 | | |
1708 | | /* ensure everything is still good */ |
1709 | 0 | unsigned int num_tables = array_count(&init_ctx.tables); |
1710 | 0 | unsigned int num_provs = array_count(&init_ctx.providers); |
1711 | 0 | unsigned int num_ctx = array_count(&init_ctx.contexts); |
1712 | 0 | i_assert(I_MAX(num_tables, num_provs) == num_ctx); |
1713 | | |
1714 | 0 | array_append_zero(&init_ctx.tables); |
1715 | 0 | array_append_zero(&init_ctx.providers); |
1716 | 0 | void *context = VAR_EXPAND_CONTEXTS_END; |
1717 | 0 | array_push_back(&init_ctx.contexts, &context); |
1718 | |
|
1719 | 0 | ctx->var_params.tables_arr = array_front(&init_ctx.tables); |
1720 | 0 | ctx->var_params.providers_arr = array_front(&init_ctx.providers); |
1721 | 0 | ctx->var_params.contexts = array_front(&init_ctx.contexts); |
1722 | 0 | if (ctx->escape_func != NULL) { |
1723 | | /* settings_get_params()'s escape_func overrides all others */ |
1724 | 0 | ctx->var_params.escape_func = ctx->escape_func; |
1725 | 0 | ctx->var_params.escape_context = ctx->escape_context; |
1726 | 0 | } else { |
1727 | 0 | ctx->var_params.escape_func = init_ctx.escape_func; |
1728 | 0 | ctx->var_params.escape_context = init_ctx.escape_context; |
1729 | 0 | } |
1730 | 0 | ctx->var_params.event = ctx->event; |
1731 | 0 | } |
1732 | | |
1733 | | static int |
1734 | | settings_apply_override_cmp(const struct settings_apply_override *set1, |
1735 | | const struct settings_apply_override *set2) |
1736 | 0 | { |
1737 | | /* We want to sort the override similarly to filters in binary config, |
1738 | | i.e. primarily based on named list filter hierarchy length. This way |
1739 | | for example userdb namespace/inbox/mailbox/trash/auto is handled |
1740 | | before -o mailbox/trash/auto CLI override. */ |
1741 | 0 | int ret = (int)set2->set->filter_array_element_count - |
1742 | 0 | (int)set1->set->filter_array_element_count; |
1743 | 0 | if (ret != 0) |
1744 | 0 | return ret; |
1745 | | |
1746 | | /* Sort by the number of named (non-list) filters. Do this only if |
1747 | | both overrides have named [list] filters, because we want e.g. |
1748 | | -o mail_path to override the default sdbox/mail_path. */ |
1749 | 0 | if (set1->set->filter_array_element_count > 0 || |
1750 | 0 | (set1->set->filter_element_count > 0 && |
1751 | 0 | set2->set->filter_element_count > 0)) { |
1752 | 0 | ret = (int)set2->set->filter_element_count - |
1753 | 0 | (int)set1->set->filter_element_count; |
1754 | 0 | if (ret != 0) |
1755 | 0 | return ret; |
1756 | 0 | } |
1757 | | |
1758 | | /* Next, the override type determines the order. */ |
1759 | 0 | ret = (int)set2->set->type - (int)set1->set->type; |
1760 | 0 | if (ret != 0) |
1761 | 0 | return ret; |
1762 | | |
1763 | 0 | return set2->order - set1->order; |
1764 | 0 | } |
1765 | | |
1766 | | static int |
1767 | | settings_override_cmp_filter_order(const struct settings_override *set, |
1768 | | struct settings_mmap_event_filter *set_filter) |
1769 | 0 | { |
1770 | 0 | return (int)set->filter_array_element_count - |
1771 | 0 | (int)set_filter->named_list_filter_count; |
1772 | 0 | } |
1773 | | |
1774 | | static bool |
1775 | | settings_mmap_registered_lookup_key(const char *key, enum setting_type *type_r) |
1776 | 0 | { |
1777 | 0 | if (!array_is_created(&set_registered_infos)) |
1778 | 0 | return FALSE; |
1779 | | |
1780 | 0 | const struct setting_parser_info *info; |
1781 | 0 | array_foreach_elem(&set_registered_infos, info) { |
1782 | 0 | unsigned int key_idx; |
1783 | 0 | if (setting_parser_info_find_key(info, key, &key_idx)) { |
1784 | 0 | *type_r = info->defines[key_idx].type; |
1785 | 0 | return TRUE; |
1786 | 0 | } |
1787 | 0 | } |
1788 | 0 | return FALSE; |
1789 | 0 | } |
1790 | | |
1791 | | static bool |
1792 | | settings_mmap_lookup_key(struct settings_mmap *mmap, const char *key, |
1793 | | enum setting_type *type_r, const void **blocks_r) |
1794 | 0 | { |
1795 | 0 | if (mmap == NULL) |
1796 | 0 | return settings_mmap_registered_lookup_key(key, type_r); |
1797 | | |
1798 | | /* Look it up from hash table */ |
1799 | 0 | uint32_t key_hash = (mmap->all_keys_hash_key_prefix ^ str_stable_hash(key)) % |
1800 | 0 | mmap->all_keys_hash_count; |
1801 | 0 | uint32_t rel_offset = mmap->all_keys_hash[key_hash]; |
1802 | 0 | if (rel_offset == 0) |
1803 | 0 | return FALSE; |
1804 | | |
1805 | | /* Verify the name matches */ |
1806 | 0 | const char *name = CONST_PTR_OFFSET(mmap->all_keys_base, rel_offset); |
1807 | 0 | if (strcmp(name, key) != 0) |
1808 | 0 | return FALSE; |
1809 | | |
1810 | | /* Get setting type */ |
1811 | 0 | const void *p = name + strlen(name) + 1; |
1812 | 0 | uint32_t set_type; |
1813 | 0 | memcpy(&set_type, p, sizeof(set_type)); |
1814 | 0 | *type_r = set_type; |
1815 | 0 | *blocks_r = CONST_PTR_OFFSET(p, sizeof(set_type)); |
1816 | 0 | return TRUE; |
1817 | 0 | } |
1818 | | |
1819 | | static bool |
1820 | | settings_override_key_part_find(struct settings_mmap *mmap, const char **key, |
1821 | | const char *last_filter_key, |
1822 | | const char *last_filter_value, |
1823 | | enum setting_type *type_r, |
1824 | | const void **blocks_r) |
1825 | 0 | { |
1826 | 0 | if (last_filter_value != NULL) { |
1827 | 0 | i_assert(last_filter_key != NULL); |
1828 | 0 | const char *key_prefix = last_filter_key; |
1829 | | /* Try filter/name/key -> filter_name_key, and fallback to |
1830 | | filter_key. Do this before the non-prefixed check, so e.g. |
1831 | | inet_listener/imap/ssl won't try to change the global ssl |
1832 | | setting. */ |
1833 | 0 | const char *prefixed_key = |
1834 | 0 | t_strdup_printf("%s_%s_%s", key_prefix, |
1835 | 0 | last_filter_value, *key); |
1836 | 0 | if (settings_mmap_lookup_key(mmap, prefixed_key, type_r, blocks_r)) { |
1837 | 0 | *key = prefixed_key; |
1838 | 0 | return TRUE; |
1839 | 0 | } |
1840 | | |
1841 | 0 | prefixed_key = t_strdup_printf("%s_%s", key_prefix, *key); |
1842 | 0 | if (settings_mmap_lookup_key(mmap, prefixed_key, type_r, blocks_r)) { |
1843 | 0 | *key = prefixed_key; |
1844 | 0 | return TRUE; |
1845 | 0 | } |
1846 | 0 | } |
1847 | 0 | return settings_mmap_lookup_key(mmap, *key, type_r, blocks_r); |
1848 | 0 | } |
1849 | | |
1850 | | static bool |
1851 | | settings_key_part_find(struct settings_apply_ctx *ctx, const char **key, |
1852 | | const char *last_filter_key, |
1853 | | const char *last_filter_value, |
1854 | | unsigned int *key_idx_r) |
1855 | 0 | { |
1856 | 0 | if (last_filter_value != NULL) { |
1857 | 0 | i_assert(last_filter_key != NULL); |
1858 | 0 | const char *key_prefix = last_filter_key; |
1859 | | /* Try filter/name/key -> filter_name_key, and fallback to |
1860 | | filter_key. Do this before the non-prefixed check, so e.g. |
1861 | | inet_listener/imap/ssl won't try to change the global ssl |
1862 | | setting. */ |
1863 | 0 | const char *prefixed_key = |
1864 | 0 | t_strdup_printf("%s_%s_%s", key_prefix, |
1865 | 0 | last_filter_value, *key); |
1866 | 0 | if (setting_parser_info_find_key(ctx->info, prefixed_key, |
1867 | 0 | key_idx_r)) { |
1868 | 0 | *key = prefixed_key; |
1869 | 0 | return TRUE; |
1870 | 0 | } |
1871 | | |
1872 | 0 | prefixed_key = t_strdup_printf("%s_%s", key_prefix, *key); |
1873 | 0 | if (setting_parser_info_find_key(ctx->info, prefixed_key, |
1874 | 0 | key_idx_r)) { |
1875 | 0 | *key = prefixed_key; |
1876 | 0 | return TRUE; |
1877 | 0 | } |
1878 | 0 | } |
1879 | 0 | return setting_parser_info_find_key(ctx->info, *key, key_idx_r); |
1880 | 0 | } |
1881 | | |
1882 | | static int |
1883 | | settings_override_set_blocks(struct settings_mmap *mmap, |
1884 | | struct settings_override *set, |
1885 | | const char **error_r) |
1886 | 0 | { |
1887 | 0 | enum setting_type set_type; |
1888 | 0 | const char *key = t_strcut(set->key, '/'); |
1889 | 0 | const void *blocks; |
1890 | |
|
1891 | 0 | if (key[0] == SETTINGS_INCLUDE_GROUP_PREFIX) { |
1892 | | /* Group override - we don't currently support optimizing the |
1893 | | block name checks. */ |
1894 | 0 | return 1; |
1895 | 0 | } |
1896 | 0 | if (mmap == NULL) { |
1897 | | /* Using only built-in settings - skip optimizations. */ |
1898 | 0 | return 1; |
1899 | 0 | } |
1900 | 0 | if (!settings_override_key_part_find(mmap, &key, set->last_filter_key, |
1901 | 0 | set->last_filter_value, &set_type, |
1902 | 0 | &blocks)) { |
1903 | 0 | *error_r = t_strdup_printf("Unknown setting '%s' (in %s)", |
1904 | 0 | key, set->orig_key); |
1905 | 0 | return -1; |
1906 | 0 | } |
1907 | | |
1908 | 0 | uint32_t i, count, block_idx; |
1909 | 0 | memcpy(&count, blocks, sizeof(count)); |
1910 | 0 | blocks = CONST_PTR_OFFSET(blocks, sizeof(count)); |
1911 | |
|
1912 | 0 | if (count == 0) { |
1913 | 0 | *error_r = t_strdup_printf("BUG: Setting %s has no blocks", key); |
1914 | 0 | return -1; |
1915 | 0 | } |
1916 | | |
1917 | 0 | const char **block_names = p_new(set->pool, const char *, count + 1); |
1918 | 0 | for (i = 0; i < count; i++) { |
1919 | 0 | memcpy(&block_idx, blocks, sizeof(block_idx)); |
1920 | 0 | blocks = CONST_PTR_OFFSET(blocks, sizeof(block_idx)); |
1921 | |
|
1922 | 0 | if (block_idx >= mmap->block_names_count) { |
1923 | 0 | *error_r = t_strdup_printf( |
1924 | 0 | "BUG: Setting %s record points to nonexistent block index %u", |
1925 | 0 | key, block_idx); |
1926 | 0 | return -1; |
1927 | 0 | } |
1928 | | |
1929 | 0 | block_names[i] = |
1930 | 0 | CONST_PTR_OFFSET(mmap->all_keys_base, |
1931 | 0 | mmap->block_names_rel_offsets[block_idx]); |
1932 | 0 | } |
1933 | 0 | set->block_names = block_names; |
1934 | 0 | return 1; |
1935 | 0 | } |
1936 | | |
1937 | | static bool setting_override_match_info(struct settings_apply_ctx *ctx, |
1938 | | struct settings_override *set) |
1939 | 0 | { |
1940 | 0 | if (set->block_names == NULL) { |
1941 | | /* Optimization disabled. */ |
1942 | 0 | return TRUE; |
1943 | 0 | } |
1944 | 0 | for (unsigned int i = 0; set->block_names[i] != NULL; i++) { |
1945 | 0 | if (strcmp(set->block_names[i], ctx->info->name) == 0) |
1946 | 0 | return TRUE; |
1947 | 0 | } |
1948 | 0 | return FALSE; |
1949 | 0 | } |
1950 | | |
1951 | | static int |
1952 | | settings_override_filter_match(struct settings_apply_ctx *ctx, |
1953 | | struct settings_override *set, |
1954 | | const char **error_r) |
1955 | 0 | { |
1956 | 0 | const struct failure_context failure_ctx = { |
1957 | 0 | .type = LOG_TYPE_DEBUG |
1958 | 0 | }; |
1959 | 0 | const char *p, *error; |
1960 | | |
1961 | | /* check if the filter is already set */ |
1962 | 0 | if (set->filter != NULL) { |
1963 | 0 | if (set->filter == EVENT_FILTER_MATCH_NEVER) |
1964 | 0 | return 0; |
1965 | 0 | if (!setting_override_match_info(ctx, set)) |
1966 | 0 | return 0; |
1967 | 0 | if (set->filter == EVENT_FILTER_MATCH_ALWAYS) |
1968 | 0 | return 1; |
1969 | 0 | return event_filter_match(set->filter, ctx->event, |
1970 | 0 | &failure_ctx) ? 1 : 0; |
1971 | 0 | } |
1972 | | |
1973 | 0 | if (str_begins(set->key, "*"SETTINGS_SEPARATOR_S, &set->key)) { |
1974 | | /* always match, also for any named list filters */ |
1975 | 0 | int ret = settings_override_set_blocks(ctx->instance->mmap, |
1976 | 0 | set, error_r); |
1977 | 0 | if (ret <= 0) |
1978 | 0 | return ret; |
1979 | | /* Sort this as the first */ |
1980 | 0 | set->filter_array_element_count = INT_MAX; |
1981 | 0 | set->filter = EVENT_FILTER_MATCH_ALWAYS; |
1982 | 0 | return setting_override_match_info(ctx, set) ? 1 : 0; |
1983 | 0 | } |
1984 | | |
1985 | 0 | string_t *filter_string = NULL; |
1986 | 0 | const char *last_filter_key = NULL; |
1987 | 0 | const char *last_filter_value = NULL; |
1988 | 0 | while ((p = strchr(set->key, SETTINGS_SEPARATOR)) != NULL) { |
1989 | 0 | const char *part = t_strdup_until(set->key, p); |
1990 | 0 | enum setting_type set_type; |
1991 | 0 | const void *blocks ATTR_UNUSED; |
1992 | |
|
1993 | 0 | if (!settings_override_key_part_find(ctx->instance->mmap, &part, |
1994 | 0 | last_filter_key, |
1995 | 0 | last_filter_value, |
1996 | 0 | &set_type, &blocks)) { |
1997 | 0 | *error_r = t_strdup_printf("Unknown setting '%s' (in %s)", |
1998 | 0 | part, set->orig_key); |
1999 | 0 | return -1; |
2000 | 0 | } |
2001 | | |
2002 | 0 | if (set_type == SET_STRLIST || set_type == SET_BOOLLIST) |
2003 | 0 | break; |
2004 | | |
2005 | 0 | if (filter_string == NULL) |
2006 | 0 | filter_string = t_str_new(64); |
2007 | 0 | else |
2008 | 0 | str_append(filter_string, " AND "); |
2009 | 0 | switch (set_type) { |
2010 | 0 | case SET_FILTER_NAME: |
2011 | 0 | last_filter_key = part; |
2012 | 0 | last_filter_value = NULL; |
2013 | 0 | str_printfa(filter_string, SETTINGS_EVENT_FILTER_NAME"=\"%s\"", |
2014 | 0 | wildcard_str_escape(last_filter_key)); |
2015 | 0 | set->filter_element_count++; |
2016 | 0 | break; |
2017 | 0 | case SET_FILTER_ARRAY: { |
2018 | 0 | const char *value = p + 1; |
2019 | 0 | p = strchr(value, SETTINGS_SEPARATOR); |
2020 | 0 | if (p == NULL) { |
2021 | 0 | *error_r = t_strdup_printf( |
2022 | 0 | "Setting override %s is missing filter name child element ('/child' expected)", |
2023 | 0 | set->key); |
2024 | 0 | return -1; |
2025 | 0 | } |
2026 | 0 | last_filter_key = part; |
2027 | 0 | last_filter_value = t_strdup_until(value, p); |
2028 | 0 | str_printfa(filter_string, SETTINGS_EVENT_FILTER_NAME"=\"%s/%s\"", |
2029 | 0 | last_filter_key, wildcard_str_escape(settings_section_escape(last_filter_value))); |
2030 | 0 | set->filter_array_element_count++; |
2031 | 0 | break; |
2032 | 0 | } |
2033 | 0 | default: |
2034 | 0 | *error_r = t_strdup_printf( |
2035 | 0 | "Setting override %s type doesn't support child elements in the path ('/' not expected)", |
2036 | 0 | set->key); |
2037 | 0 | return -1; |
2038 | 0 | } |
2039 | 0 | set->key = p + 1; |
2040 | 0 | } |
2041 | | |
2042 | 0 | if (set->last_filter_key != last_filter_key) |
2043 | 0 | set->last_filter_key = p_strdup(set->pool, last_filter_key); |
2044 | 0 | if (set->last_filter_value != last_filter_value) |
2045 | 0 | set->last_filter_value = p_strdup(set->pool, last_filter_value); |
2046 | |
|
2047 | 0 | int ret = settings_override_set_blocks(ctx->instance->mmap, set, error_r); |
2048 | 0 | if (ret <= 0) |
2049 | 0 | return ret; |
2050 | | |
2051 | 0 | if (filter_string == NULL) |
2052 | 0 | set->filter = EVENT_FILTER_MATCH_ALWAYS; |
2053 | 0 | else { |
2054 | 0 | set->filter = event_filter_create_with_pool(set->pool); |
2055 | 0 | if (event_filter_parse_case_sensitive(str_c(filter_string), |
2056 | 0 | set->filter, &error) < 0) { |
2057 | 0 | i_panic("BUG: Failed to create event filter filter for %s: %s (%s)", |
2058 | 0 | set->orig_key, error, str_c(filter_string)); |
2059 | 0 | } |
2060 | 0 | pool_ref(set->pool); |
2061 | 0 | } |
2062 | | |
2063 | 0 | if (!setting_override_match_info(ctx, set)) |
2064 | 0 | return 0; |
2065 | 0 | return (set->filter == EVENT_FILTER_MATCH_ALWAYS || |
2066 | 0 | event_filter_match(set->filter, ctx->event, &failure_ctx)) ? 1 : 0; |
2067 | 0 | } |
2068 | | |
2069 | | static bool settings_override_find_filter_check(struct settings_apply_ctx *ctx, |
2070 | | struct settings_override *set) |
2071 | 0 | { |
2072 | 0 | static const struct failure_context failure_ctx = { |
2073 | 0 | .type = LOG_TYPE_DEBUG |
2074 | 0 | }; |
2075 | 0 | return ctx->filter_key != NULL && set->last_filter_key != NULL && |
2076 | 0 | strcmp(ctx->filter_key, set->last_filter_key) == 0 && |
2077 | 0 | null_strcmp(ctx->filter_value, set->last_filter_value) == 0 && |
2078 | | |
2079 | 0 | set->filter != EVENT_FILTER_MATCH_NEVER && |
2080 | 0 | set->filter != EVENT_FILTER_MATCH_ALWAYS && |
2081 | 0 | event_filter_match(set->filter, ctx->event, &failure_ctx); |
2082 | 0 | } |
2083 | | |
2084 | | static bool settings_overrides_find_filter(struct settings_apply_ctx *ctx) |
2085 | 0 | { |
2086 | | /* We've optimized skipping overrides that don't match the current |
2087 | | info. Now it looks like a filter lookup didn't find any settings. |
2088 | | But it's possible that there are settings for the filter in |
2089 | | overrides, just for different infos. Check if this is the case. */ |
2090 | 0 | struct settings_override *set; |
2091 | 0 | if (array_is_created(&ctx->instance->overrides)) { |
2092 | 0 | array_foreach_modifiable(&ctx->instance->overrides, set) { |
2093 | 0 | if (settings_override_find_filter_check(ctx, set)) |
2094 | 0 | return TRUE; |
2095 | 0 | } |
2096 | 0 | } |
2097 | 0 | if (array_is_created(&ctx->root->overrides)) { |
2098 | 0 | array_foreach_modifiable(&ctx->root->overrides, set) { |
2099 | 0 | if (settings_override_find_filter_check(ctx, set)) |
2100 | 0 | return TRUE; |
2101 | 0 | } |
2102 | 0 | } |
2103 | 0 | return FALSE; |
2104 | 0 | } |
2105 | | |
2106 | | static int |
2107 | | settings_override_get_value(struct settings_apply_ctx *ctx, |
2108 | | const struct settings_override *set, |
2109 | | const char **_key, unsigned int *key_idx_r, |
2110 | | const char **value_r) |
2111 | 0 | { |
2112 | 0 | const char *key = *_key; |
2113 | 0 | unsigned int key_idx = UINT_MAX; |
2114 | |
|
2115 | 0 | if (!settings_key_part_find(ctx, &key, set->last_filter_key, |
2116 | 0 | set->last_filter_value, &key_idx)) |
2117 | 0 | key_idx = UINT_MAX; |
2118 | |
|
2119 | 0 | if (key_idx == UINT_MAX) |
2120 | 0 | return 0; |
2121 | | |
2122 | | /* remove alias */ |
2123 | 0 | const char *list = strchr(key, SETTINGS_SEPARATOR); |
2124 | 0 | if (list == NULL) |
2125 | 0 | key = ctx->info->defines[key_idx].key; |
2126 | 0 | else |
2127 | 0 | key = t_strconcat(ctx->info->defines[key_idx].key, list, NULL); |
2128 | |
|
2129 | 0 | if (!set->append || |
2130 | 0 | !setting_type_is_str_vars(ctx->info->defines[key_idx].type)) { |
2131 | 0 | if (set->append && ctx->info->defines[key_idx].type != SET_FILTER_ARRAY) |
2132 | 0 | *_key = t_strconcat(key, SETTINGS_APPEND_KEY_SUFFIX, NULL); |
2133 | 0 | else |
2134 | 0 | *_key = key; |
2135 | 0 | *key_idx_r = key_idx; |
2136 | |
|
2137 | 0 | const char *inline_value; |
2138 | 0 | if (!str_begins(set->value, SET_FILE_INLINE_PREFIX, |
2139 | 0 | &inline_value)) |
2140 | 0 | *value_r = set->value; |
2141 | 0 | else |
2142 | 0 | *value_r = t_strconcat("\n", inline_value, NULL); |
2143 | 0 | return 1; |
2144 | 0 | } |
2145 | | |
2146 | 0 | const char *const *strp = |
2147 | 0 | PTR_OFFSET(ctx->set_struct, ctx->info->defines[key_idx].offset); |
2148 | 0 | *_key = key; |
2149 | 0 | *key_idx_r = key_idx; |
2150 | 0 | *value_r = t_strconcat(*strp, set->value, NULL); |
2151 | 0 | return 1; |
2152 | 0 | } |
2153 | | |
2154 | | static int |
2155 | | settings_instance_override_add_default(struct settings_apply_ctx *ctx, |
2156 | | struct settings_override *array_set, |
2157 | | unsigned int key_idx, |
2158 | | const char **error_r) |
2159 | 0 | { |
2160 | 0 | const struct setting_define *array_def = |
2161 | 0 | &ctx->info->defines[key_idx]; |
2162 | |
|
2163 | 0 | i_assert(array_set->overrides_array != NULL); |
2164 | 0 | array_set->array_default_added = TRUE; |
2165 | | |
2166 | | /* As an example we could be here because of |
2167 | | -o namespace/inbox/mailbox+=foo,bar override (= array_set): |
2168 | | |
2169 | | * array_def points to mailbox's setting_define |
2170 | | * We're going to add namespace/inbox/mailbox/foo/mailbox_name=foo |
2171 | | and namespace/inbox/mailbox/bar/mailbox_name=bar to avoid having |
2172 | | to explicitly define them. |
2173 | | * These are SETTINGS_OVERRIDE_TYPE_DEFAULT, so they will be used |
2174 | | only if the configuration file didn't already specify a different |
2175 | | mailbox_name for these filters. |
2176 | | * We're going to have to build the event filter ourself here, |
2177 | | because it's most likely too late for it to be left later. The |
2178 | | next settings_get_filter() could be trying to look up this filter |
2179 | | and it won't know enough to build the event filter. |
2180 | | */ |
2181 | 0 | const char *const *items = |
2182 | 0 | t_strsplit(array_set->value, SETTINGS_FILTER_ARRAY_SEPARATORS); |
2183 | 0 | for (unsigned int i = 0; items[i] != NULL; i++) { |
2184 | 0 | struct settings_override *set = |
2185 | 0 | array_append_space(array_set->overrides_array); |
2186 | 0 | set->pool = array_set->pool; |
2187 | 0 | set->type = SETTINGS_OVERRIDE_TYPE_DEFAULT; |
2188 | |
|
2189 | 0 | set->orig_key = p_strdup_printf(set->pool, "%s/%s/%s", |
2190 | 0 | array_set->orig_key, items[i], |
2191 | 0 | array_def->filter_array_field_name); |
2192 | | /* We're building the full event filter, so jump forward in |
2193 | | the key path until the last field. */ |
2194 | 0 | set->key = array_def->filter_array_field_name; |
2195 | 0 | set->filter_array_element_count = |
2196 | 0 | array_set->filter_array_element_count; |
2197 | 0 | set->filter_element_count = |
2198 | 0 | array_set->filter_element_count; |
2199 | 0 | set->value = p_strdup(set->pool, items[i]); |
2200 | | |
2201 | | /* Get the final key we want to use in the event filter. |
2202 | | For example "mailbox", which in this special case gets |
2203 | | translated to "mailbox_subname". */ |
2204 | 0 | const char *filter_key = strrchr(array_set->orig_key, '/'); |
2205 | 0 | if (filter_key != NULL) |
2206 | 0 | filter_key++; |
2207 | 0 | else |
2208 | 0 | filter_key = array_set->orig_key; |
2209 | | |
2210 | | /* Build the final event filter. */ |
2211 | 0 | const char *filter_string = t_strdup_printf( |
2212 | 0 | SETTINGS_EVENT_FILTER_NAME"=\"%s/%s\"", |
2213 | 0 | filter_key, wildcard_str_escape(settings_section_escape(items[i]))); |
2214 | 0 | const char *error; |
2215 | |
|
2216 | 0 | if (settings_override_set_blocks(ctx->instance->mmap, |
2217 | 0 | set, error_r) < 0) |
2218 | 0 | return -1; |
2219 | | |
2220 | 0 | set->filter = event_filter_create_with_pool(set->pool); |
2221 | 0 | pool_ref(set->pool); |
2222 | 0 | if (event_filter_parse_case_sensitive(filter_string, |
2223 | 0 | set->filter, &error) < 0) { |
2224 | 0 | i_panic("BUG: Failed to create event filter for %s: %s (%s)", |
2225 | 0 | set->orig_key, error, filter_string); |
2226 | 0 | } |
2227 | 0 | if (array_set->filter != EVENT_FILTER_MATCH_ALWAYS) { |
2228 | | /* Merge the final event filter with the parent |
2229 | | SET_FILTER_ARRAY's event filter. */ |
2230 | 0 | event_filter_merge(set->filter, array_set->filter, |
2231 | 0 | EVENT_FILTER_MERGE_OP_AND); |
2232 | 0 | } |
2233 | 0 | } |
2234 | 0 | return 0; |
2235 | 0 | } |
2236 | | |
2237 | | static int settings_apply_add_override(struct settings_apply_ctx *ctx, |
2238 | | struct settings_override *set, |
2239 | | const char **error_r) |
2240 | 0 | { |
2241 | 0 | int ret; |
2242 | 0 | T_BEGIN { |
2243 | 0 | ret = settings_override_filter_match(ctx, set, error_r); |
2244 | 0 | } T_END_PASS_STR_IF(ret < 0, error_r); |
2245 | 0 | if (ret <= 0) |
2246 | 0 | return ret; |
2247 | | |
2248 | 0 | struct settings_apply_override *override = |
2249 | 0 | array_append_space(&ctx->overrides); |
2250 | 0 | override->set = set; |
2251 | 0 | override->order = array_count(&ctx->overrides); |
2252 | 0 | return 1; |
2253 | 0 | } |
2254 | | |
2255 | | static void |
2256 | | settings_instance_overrides_add_filters(struct settings_apply_ctx *ctx, |
2257 | | const struct setting_parser_info *info) |
2258 | 0 | { |
2259 | 0 | const struct setting_keyvalue *defaults = info->default_settings; |
2260 | 0 | struct settings_override *set; |
2261 | 0 | const char *error; |
2262 | 0 | int ret; |
2263 | |
|
2264 | 0 | if (defaults == NULL) |
2265 | 0 | return; |
2266 | 0 | for (unsigned int i = 0; defaults[i].key != NULL; i++) { |
2267 | 0 | set = p_new(ctx->temp_pool, struct settings_override, 1); |
2268 | 0 | set->pool = ctx->temp_pool; |
2269 | 0 | set->type = SETTINGS_OVERRIDE_TYPE_DEFAULT; |
2270 | 0 | set->key = set->orig_key = defaults[i].key; |
2271 | 0 | set->value = defaults[i].value; |
2272 | 0 | if ((ret = settings_apply_add_override(ctx, set, &error)) < 0) |
2273 | 0 | i_panic("Applying default settings failed: %s", error); |
2274 | 0 | if (ret == 0) |
2275 | 0 | settings_override_free(set); |
2276 | 0 | } |
2277 | 0 | } |
2278 | | |
2279 | | static int |
2280 | | settings_instance_override_init(struct settings_apply_ctx *ctx, |
2281 | | const char **error_r) |
2282 | 0 | { |
2283 | 0 | struct settings_override *set; |
2284 | 0 | bool have_2nd_defaults = FALSE; |
2285 | |
|
2286 | 0 | if (ctx->instance->mmap == NULL) { |
2287 | | /* For unit tests, auto-register the looked up info */ |
2288 | 0 | settings_info_register(ctx->info); |
2289 | 0 | } |
2290 | |
|
2291 | 0 | t_array_init(&ctx->overrides, 64); |
2292 | 0 | if (array_is_created(&ctx->instance->overrides)) { |
2293 | 0 | array_foreach_modifiable(&ctx->instance->overrides, set) { |
2294 | 0 | if (set->type == SETTINGS_OVERRIDE_TYPE_2ND_DEFAULT) |
2295 | 0 | have_2nd_defaults = TRUE; |
2296 | 0 | if (settings_apply_add_override(ctx, set, error_r) < 0) |
2297 | 0 | return -1; |
2298 | 0 | } |
2299 | 0 | } |
2300 | 0 | if (array_is_created(&ctx->root->overrides)) { |
2301 | 0 | array_foreach_modifiable(&ctx->root->overrides, set) { |
2302 | 0 | if (set->type == SETTINGS_OVERRIDE_TYPE_2ND_DEFAULT) |
2303 | 0 | have_2nd_defaults = TRUE; |
2304 | 0 | if (settings_apply_add_override(ctx, set, error_r) < 0) |
2305 | 0 | return -1; |
2306 | 0 | } |
2307 | 0 | } |
2308 | 0 | if ((ctx->instance->mmap == NULL || have_2nd_defaults) && |
2309 | 0 | array_is_created(&set_registered_infos)) { |
2310 | | /* a) No configuration - default_settings won't be |
2311 | | applied unless we add them here also. This isn't for any |
2312 | | production use, so performance doesn't matter. |
2313 | | |
2314 | | b) SETTINGS_OVERRIDE_TYPE_2ND_DEFAULT has been used. The |
2315 | | idea is that unless SETTINGS_OVERRIDE_TYPE_2ND_CLI_PARAM has |
2316 | | overridden it, the value should be the default. This works |
2317 | | for the defaults in the struct without extra code, but if |
2318 | | the default comes from setting_parser_info.default_settings, |
2319 | | we'll need to add those into overrides as |
2320 | | SETTINGS_OVERRIDE_TYPE_DEFAULT. We really only need to |
2321 | | add those specific keys that have |
2322 | | SETTINGS_OVERRIDE_TYPE_2ND_DEFAULT, but it's a bit |
2323 | | troublesome to check which ones have it. It's not harmful |
2324 | | to have other settings there as well, since for other |
2325 | | settings the SETTINGS_OVERRIDE_TYPE_DEFAULT overrides are |
2326 | | never even processed, because the final settings come from |
2327 | | the binary config. */ |
2328 | 0 | const struct setting_parser_info *info; |
2329 | 0 | ctx->temp_pool = pool_alloconly_create("settings temp pool", 256); |
2330 | 0 | array_foreach_elem(&set_registered_infos, info) |
2331 | 0 | settings_instance_overrides_add_filters(ctx, info); |
2332 | 0 | } |
2333 | | /* sort overrides so that the most specific ones are first */ |
2334 | 0 | array_sort(&ctx->overrides, settings_apply_override_cmp); |
2335 | 0 | return 0; |
2336 | 0 | } |
2337 | | |
2338 | | static void |
2339 | | settings_apply_ctx_overrides_deinit(struct settings_apply_ctx *ctx) |
2340 | 0 | { |
2341 | 0 | struct settings_apply_override *set; |
2342 | |
|
2343 | 0 | array_foreach_modifiable(&ctx->overrides, set) { |
2344 | 0 | if (set->set != NULL && set->set->pool == ctx->temp_pool) |
2345 | 0 | settings_override_free(set->set); |
2346 | 0 | } |
2347 | 0 | } |
2348 | | |
2349 | | static void |
2350 | | settings_include_group_add_or_update(ARRAY_TYPE(settings_group) *include_groups, |
2351 | | const struct settings_override *set) |
2352 | 0 | { |
2353 | 0 | struct settings_group *include_group; |
2354 | | |
2355 | | /* we assume that the key/value pointers in "set" exist at least as |
2356 | | long as "includes" array */ |
2357 | 0 | array_foreach_modifiable(include_groups, include_group) { |
2358 | 0 | if (strcmp(include_group->label, set->key + 1) == 0) { |
2359 | 0 | include_group->name = set->value; |
2360 | 0 | return; |
2361 | 0 | } |
2362 | 0 | } |
2363 | 0 | include_group = array_append_space(include_groups); |
2364 | 0 | include_group->label = set->key + 1; |
2365 | 0 | include_group->name = set->value; |
2366 | 0 | } |
2367 | | |
2368 | | static int |
2369 | | settings_instance_override(struct settings_apply_ctx *ctx, |
2370 | | struct settings_mmap_event_filter *set_filter, |
2371 | | bool *defaults, const char **error_r) |
2372 | 0 | { |
2373 | 0 | struct settings_apply_override *override; |
2374 | 0 | struct settings_override *set; |
2375 | 0 | enum setting_apply_flags apply_flags = SETTING_APPLY_FLAG_OVERRIDE | |
2376 | 0 | ((ctx->flags & SETTINGS_GET_FLAG_NO_EXPAND) == 0 ? 0 : |
2377 | 0 | SETTING_APPLY_FLAG_NO_EXPAND); |
2378 | |
|
2379 | 0 | bool seen_defaults = FALSE; |
2380 | 0 | bool seen_filter = FALSE; |
2381 | 0 | array_foreach_modifiable(&ctx->overrides, override) { |
2382 | 0 | if (override->set == NULL) |
2383 | 0 | continue; /* already applied */ |
2384 | 0 | set = override->set; |
2385 | |
|
2386 | 0 | unsigned int key_idx; |
2387 | 0 | int ret; |
2388 | | |
2389 | | /* If we're being called while applying filters, only apply |
2390 | | the overrides that have a matching filter. This preserves |
2391 | | the expected order in which settings are applied. */ |
2392 | 0 | if (set_filter->filter != EVENT_FILTER_MATCH_ALWAYS && |
2393 | 0 | settings_override_cmp_filter_order(set, set_filter) < 0) |
2394 | 0 | break; |
2395 | | |
2396 | 0 | if (set->type == SETTINGS_OVERRIDE_TYPE_DEFAULT) { |
2397 | 0 | seen_defaults = TRUE; |
2398 | 0 | if (!*defaults) |
2399 | 0 | continue; |
2400 | 0 | } |
2401 | | |
2402 | 0 | if (ctx->filter_key != NULL && set->last_filter_key != NULL && |
2403 | 0 | strcmp(ctx->filter_key, set->last_filter_key) == 0 && |
2404 | 0 | null_strcmp(ctx->filter_value, set->last_filter_value) == 0) |
2405 | 0 | seen_filter = TRUE; |
2406 | | |
2407 | | /* Set key only after settings_override_filter_match() has |
2408 | | potentially changed it. */ |
2409 | 0 | const char *key = set->key, *value; |
2410 | 0 | if (key[0] == SETTINGS_INCLUDE_GROUP_PREFIX) { |
2411 | 0 | settings_include_group_add_or_update( |
2412 | 0 | &ctx->include_groups, set); |
2413 | 0 | continue; |
2414 | 0 | } |
2415 | 0 | ret = settings_override_get_value(ctx, set, &key, |
2416 | 0 | &key_idx, &value); |
2417 | 0 | if (ret < 0) |
2418 | 0 | return -1; |
2419 | 0 | if (ret == 0) { |
2420 | | /* setting doesn't exist in this info */ |
2421 | 0 | continue; |
2422 | 0 | } |
2423 | 0 | bool value_needs_dup = (value != set->value); |
2424 | 0 | bool track_seen = TRUE; |
2425 | 0 | if (ctx->info->defines[key_idx].type == SET_FILTER_ARRAY) { |
2426 | 0 | track_seen = FALSE; |
2427 | | /* Last part is a named list filter. Permanently store |
2428 | | pointer to the definition for this setting, which |
2429 | | allows future override lookups to fill the default |
2430 | | array name setting. */ |
2431 | 0 | if (!set->array_default_added) { |
2432 | 0 | if (settings_instance_override_add_default(ctx, |
2433 | 0 | set, key_idx, error_r) < 0) |
2434 | 0 | return -1; |
2435 | 0 | } |
2436 | 0 | } else if (ctx->info->defines[key_idx].type == SET_STRLIST || |
2437 | 0 | ctx->info->defines[key_idx].type == SET_BOOLLIST) { |
2438 | 0 | const char *suffix; |
2439 | 0 | if (!str_begins(key, ctx->info->defines[key_idx].key, &suffix)) |
2440 | 0 | i_unreached(); |
2441 | 0 | if (suffix[0] == '/' && |
2442 | 0 | set->type == SETTINGS_OVERRIDE_TYPE_DEFAULT) { |
2443 | | /* Expand %variables in the key, but only for |
2444 | | default overrides - the same way the value is |
2445 | | expanded below. -o and userdb overrides keep |
2446 | | their keys (and values) literal. Done before |
2447 | | has_key() so dedup uses the final key. */ |
2448 | 0 | const char *exp_key = suffix + 1, *error; |
2449 | 0 | if (settings_var_expand(ctx, key_idx, &exp_key, |
2450 | 0 | &error) < 0) { |
2451 | 0 | *error_r = t_strdup_printf( |
2452 | 0 | "Failed to expand default setting %s key variables: %s", |
2453 | 0 | key, error); |
2454 | 0 | return -1; |
2455 | 0 | } |
2456 | 0 | key = t_strconcat(ctx->info->defines[key_idx].key, |
2457 | 0 | "/", exp_key, NULL); |
2458 | 0 | suffix = key + strlen(ctx->info->defines[key_idx].key); |
2459 | 0 | } |
2460 | 0 | if (suffix[0] != '/') { |
2461 | | /* replace full boollist setting |
2462 | | (invalid for strlist) */ |
2463 | 0 | i_assert(suffix[0] == '\0'); |
2464 | 0 | } else if (settings_parse_list_has_key(ctx->parser, |
2465 | 0 | key_idx, |
2466 | 0 | settings_section_unescape(suffix + 1))) |
2467 | 0 | continue; |
2468 | 0 | else |
2469 | 0 | track_seen = FALSE; |
2470 | 0 | } |
2471 | 0 | if (track_seen) { |
2472 | 0 | enum set_seen_type *set_seenp = |
2473 | 0 | array_idx_get_space(&ctx->set_seen, key_idx); |
2474 | 0 | switch (*set_seenp) { |
2475 | 0 | case SET_SEEN_NO: |
2476 | 0 | break; |
2477 | 0 | case SET_SEEN_YES: |
2478 | | /* already set - skip */ |
2479 | 0 | continue; |
2480 | 0 | case SET_SEEN_DEFAULT: |
2481 | | /* already seen, but still apply defaults */ |
2482 | 0 | if (set->type != SETTINGS_OVERRIDE_TYPE_DEFAULT) |
2483 | 0 | continue; |
2484 | 0 | break; |
2485 | 0 | } |
2486 | 0 | if (set->type == SETTINGS_OVERRIDE_TYPE_2ND_DEFAULT) |
2487 | 0 | *set_seenp = SET_SEEN_DEFAULT; |
2488 | 0 | else |
2489 | 0 | *set_seenp = SET_SEEN_YES; |
2490 | 0 | } |
2491 | | /* skip this setting the next time */ |
2492 | 0 | override->set = NULL; |
2493 | 0 | if (set->pool == ctx->temp_pool) |
2494 | 0 | settings_override_free(set); |
2495 | |
|
2496 | 0 | if (set->type == SETTINGS_OVERRIDE_TYPE_DEFAULT) { |
2497 | | /* Expand %variables only for default settings. |
2498 | | These defaults are usually handled already by the |
2499 | | config process, but we get here with -O parameter |
2500 | | or with SETTINGS_OVERRIDE_TYPE_2ND_DEFAULT. */ |
2501 | 0 | const char *error; |
2502 | 0 | ret = settings_var_expand(ctx, key_idx, &value, &error); |
2503 | 0 | if (ret < 0) { |
2504 | 0 | *error_r = t_strdup_printf( |
2505 | 0 | "Failed to expand default setting %s=%s variables: %s", |
2506 | 0 | key, value, error); |
2507 | 0 | return -1; |
2508 | 0 | } |
2509 | 0 | if (ret > 0) { |
2510 | | /* value was already strdup()ed */ |
2511 | 0 | value_needs_dup = FALSE; |
2512 | 0 | } |
2513 | 0 | } else if ((ctx->info->defines[key_idx].type == SET_PATH_FILE || |
2514 | 0 | ctx->info->defines[key_idx].type == SET_PATH_DIR) && |
2515 | 0 | (ctx->flags & SETTINGS_GET_FLAG_NO_EXPAND) == 0) { |
2516 | 0 | size_t value_len = strlen(value); |
2517 | 0 | if (ctx->info->defines[key_idx].type == SET_PATH_DIR && |
2518 | 0 | value_len > 0 && value[value_len-1] == '/') { |
2519 | | /* drop trailing '/' */ |
2520 | 0 | value = t_strndup(value, value_len - 1); |
2521 | 0 | value_needs_dup = TRUE; |
2522 | 0 | } |
2523 | 0 | if (str_begins_with(value, "~/") || |
2524 | 0 | strcmp(value, "~") == 0) { |
2525 | | /* Expand only the ~/ prefix to the %{home} |
2526 | | variable's value. Unlike default settings, |
2527 | | the rest of the override value is kept |
2528 | | literal without %variable expansion. */ |
2529 | 0 | const char *error; |
2530 | 0 | string_t *home = t_str_new(64); |
2531 | 0 | if (var_expand(home, "%{home}", &ctx->var_params, |
2532 | 0 | &error) < 0) { |
2533 | 0 | if ((ctx->flags & SETTINGS_GET_FLAG_FAKE_EXPAND) == 0) { |
2534 | 0 | *error_r = t_strdup_printf( |
2535 | 0 | "Failed to expand %s=%s home directory: %s", |
2536 | 0 | key, value, error); |
2537 | 0 | return -1; |
2538 | 0 | } |
2539 | 0 | } else { |
2540 | 0 | str_append(home, value + 1); |
2541 | 0 | value = str_c(home); |
2542 | 0 | value_needs_dup = TRUE; |
2543 | 0 | } |
2544 | 0 | } |
2545 | 0 | } |
2546 | 0 | if (ctx->info->defines[key_idx].type == SET_FILTER_ARRAY && |
2547 | 0 | set->type <= SETTINGS_OVERRIDE_TYPE_CLI_PARAM && |
2548 | 0 | str_begins_with(value, "__")) { |
2549 | 0 | *error_r = t_strdup_printf( |
2550 | 0 | "Named list filter name must not begin with '__': %s", |
2551 | 0 | value); |
2552 | 0 | return -1; |
2553 | 0 | } |
2554 | | |
2555 | 0 | if (value_needs_dup) |
2556 | 0 | value = p_strdup(&ctx->mpool->pool, value); |
2557 | 0 | else if (value == set->value) { |
2558 | | /* Add explicit reference to instance->pool, which is |
2559 | | kept by the settings struct's pool. This allows |
2560 | | settings to survive even if the instance is freed. |
2561 | | |
2562 | | If there is no instance pool, it means there are |
2563 | | only CLI_PARAM settings, which are allocated from |
2564 | | FIXME: should figure out some efficient way how to |
2565 | | store them. */ |
2566 | 0 | if (array_is_created(&ctx->mpool->pool.external_refs)) |
2567 | 0 | i_assert(array_idx_elem(&ctx->mpool->pool.external_refs, 0) == ctx->instance->pool); |
2568 | 0 | else if (ctx->instance->pool != NULL) |
2569 | 0 | pool_add_external_ref(&ctx->mpool->pool, ctx->instance->pool); |
2570 | 0 | } |
2571 | | /* An empty built-in default for a setting that can't be empty |
2572 | | (e.g. a number or time) means "no default": leave the value |
2573 | | to be inherited from a less specific filter instead of |
2574 | | failing. This lets a named filter keep a default-settings |
2575 | | entry - so settings-history can override the value for old |
2576 | | config versions - without forcing that value for current |
2577 | | versions. The config process handles this equivalently in |
2578 | | config_apply_exact_line(). */ |
2579 | 0 | bool empty_default = |
2580 | 0 | set->type == SETTINGS_OVERRIDE_TYPE_DEFAULT && |
2581 | 0 | value[0] == '\0'; |
2582 | 0 | if (ctx->info->setting_apply != NULL && |
2583 | 0 | !ctx->info->setting_apply(ctx->event, ctx->set_struct, key, |
2584 | 0 | &value, apply_flags, error_r)) { |
2585 | 0 | if (empty_default) |
2586 | 0 | continue; |
2587 | 0 | *error_r = t_strdup_printf( |
2588 | 0 | "Failed to override configuration from %s: " |
2589 | 0 | "Invalid %s=%s: %s", |
2590 | 0 | settings_override_type_names[set->type], |
2591 | 0 | key, value, *error_r); |
2592 | 0 | return -1; |
2593 | 0 | } |
2594 | 0 | if (settings_parse_keyidx_value_nodup(ctx->parser, key_idx, key, |
2595 | 0 | value) < 0) { |
2596 | 0 | if (empty_default) |
2597 | 0 | continue; |
2598 | 0 | *error_r = t_strdup_printf( |
2599 | 0 | "Failed to override configuration from %s: " |
2600 | 0 | "Invalid %s=%s: %s", |
2601 | 0 | settings_override_type_names[set->type], |
2602 | 0 | key, value, settings_parser_get_error(ctx->parser)); |
2603 | 0 | return -1; |
2604 | 0 | } |
2605 | | |
2606 | 0 | if (!set->append && |
2607 | 0 | ctx->info->defines[key_idx].type == SET_FILTER_ARRAY) { |
2608 | | /* The named list filter is filled in reverse order. |
2609 | | Mark it now as "stopped", so following filters won't |
2610 | | try to insert anything more into it. */ |
2611 | 0 | settings_parse_array_stop(ctx->parser, key_idx); |
2612 | 0 | } |
2613 | 0 | } |
2614 | 0 | *defaults = seen_defaults; |
2615 | 0 | return seen_filter ? 1 : 0; |
2616 | 0 | } |
2617 | | |
2618 | | static int |
2619 | | settings_instance_get(struct settings_apply_ctx *ctx, |
2620 | | const char *source_filename, |
2621 | | unsigned int source_linenum, |
2622 | | const void **set_r, const char **error_r) |
2623 | 0 | { |
2624 | 0 | const char *error; |
2625 | 0 | bool seen_filter = FALSE; |
2626 | 0 | int ret = 0; |
2627 | |
|
2628 | 0 | i_assert(ctx->info->pool_offset1 != 0); |
2629 | | |
2630 | 0 | *set_r = NULL; |
2631 | |
|
2632 | 0 | if (event_find_field_recursive(ctx->event, "protocol") == NULL) |
2633 | 0 | event_add_str(ctx->event, "protocol", ctx->root->protocol_name); |
2634 | |
|
2635 | 0 | ctx->mpool = settings_mmap_pool_create(ctx->root, ctx->instance->mmap, |
2636 | 0 | source_filename, source_linenum); |
2637 | 0 | pool_t set_pool = &ctx->mpool->pool; |
2638 | 0 | ctx->parser = settings_parser_init(set_pool, ctx->info, |
2639 | 0 | SETTINGS_PARSER_FLAG_IGNORE_UNKNOWN_KEYS | |
2640 | 0 | SETTINGS_PARSER_FLAG_INSERT_FILTERS); |
2641 | | |
2642 | | /* Set the pool early on before any callbacks are called. */ |
2643 | 0 | ctx->set_struct = settings_parser_get_set(ctx->parser); |
2644 | 0 | pool_t *pool_p = PTR_OFFSET(ctx->set_struct, |
2645 | 0 | ctx->info->pool_offset1 - 1); |
2646 | 0 | *pool_p = set_pool; |
2647 | |
|
2648 | 0 | t_array_init(&ctx->include_groups, 4); |
2649 | 0 | ctx->str = str_new(default_pool, 256); |
2650 | 0 | i_array_init(&ctx->set_seen, 64); |
2651 | 0 | if ((ctx->flags & SETTINGS_GET_FLAG_NO_EXPAND) == 0 && |
2652 | 0 | (ctx->flags & SETTINGS_GET_FLAG_FAKE_EXPAND) == 0) |
2653 | 0 | settings_var_expand_init(ctx); |
2654 | |
|
2655 | 0 | if (settings_instance_override_init(ctx, error_r) < 0) |
2656 | 0 | ret = -1; |
2657 | 0 | else if (ctx->instance->mmap != NULL) { |
2658 | 0 | ret = settings_mmap_apply(ctx, &error); |
2659 | 0 | if (ret < 0) { |
2660 | 0 | *error_r = t_strdup_printf( |
2661 | 0 | "Failed to parse configuration: %s", error); |
2662 | 0 | } |
2663 | 0 | } else { |
2664 | | /* No configuration file - apply all overrides */ |
2665 | 0 | bool defaults = TRUE; |
2666 | 0 | struct settings_mmap_event_filter set_filter = { |
2667 | 0 | }; |
2668 | 0 | ret = settings_instance_override(ctx, &set_filter, |
2669 | 0 | &defaults, error_r); |
2670 | 0 | } |
2671 | 0 | if (ret > 0) |
2672 | 0 | seen_filter = TRUE; |
2673 | 0 | settings_apply_ctx_overrides_deinit(ctx); |
2674 | |
|
2675 | 0 | if (ret >= 0) |
2676 | 0 | ret = settings_mmap_apply_defaults(ctx, error_r); |
2677 | 0 | if (ret < 0) { |
2678 | 0 | pool_unref(&set_pool); |
2679 | 0 | return -1; |
2680 | 0 | } |
2681 | | |
2682 | 0 | if (ctx->filter_key != NULL && !seen_filter && |
2683 | 0 | ctx->filter_name_required && |
2684 | 0 | !settings_overrides_find_filter(ctx)) { |
2685 | 0 | pool_unref(&set_pool); |
2686 | 0 | return 0; |
2687 | 0 | } |
2688 | | |
2689 | 0 | if ((ctx->flags & SETTINGS_GET_FLAG_NO_CHECK) == 0) { |
2690 | 0 | if (!settings_check(ctx->event, ctx->info, *pool_p, |
2691 | 0 | ctx->set_struct, error_r)) { |
2692 | 0 | *error_r = t_strdup_printf("Invalid settings: %s", |
2693 | 0 | *error_r); |
2694 | 0 | pool_unref(&set_pool); |
2695 | 0 | return -1; |
2696 | 0 | } |
2697 | 0 | } |
2698 | | |
2699 | 0 | *set_r = ctx->set_struct; |
2700 | 0 | return 1; |
2701 | 0 | } |
2702 | | |
2703 | | static void |
2704 | | settings_event_convert_filter_names(struct event *src_event, struct event *dest_event) |
2705 | 0 | { |
2706 | 0 | const char *filter_name = |
2707 | 0 | event_get_ptr(src_event, SETTINGS_EVENT_FILTER_NAME); |
2708 | 0 | if (filter_name == NULL) |
2709 | 0 | return; |
2710 | 0 | event_strlist_append(dest_event, SETTINGS_EVENT_FILTER_NAME, |
2711 | 0 | filter_name); |
2712 | | |
2713 | | /* usually there is just one, continue fast path so see if there is a |
2714 | | 2nd one. */ |
2715 | 0 | filter_name = event_get_ptr(src_event, SETTINGS_EVENT_FILTER_NAME"2"); |
2716 | 0 | if (filter_name == NULL) |
2717 | 0 | return; |
2718 | 0 | event_strlist_append(dest_event, SETTINGS_EVENT_FILTER_NAME, |
2719 | 0 | filter_name); |
2720 | | |
2721 | | /* check the rest: */ |
2722 | 0 | string_t *key = t_str_new(strlen(SETTINGS_EVENT_FILTER_NAME) + 1); |
2723 | 0 | str_append(key, SETTINGS_EVENT_FILTER_NAME); |
2724 | 0 | for (unsigned int i = 3;; i++) { |
2725 | 0 | str_truncate(key, strlen(SETTINGS_EVENT_FILTER_NAME)); |
2726 | 0 | str_printfa(key, "%u", i); |
2727 | 0 | filter_name = event_get_ptr(src_event, str_c(key)); |
2728 | 0 | if (filter_name == NULL) |
2729 | 0 | break; |
2730 | 0 | event_strlist_append(dest_event, SETTINGS_EVENT_FILTER_NAME, |
2731 | 0 | filter_name); |
2732 | 0 | } |
2733 | 0 | } |
2734 | | |
2735 | | static int |
2736 | | settings_get_real(struct event *event, |
2737 | | const char *filter_key, const char *filter_value, |
2738 | | const struct setting_parser_info *info, |
2739 | | const struct settings_get_params *params, |
2740 | | const char *source_filename, |
2741 | | unsigned int source_linenum, |
2742 | | const void **set_r, const char **error_r) |
2743 | 0 | { |
2744 | 0 | struct settings_root *scan_root, *root = NULL; |
2745 | 0 | struct settings_mmap *mmap = NULL; |
2746 | 0 | struct settings_instance *scan_instance, *instance = NULL; |
2747 | 0 | struct event *lookup_event, *scan_event = event; |
2748 | 0 | const char *filter_name = NULL; |
2749 | 0 | bool filter_name_required = FALSE; |
2750 | |
|
2751 | 0 | lookup_event = event_create(event); |
2752 | 0 | if (filter_value != NULL) { |
2753 | 0 | filter_name = t_strdup_printf("%s/%s", filter_key, |
2754 | 0 | settings_section_escape(filter_value)); |
2755 | | /* the filter key=value field is needed by setting override |
2756 | | handling to incrementally generate the filter */ |
2757 | 0 | event_add_str(lookup_event, filter_key, filter_value); |
2758 | 0 | event_strlist_append(lookup_event, SETTINGS_EVENT_FILTER_NAME, |
2759 | 0 | filter_name); |
2760 | 0 | filter_name_required = TRUE; |
2761 | 0 | } else if (filter_key != NULL) { |
2762 | 0 | filter_name = filter_key; |
2763 | 0 | event_strlist_append(lookup_event, SETTINGS_EVENT_FILTER_NAME, |
2764 | 0 | filter_name); |
2765 | 0 | filter_name_required = TRUE; |
2766 | 0 | } |
2767 | |
|
2768 | 0 | do { |
2769 | 0 | scan_root = event_get_ptr(scan_event, SETTINGS_EVENT_ROOT); |
2770 | 0 | scan_instance = event_get_ptr(scan_event, |
2771 | 0 | SETTINGS_EVENT_INSTANCE); |
2772 | |
|
2773 | 0 | if (root == NULL) |
2774 | 0 | root = scan_root; |
2775 | 0 | else if ((scan_root != NULL && root != scan_root) || |
2776 | 0 | (scan_instance != NULL && root != scan_instance->root)) { |
2777 | | /* settings root changed - ignore the rest of the |
2778 | | event hierarchy. */ |
2779 | 0 | break; |
2780 | 0 | } |
2781 | 0 | if (instance == NULL && scan_instance != NULL) { |
2782 | 0 | instance = scan_instance; |
2783 | 0 | root = instance->root; |
2784 | 0 | } |
2785 | |
|
2786 | 0 | settings_event_convert_filter_names(scan_event, lookup_event); |
2787 | 0 | scan_event = event_get_parent(scan_event); |
2788 | 0 | } while (scan_event != NULL); |
2789 | |
|
2790 | 0 | if (root == NULL) |
2791 | 0 | i_panic("settings_get() - event has no SETTINGS_EVENT_ROOT"); |
2792 | 0 | if (instance != NULL) |
2793 | 0 | mmap = instance->mmap; |
2794 | 0 | else |
2795 | 0 | mmap = root->mmap; |
2796 | | |
2797 | | /* no instance-specific settings */ |
2798 | 0 | struct settings_instance empty_instance = { |
2799 | 0 | .mmap = mmap, |
2800 | 0 | }; |
2801 | 0 | if (instance == NULL) |
2802 | 0 | instance = &empty_instance; |
2803 | |
|
2804 | 0 | struct settings_apply_ctx ctx = { |
2805 | 0 | .event = lookup_event, |
2806 | 0 | .root = root, |
2807 | 0 | .instance = instance, |
2808 | 0 | .info = info, |
2809 | 0 | .flags = params->flags, |
2810 | 0 | .escape_func = params->escape_func, |
2811 | 0 | .escape_context = params->escape_context, |
2812 | 0 | .filter_name = filter_name, |
2813 | 0 | .filter_key = filter_key, |
2814 | 0 | .filter_value = filter_value, |
2815 | 0 | .filter_name_required = filter_name_required, |
2816 | 0 | }; |
2817 | |
|
2818 | 0 | int ret = settings_instance_get(&ctx, source_filename, source_linenum, |
2819 | 0 | set_r, error_r); |
2820 | 0 | if (ret < 0) { |
2821 | 0 | *error_r = t_strdup_printf("%s settings%s: %s", info->name, |
2822 | 0 | filter_name == NULL ? "" : |
2823 | 0 | t_strdup_printf(" (filter=%s)", filter_name), |
2824 | 0 | *error_r); |
2825 | 0 | } |
2826 | 0 | settings_parser_unref(&ctx.parser); |
2827 | 0 | event_unref(&lookup_event); |
2828 | 0 | array_free(&ctx.set_seen); |
2829 | 0 | str_free(&ctx.str); |
2830 | 0 | pool_unref(&ctx.temp_pool); |
2831 | 0 | return ret; |
2832 | 0 | } |
2833 | | |
2834 | | bool settings_key_exists(struct event *event, const char *key) |
2835 | 0 | { |
2836 | 0 | struct settings_root *root = NULL; |
2837 | 0 | do { |
2838 | 0 | root = event_get_ptr(event, SETTINGS_EVENT_ROOT); |
2839 | 0 | event = event_get_parent(event); |
2840 | 0 | } while (root == NULL && event != NULL); |
2841 | 0 | if (root == NULL) |
2842 | 0 | i_panic("settings_key_exists() - event has no SETTINGS_EVENT_ROOT"); |
2843 | | |
2844 | 0 | bool ret; |
2845 | 0 | T_BEGIN { |
2846 | 0 | size_t len = strlen(key); |
2847 | 0 | if (len > 0 && key[len-1] == SETTINGS_APPEND_KEY_SUFFIX[0]) |
2848 | 0 | key = t_strndup(key, len - 1); |
2849 | |
|
2850 | 0 | enum setting_type set_type ATTR_UNUSED; |
2851 | 0 | const void *blocks ATTR_UNUSED; |
2852 | 0 | ret = settings_mmap_lookup_key(root->mmap, key, |
2853 | 0 | &set_type, &blocks); |
2854 | 0 | } T_END; |
2855 | 0 | return ret; |
2856 | 0 | } |
2857 | | |
2858 | | struct settings_filter_array_item { |
2859 | | union { |
2860 | | unsigned int uint; |
2861 | | const char *str; |
2862 | | } order; |
2863 | | const char *value; |
2864 | | }; |
2865 | | |
2866 | | static int |
2867 | | settings_filter_array_uint_cmp(const struct settings_filter_array_item *item1, |
2868 | | const struct settings_filter_array_item *item2) |
2869 | 0 | { |
2870 | 0 | if (item1->order.uint < item2->order.uint) |
2871 | 0 | return -1; |
2872 | 0 | if (item1->order.uint > item2->order.uint) |
2873 | 0 | return 1; |
2874 | 0 | return 0; |
2875 | 0 | } |
2876 | | |
2877 | | static int |
2878 | | settings_filter_array_str_cmp(const struct settings_filter_array_item *item1, |
2879 | | const struct settings_filter_array_item *item2) |
2880 | 0 | { |
2881 | 0 | return strcmp(item1->order.str, item2->order.str); |
2882 | 0 | } |
2883 | | |
2884 | | static int |
2885 | | settings_sort_filter_array(struct event *event, const char *field_name, |
2886 | | const struct setting_filter_array_order *order, |
2887 | | ARRAY_TYPE(const_string) *fvals_arr, |
2888 | | const struct settings_get_params *params, |
2889 | | const char *source_filename, |
2890 | | unsigned int source_linenum, const char **error_r) |
2891 | 0 | { |
2892 | 0 | const char **fvals; |
2893 | 0 | unsigned int fidx, count, i; |
2894 | |
|
2895 | 0 | if (!array_is_created(fvals_arr)) |
2896 | 0 | return 0; |
2897 | | |
2898 | | /* Find definition index of order field */ |
2899 | 0 | if (!setting_parser_info_find_key(order->info, order->field_name, |
2900 | 0 | &fidx)) |
2901 | 0 | i_panic("BUG: Order field %s for filter array %s in %s " |
2902 | 0 | "not found", |
2903 | 0 | order->field_name, field_name, order->info->name); |
2904 | | |
2905 | | /* Resolve alias */ |
2906 | 0 | while (fidx > 0 && order->info->defines[fidx].type == SET_ALIAS) |
2907 | 0 | fidx--; |
2908 | | |
2909 | | /* Determine type of field */ |
2910 | 0 | enum { |
2911 | 0 | _ITEM_TYPE_UINT, |
2912 | 0 | _ITEM_TYPE_STR, |
2913 | 0 | } item_type; |
2914 | |
|
2915 | 0 | const struct setting_define *order_def = &order->info->defines[fidx]; |
2916 | 0 | switch (order_def->type) { |
2917 | 0 | case SET_UINT: |
2918 | 0 | case SET_UINT_OCT: |
2919 | 0 | case SET_TIME: |
2920 | 0 | case SET_TIME_MSECS: |
2921 | 0 | item_type = _ITEM_TYPE_UINT; |
2922 | 0 | break; |
2923 | 0 | case SET_STR: |
2924 | 0 | case SET_STR_NOVARS: |
2925 | 0 | case SET_PATH_FILE: |
2926 | 0 | case SET_PATH_DIR: |
2927 | 0 | item_type = _ITEM_TYPE_STR; |
2928 | 0 | break; |
2929 | 0 | case SET_ALIAS: |
2930 | 0 | i_panic("BUG: Order field %s for filter array %s in %s " |
2931 | 0 | "is an invalid alias", |
2932 | 0 | order->field_name, field_name, order->info->name); |
2933 | 0 | default: |
2934 | 0 | i_panic("BUG: Order field %s for filter array %s in %s " |
2935 | 0 | "has a type unsuitable for defining an order", |
2936 | 0 | order->field_name, field_name, order->info->name); |
2937 | 0 | } |
2938 | | |
2939 | | /* Read all order fields from filter array items */ |
2940 | 0 | ARRAY(struct settings_filter_array_item) items_arr; |
2941 | |
|
2942 | 0 | fvals = array_get_modifiable(fvals_arr, &count); |
2943 | 0 | t_array_init(&items_arr, count); |
2944 | 0 | for (i = 0; i < count; i++) { |
2945 | 0 | const void *set; |
2946 | 0 | int ret; |
2947 | |
|
2948 | 0 | T_BEGIN { |
2949 | 0 | ret = settings_get_real(event, field_name, fvals[i], |
2950 | 0 | order->info, params, |
2951 | 0 | source_filename, source_linenum, |
2952 | 0 | &set, error_r); |
2953 | 0 | } T_END_PASS_STR_IF(ret < 0, error_r); |
2954 | 0 | if (ret < 0) |
2955 | 0 | return -1; |
2956 | 0 | if (ret == 0) { |
2957 | 0 | *error_r = t_strdup_printf( |
2958 | 0 | "Filter %s=%s unexpectedly not found while sorting " |
2959 | 0 | "(invalid userdb or -o override settings?)", |
2960 | 0 | field_name, fvals[i]); |
2961 | 0 | return -1; |
2962 | 0 | } |
2963 | | |
2964 | 0 | pool_t *pool_p = PTR_OFFSET(set, order->info->pool_offset1 - 1); |
2965 | 0 | pool_t pool = *pool_p; |
2966 | 0 | struct settings_filter_array_item *item; |
2967 | |
|
2968 | 0 | item = array_append_space(&items_arr); |
2969 | 0 | item->value = fvals[i]; |
2970 | |
|
2971 | 0 | switch (item_type) { |
2972 | 0 | case _ITEM_TYPE_UINT: |
2973 | 0 | item->order.uint = *((unsigned int *) |
2974 | 0 | PTR_OFFSET(set, order_def->offset)); |
2975 | 0 | break; |
2976 | 0 | case _ITEM_TYPE_STR: |
2977 | 0 | item->order.str = *((const char **) |
2978 | 0 | PTR_OFFSET(set, order_def->offset)); |
2979 | 0 | item->order.str = t_strdup(item->order.str); |
2980 | 0 | break; |
2981 | 0 | default: |
2982 | 0 | i_unreached(); |
2983 | 0 | } |
2984 | | |
2985 | 0 | pool_unref(&pool); |
2986 | 0 | } |
2987 | | |
2988 | | /* Sort the filter array items based on order field */ |
2989 | 0 | switch (item_type) { |
2990 | 0 | case _ITEM_TYPE_UINT: |
2991 | 0 | array_sort(&items_arr, settings_filter_array_uint_cmp); |
2992 | 0 | break; |
2993 | 0 | case _ITEM_TYPE_STR: |
2994 | 0 | array_sort(&items_arr, settings_filter_array_str_cmp); |
2995 | 0 | break; |
2996 | 0 | default: |
2997 | 0 | i_unreached(); |
2998 | 0 | } |
2999 | | |
3000 | | /* Move the items in the settings */ |
3001 | 0 | const struct settings_filter_array_item *items; |
3002 | 0 | unsigned int items_count; |
3003 | |
|
3004 | 0 | items = array_get(&items_arr, &items_count); |
3005 | 0 | i_assert(items_count == count); |
3006 | 0 | for (i = 0; i < count; i++) { |
3007 | 0 | if (!order->reverse) |
3008 | 0 | fvals[i] = items[i].value; |
3009 | 0 | else |
3010 | 0 | fvals[i] = items[count - 1 - i].value; |
3011 | 0 | } |
3012 | 0 | return 0; |
3013 | 0 | } |
3014 | | |
3015 | | static int |
3016 | | settings_get_full(struct event *event, |
3017 | | const char *filter_key, const char *filter_value, |
3018 | | const struct setting_parser_info *info, |
3019 | | const struct settings_get_params *params, |
3020 | | const char *source_filename, |
3021 | | unsigned int source_linenum, |
3022 | | const void **set_r, const char **error_r) |
3023 | 0 | { |
3024 | 0 | const void *set; |
3025 | 0 | int ret; |
3026 | |
|
3027 | 0 | *set_r = NULL; |
3028 | 0 | T_BEGIN { |
3029 | 0 | ret = settings_get_real(event, filter_key, filter_value, info, |
3030 | 0 | params, source_filename, source_linenum, |
3031 | 0 | &set, error_r); |
3032 | 0 | } T_END_PASS_STR_IF(ret < 0, error_r); |
3033 | 0 | if (ret <= 0) |
3034 | 0 | return ret; |
3035 | 0 | if ((params->flags & SETTINGS_GET_FLAG_SORT_FILTER_ARRAYS) == 0) { |
3036 | | /* Sorting filter arrays disabled */ |
3037 | 0 | *set_r = set; |
3038 | 0 | return 1; |
3039 | 0 | } |
3040 | | |
3041 | 0 | const struct setting_define *def; |
3042 | | |
3043 | | /* Sort filter arrays when order is defined */ |
3044 | 0 | for (def = info->defines; def->key != NULL; def++) { |
3045 | 0 | int ret; |
3046 | |
|
3047 | 0 | if (def->type != SET_FILTER_ARRAY) { |
3048 | | /* Not a filter array */ |
3049 | 0 | continue; |
3050 | 0 | } |
3051 | 0 | if (def->filter_array_order == NULL) { |
3052 | | /* No order defined */ |
3053 | 0 | continue; |
3054 | 0 | } |
3055 | | |
3056 | 0 | ARRAY_TYPE(const_string) *fvals_arr = |
3057 | 0 | PTR_OFFSET(set, def->offset); |
3058 | 0 | i_assert(fvals_arr != NULL); |
3059 | | |
3060 | | /* Sort this array */ |
3061 | 0 | T_BEGIN { |
3062 | 0 | ret = settings_sort_filter_array( |
3063 | 0 | event, def->key, def->filter_array_order, |
3064 | 0 | fvals_arr, params, |
3065 | 0 | source_filename, source_linenum, error_r); |
3066 | 0 | } T_END_PASS_STR_IF(ret < 0, error_r); |
3067 | 0 | if (ret < 0) { |
3068 | 0 | pool_t *pool_p = PTR_OFFSET(set, info->pool_offset1 - 1); |
3069 | 0 | pool_t pool = *pool_p; |
3070 | 0 | pool_unref(&pool); |
3071 | 0 | return -1; |
3072 | 0 | } |
3073 | 0 | } |
3074 | | |
3075 | 0 | *set_r = set; |
3076 | 0 | return 1; |
3077 | 0 | } |
3078 | | |
3079 | | #undef settings_get |
3080 | | int settings_get(struct event *event, |
3081 | | const struct setting_parser_info *info, |
3082 | | enum settings_get_flags flags, |
3083 | | const char *source_filename, |
3084 | | unsigned int source_linenum, |
3085 | | const void **set_r, const char **error_r) |
3086 | 0 | { |
3087 | 0 | const struct settings_get_params params = { |
3088 | 0 | .flags = flags, |
3089 | 0 | }; |
3090 | 0 | int ret = settings_get_full(event, NULL, NULL, info, ¶ms, |
3091 | 0 | source_filename, source_linenum, |
3092 | 0 | set_r, error_r); |
3093 | 0 | i_assert(ret != 0); |
3094 | 0 | return ret < 0 ? -1 : 0; |
3095 | 0 | } |
3096 | | |
3097 | | #undef settings_get_params |
3098 | | int settings_get_params(struct event *event, |
3099 | | const struct setting_parser_info *info, |
3100 | | const struct settings_get_params *params, |
3101 | | const char *source_filename, |
3102 | | unsigned int source_linenum, |
3103 | | const void **set_r, const char **error_r) |
3104 | 0 | { |
3105 | 0 | int ret = settings_get_full(event, NULL, NULL, info, params, |
3106 | 0 | source_filename, source_linenum, |
3107 | 0 | set_r, error_r); |
3108 | 0 | i_assert(ret != 0); |
3109 | 0 | return ret < 0 ? -1 : 0; |
3110 | 0 | } |
3111 | | |
3112 | | #undef settings_get_filter |
3113 | | int settings_get_filter(struct event *event, |
3114 | | const char *filter_key, const char *filter_value, |
3115 | | const struct setting_parser_info *info, |
3116 | | enum settings_get_flags flags, |
3117 | | const char *source_filename, |
3118 | | unsigned int source_linenum, |
3119 | | const void **set_r, const char **error_r) |
3120 | 0 | { |
3121 | 0 | i_assert(filter_key != NULL); |
3122 | 0 | i_assert(filter_value != NULL); |
3123 | | |
3124 | 0 | const struct settings_get_params params = { |
3125 | 0 | .flags = flags, |
3126 | 0 | }; |
3127 | 0 | int ret = settings_get_full(event, filter_key, filter_value, info, |
3128 | 0 | ¶ms, source_filename, source_linenum, |
3129 | 0 | set_r, error_r); |
3130 | 0 | if (ret < 0) |
3131 | 0 | return -1; |
3132 | 0 | if (ret == 0) { |
3133 | | /* e.g. namespace=foo was given but no namespace/foo/name */ |
3134 | 0 | *error_r = t_strdup_printf( |
3135 | 0 | "Filter %s=%s unexpectedly not found " |
3136 | 0 | "(invalid userdb or -o override settings?)", |
3137 | 0 | filter_key, filter_value); |
3138 | 0 | return -1; |
3139 | 0 | } |
3140 | 0 | return 0; |
3141 | 0 | } |
3142 | | |
3143 | | #undef settings_try_get_filter |
3144 | | int settings_try_get_filter(struct event *event, |
3145 | | const char *filter_key, const char *filter_value, |
3146 | | const struct setting_parser_info *info, |
3147 | | enum settings_get_flags flags, |
3148 | | const char *source_filename, |
3149 | | unsigned int source_linenum, |
3150 | | const void **set_r, const char **error_r) |
3151 | 0 | { |
3152 | 0 | i_assert(filter_key != NULL); |
3153 | 0 | i_assert(filter_value != NULL); |
3154 | | |
3155 | 0 | const struct settings_get_params params = { |
3156 | 0 | .flags = flags, |
3157 | 0 | }; |
3158 | 0 | return settings_get_full(event, filter_key, filter_value, info, |
3159 | 0 | ¶ms, source_filename, source_linenum, |
3160 | 0 | set_r, error_r); |
3161 | 0 | } |
3162 | | |
3163 | | #undef settings_get_or_fatal |
3164 | | const void * |
3165 | | settings_get_or_fatal(struct event *event, |
3166 | | const struct setting_parser_info *info, |
3167 | | const char *source_filename, |
3168 | | unsigned int source_linenum) |
3169 | 0 | { |
3170 | 0 | const void *set; |
3171 | 0 | const char *error; |
3172 | |
|
3173 | 0 | if (settings_get(event, info, 0, source_filename, |
3174 | 0 | source_linenum, &set, &error) < 0) |
3175 | 0 | i_fatal("%s", error); |
3176 | 0 | return set; |
3177 | 0 | } |
3178 | | |
3179 | | static void |
3180 | | settings_override_fill(struct settings_override *set, pool_t pool, |
3181 | | const char *key, const char *value, |
3182 | | enum settings_override_type type) |
3183 | 0 | { |
3184 | 0 | set->pool = pool; |
3185 | 0 | set->type = type; |
3186 | 0 | size_t len = strlen(key); |
3187 | 0 | T_BEGIN { |
3188 | 0 | if (len > 0 && key[len-1] == SETTINGS_APPEND_KEY_SUFFIX[0]) { |
3189 | | /* key+=value */ |
3190 | 0 | set->append = TRUE; |
3191 | 0 | len--; |
3192 | 0 | while (len > 0 && i_isspace(key[len-1])) |
3193 | 0 | len--; |
3194 | 0 | key = t_strndup(key, len); |
3195 | 0 | } |
3196 | 0 | set->key = set->orig_key = p_strdup(pool, key); |
3197 | 0 | set->value = p_strdup(pool, value); |
3198 | 0 | } T_END; |
3199 | 0 | } |
3200 | | |
3201 | | void settings_override(struct settings_instance *instance, |
3202 | | const char *key, const char *value, |
3203 | | enum settings_override_type type) |
3204 | 0 | { |
3205 | 0 | if (!array_is_created(&instance->overrides)) |
3206 | 0 | p_array_init(&instance->overrides, instance->pool, 16); |
3207 | 0 | struct settings_override *set = |
3208 | 0 | array_append_space(&instance->overrides); |
3209 | 0 | set->overrides_array = &instance->overrides; |
3210 | 0 | settings_override_fill(set, instance->pool, key, value, type); |
3211 | 0 | } |
3212 | | |
3213 | | void settings_root_override(struct settings_root *root, |
3214 | | const char *key, const char *value, |
3215 | | enum settings_override_type type) |
3216 | 0 | { |
3217 | 0 | if (!array_is_created(&root->overrides)) |
3218 | 0 | p_array_init(&root->overrides, root->pool, 16); |
3219 | 0 | struct settings_override *set = |
3220 | 0 | array_append_space(&root->overrides); |
3221 | 0 | set->overrides_array = &root->overrides; |
3222 | 0 | settings_override_fill(set, root->pool, key, value, type); |
3223 | 0 | } |
3224 | | |
3225 | | static bool |
3226 | | settings_override_equals(struct settings_override *set, const char *key, |
3227 | | enum settings_override_type type) |
3228 | 0 | { |
3229 | 0 | size_t key_len = strlen(key); |
3230 | 0 | bool key_append = (key_len > 0 && |
3231 | 0 | key[key_len-1] == SETTINGS_APPEND_KEY_SUFFIX[0]); |
3232 | |
|
3233 | 0 | if (set->type != type) |
3234 | 0 | return FALSE; |
3235 | 0 | if (set->append != key_append) |
3236 | 0 | return FALSE; |
3237 | | |
3238 | 0 | if (key_append) { |
3239 | 0 | size_t set_len = strlen(set->key); |
3240 | |
|
3241 | 0 | if (set_len != key_len - 1) |
3242 | 0 | return FALSE; |
3243 | 0 | return (strncmp(key, set->key, key_len - 1) == 0); |
3244 | 0 | } |
3245 | 0 | return (strcmp(key, set->key) == 0); |
3246 | 0 | } |
3247 | | |
3248 | | bool settings_root_override_remove(struct settings_root *root, |
3249 | | const char *key, |
3250 | | enum settings_override_type type) |
3251 | 0 | { |
3252 | 0 | if (!array_is_created(&root->overrides)) |
3253 | 0 | return FALSE; |
3254 | | |
3255 | 0 | struct settings_override *set; |
3256 | |
|
3257 | 0 | array_foreach_modifiable(&root->overrides, set) { |
3258 | 0 | if (!settings_override_equals(set, key, type)) |
3259 | 0 | continue; |
3260 | | |
3261 | 0 | settings_override_free(set); |
3262 | 0 | array_delete(&root->overrides, |
3263 | 0 | array_foreach_idx(&root->overrides, set), 1); |
3264 | 0 | return TRUE; |
3265 | 0 | } |
3266 | | |
3267 | 0 | return FALSE; |
3268 | 0 | } |
3269 | | |
3270 | | static const char *settings_event_get_free_filter_name_key(struct event *event) |
3271 | 0 | { |
3272 | 0 | if (event_get_ptr(event, SETTINGS_EVENT_FILTER_NAME) == NULL) |
3273 | 0 | return SETTINGS_EVENT_FILTER_NAME; |
3274 | | |
3275 | 0 | string_t *str = t_str_new(strlen(SETTINGS_EVENT_FILTER_NAME) + 1); |
3276 | 0 | str_append(str, SETTINGS_EVENT_FILTER_NAME); |
3277 | 0 | for (unsigned int i = 2;; i++) { |
3278 | 0 | str_truncate(str, strlen(SETTINGS_EVENT_FILTER_NAME)); |
3279 | 0 | str_printfa(str, "%u", i); |
3280 | 0 | if (event_get_ptr(event, str_c(str)) == NULL) |
3281 | 0 | break; |
3282 | 0 | } |
3283 | 0 | return str_c(str); |
3284 | 0 | } |
3285 | | |
3286 | | void settings_event_add_filter_name(struct event *event, const char *name) |
3287 | 0 | { |
3288 | 0 | event_set_ptr(event, settings_event_get_free_filter_name_key(event), |
3289 | 0 | p_strdup(event_get_pool(event), name)); |
3290 | 0 | } |
3291 | | |
3292 | | void settings_event_add_list_filter_name(struct event *event, |
3293 | | const char *key, const char *value) |
3294 | 0 | { |
3295 | 0 | char *filter_name = |
3296 | 0 | p_strconcat(event_get_pool(event), |
3297 | 0 | key, "/", settings_section_escape(value), NULL); |
3298 | 0 | event_set_ptr(event, settings_event_get_free_filter_name_key(event), |
3299 | 0 | filter_name); |
3300 | 0 | } |
3301 | | |
3302 | | static struct settings_instance * |
3303 | | settings_instance_alloc(void) |
3304 | 0 | { |
3305 | 0 | pool_t pool = pool_alloconly_create("settings instance", 1024); |
3306 | 0 | struct settings_instance *instance = |
3307 | 0 | p_new(pool, struct settings_instance, 1); |
3308 | 0 | instance->pool = pool; |
3309 | 0 | return instance; |
3310 | 0 | } |
3311 | | |
3312 | | struct settings_instance * |
3313 | | settings_instance_new(struct settings_root *root) |
3314 | 0 | { |
3315 | 0 | struct settings_instance *instance = settings_instance_alloc(); |
3316 | 0 | instance->root = root; |
3317 | 0 | instance->mmap = root->mmap; |
3318 | 0 | return instance; |
3319 | 0 | } |
3320 | | |
3321 | | struct settings_instance * |
3322 | | settings_instance_dup(const struct settings_instance *src) |
3323 | 0 | { |
3324 | 0 | struct settings_instance *dest = settings_instance_alloc(); |
3325 | 0 | dest->root = src->root; |
3326 | 0 | dest->mmap = src->mmap; |
3327 | |
|
3328 | 0 | if (!array_is_created(&src->overrides)) |
3329 | 0 | return dest; |
3330 | | |
3331 | 0 | p_array_init(&dest->overrides, dest->pool, |
3332 | 0 | array_count(&src->overrides) + 8); |
3333 | 0 | const struct settings_override *src_set; |
3334 | 0 | array_foreach(&src->overrides, src_set) { |
3335 | 0 | struct settings_override *dest_set = |
3336 | 0 | array_append_space(&dest->overrides); |
3337 | | /* regenerate filter later */ |
3338 | 0 | dest_set->pool = dest->pool; |
3339 | 0 | dest_set->type = src_set->type; |
3340 | 0 | dest_set->append = src_set->append; |
3341 | 0 | dest_set->orig_key = p_strdup(dest->pool, src_set->orig_key); |
3342 | 0 | dest_set->key = dest_set->orig_key; |
3343 | 0 | dest_set->value = p_strdup(dest->pool, src_set->value); |
3344 | 0 | dest_set->overrides_array = &dest->overrides; |
3345 | 0 | } |
3346 | 0 | return dest; |
3347 | 0 | } |
3348 | | |
3349 | | void settings_instance_free(struct settings_instance **_instance) |
3350 | 0 | { |
3351 | 0 | struct settings_instance *instance = *_instance; |
3352 | 0 | struct settings_override *override; |
3353 | |
|
3354 | 0 | if (instance == NULL) |
3355 | 0 | return; |
3356 | | |
3357 | 0 | *_instance = NULL; |
3358 | |
|
3359 | 0 | if (array_is_created(&instance->overrides)) { |
3360 | 0 | array_foreach_modifiable(&instance->overrides, override) |
3361 | 0 | settings_override_free(override); |
3362 | 0 | } |
3363 | 0 | pool_unref(&instance->pool); |
3364 | 0 | } |
3365 | | |
3366 | | struct settings_root *settings_root_init(void) |
3367 | 0 | { |
3368 | 0 | pool_t pool = pool_alloconly_create("settings root", 128); |
3369 | 0 | struct settings_root *root = p_new(pool, struct settings_root, 1); |
3370 | 0 | root->pool = pool; |
3371 | 0 | return root; |
3372 | 0 | } |
3373 | | |
3374 | | void settings_root_deinit(struct settings_root **_root) |
3375 | 0 | { |
3376 | 0 | struct settings_root *root = *_root; |
3377 | 0 | struct settings_override *override; |
3378 | 0 | struct settings_mmap_pool *mpool; |
3379 | |
|
3380 | 0 | if (root == NULL) |
3381 | 0 | return; |
3382 | 0 | *_root = NULL; |
3383 | |
|
3384 | 0 | if (array_is_created(&root->overrides)) { |
3385 | 0 | array_foreach_modifiable(&root->overrides, override) |
3386 | 0 | settings_override_free(override); |
3387 | 0 | } |
3388 | 0 | settings_mmap_unref(&root->mmap); |
3389 | |
|
3390 | 0 | for (mpool = root->settings_pools; mpool != NULL; mpool = mpool->next) { |
3391 | 0 | i_warning("Leaked settings: %s:%u", |
3392 | 0 | mpool->source_filename, mpool->source_linenum); |
3393 | 0 | } |
3394 | 0 | pool_unref(&root->pool); |
3395 | 0 | } |
3396 | | |
3397 | | struct settings_root *settings_root_find(const struct event *event) |
3398 | 0 | { |
3399 | 0 | struct settings_root *root; |
3400 | |
|
3401 | 0 | do { |
3402 | 0 | root = event_get_ptr(event, SETTINGS_EVENT_ROOT); |
3403 | 0 | if (root != NULL) |
3404 | 0 | return root; |
3405 | 0 | event = event_get_parent(event); |
3406 | 0 | } while (event != NULL); |
3407 | 0 | return NULL; |
3408 | 0 | } |
3409 | | |
3410 | | static void set_registered_infos_free(void) |
3411 | 0 | { |
3412 | 0 | array_free(&set_registered_infos); |
3413 | 0 | } |
3414 | | |
3415 | | static int |
3416 | | setting_parser_info_cmp(const struct setting_parser_info *info, |
3417 | | const struct setting_parser_info *const *info2) |
3418 | 0 | { |
3419 | 0 | return *info2 == info ? 0 : -1; |
3420 | 0 | } |
3421 | | |
3422 | | void settings_info_register(const struct setting_parser_info *info) |
3423 | 0 | { |
3424 | 0 | if (!array_is_created(&set_registered_infos)) { |
3425 | 0 | i_array_init(&set_registered_infos, 16); |
3426 | 0 | lib_atexit(set_registered_infos_free); |
3427 | 0 | } |
3428 | 0 | if (array_lsearch(&set_registered_infos, info, |
3429 | 0 | setting_parser_info_cmp) == NULL) |
3430 | 0 | array_push_back(&set_registered_infos, &info); |
3431 | 0 | } |
3432 | | |
3433 | | struct settings_instance *settings_instance_find(const struct event *event) |
3434 | 0 | { |
3435 | 0 | struct settings_instance *instance; |
3436 | |
|
3437 | 0 | do { |
3438 | 0 | instance = event_get_ptr(event, SETTINGS_EVENT_INSTANCE); |
3439 | 0 | if (instance != NULL) |
3440 | 0 | return instance; |
3441 | 0 | event = event_get_parent(event); |
3442 | 0 | } while (event != NULL); |
3443 | 0 | return NULL; |
3444 | 0 | } |
3445 | | |
3446 | | void settings_simple_init(struct settings_simple *set_r, |
3447 | | const char *const settings[]) |
3448 | 0 | { |
3449 | 0 | i_zero(set_r); |
3450 | 0 | set_r->root = settings_root_init(); |
3451 | 0 | set_r->event = event_create(NULL); |
3452 | 0 | event_set_ptr(set_r->event, SETTINGS_EVENT_ROOT, set_r->root); |
3453 | 0 | if (settings != NULL) |
3454 | 0 | settings_simple_update(set_r, settings); |
3455 | 0 | } |
3456 | | |
3457 | | void settings_simple_deinit(struct settings_simple *set) |
3458 | 0 | { |
3459 | 0 | settings_instance_free(&set->instance); |
3460 | 0 | settings_root_deinit(&set->root); |
3461 | 0 | event_unref(&set->event); |
3462 | 0 | } |
3463 | | |
3464 | | void settings_simple_update(struct settings_simple *set, |
3465 | | const char *const settings[]) |
3466 | 0 | { |
3467 | 0 | settings_instance_free(&set->instance); |
3468 | 0 | set->instance = settings_instance_new(set->root); |
3469 | 0 | for (unsigned int i = 0; settings[i] != NULL; i += 2) { |
3470 | 0 | settings_override(set->instance, settings[i], settings[i + 1], |
3471 | 0 | SETTINGS_OVERRIDE_TYPE_CODE); |
3472 | 0 | } |
3473 | 0 | event_set_ptr(set->event, SETTINGS_EVENT_INSTANCE, set->instance); |
3474 | 0 | } |