Coverage Report

Created: 2026-05-30 06:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/dovecot/src/lib-master/master-service-settings.c
Line
Count
Source
1
/* Copyright (c) 2005-2018 Dovecot authors, see the included COPYING file */
2
3
#include "lib.h"
4
#include "array.h"
5
#include "event-filter.h"
6
#include "path-util.h"
7
#include "hostpid.h"
8
#include "fdpass.h"
9
#include "write-full.h"
10
#include "str.h"
11
#include "sha2.h"
12
#include "hex-binary.h"
13
#include "restrict-access.h"
14
#include "syslog-util.h"
15
#include "eacces-error.h"
16
#include "env-util.h"
17
#include "execv-const.h"
18
#include "version.h"
19
#include "settings.h"
20
#include "stats-client.h"
21
#include "master-service-private.h"
22
#include "master-service-settings.h"
23
#include "strescape.h"
24
25
#include <unistd.h>
26
#include <fcntl.h>
27
#include <time.h>
28
#include <sys/stat.h>
29
30
0
#define DOVECOT_CONFIG_BIN_PATH BINDIR"/doveconf"
31
0
#define DOVECOT_CONFIG_BIN_PATH_ENV "DOVECONF_PATH"
32
0
#define DOVECOT_CONFIG_SOCKET_PATH PKG_RUNDIR"/config"
33
34
0
#define CONFIG_READ_TIMEOUT_SECS 10
35
0
#define CONFIG_HANDSHAKE "VERSION\tconfig\t3\t0\n"
36
37
#undef DEF
38
#define DEF(type, name) \
39
  SETTING_DEFINE_STRUCT_##type(#name, name, struct master_service_settings)
40
41
static bool
42
master_service_settings_check(void *_set, pool_t pool, const char **error_r);
43
44
static const struct setting_define master_service_setting_defines[] = {
45
  DEF(STR_HIDDEN, base_dir),
46
  DEF(STR_HIDDEN, state_dir),
47
  DEF(STR, instance_name),
48
  DEF(STR, log_path),
49
  DEF(STR, info_log_path),
50
  DEF(STR, debug_log_path),
51
  DEF(STR_NOVARS, log_timestamp),
52
  DEF(STR, log_debug),
53
  DEF(STR, log_core_filter),
54
  DEF(STR, process_shutdown_filter),
55
  DEF(STR, syslog_facility),
56
  DEF(STR, stats_writer_socket_path),
57
  DEF(STR, auth_master_socket_path),
58
  DEF(STR, dovecot_storage_version),
59
  DEF(BOOL, version_ignore),
60
  DEF(BOOL, shutdown_clients),
61
  DEF(BOOL, verbose_proctitle),
62
63
  DEF(STR, haproxy_trusted_networks),
64
  DEF(TIME, haproxy_timeout),
65
66
  { .type = SET_STRLIST, .key = "import_environment",
67
    .offset = offsetof(struct master_service_settings, import_environment) },
68
69
  SETTING_DEFINE_LIST_END
70
};
71
72
static const struct master_service_settings master_service_default_settings = {
73
  .base_dir = PKG_RUNDIR,
74
  .state_dir = PKG_STATEDIR,
75
  .instance_name = PACKAGE,
76
  .log_path = "syslog",
77
  .info_log_path = "",
78
  .debug_log_path = "",
79
  .log_timestamp = DEFAULT_FAILURE_STAMP_FORMAT,
80
  .log_debug = "",
81
  .log_core_filter = "",
82
  .process_shutdown_filter = "",
83
  .syslog_facility = "mail",
84
  .import_environment = ARRAY_INIT,
85
  .stats_writer_socket_path = "stats-writer",
86
  .auth_master_socket_path = "auth-master",
87
  .dovecot_storage_version = "",
88
  .version_ignore = FALSE,
89
  .shutdown_clients = TRUE,
90
  .verbose_proctitle = VERBOSE_PROCTITLE_DEFAULT,
91
92
  .haproxy_trusted_networks = "",
93
  .haproxy_timeout = 3
94
};
95
96
static const struct setting_keyvalue master_service_default_settings_keyvalue[] = {
97
  { "import_environment/TZ", "%{env:TZ}" },
98
  { "import_environment/CORE_OUTOFMEM", "%{env:CORE_OUTOFMEM}" },
99
  { "import_environment/CORE_ERROR", "%{env:CORE_ERROR}" },
100
  { "import_environment/PATH", "%{env:PATH}" },
101
#ifdef HAVE_LIBSYSTEMD
102
  { "import_environment/LISTEN_PID", "%{env:LISTEN_PID}" },
103
  { "import_environment/LISTEN_FDS", "%{env:LISTEN_FDS}" },
104
  { "import_environment/NOTIFY_SOCKET", "%{env:NOTIFY_SOCKET}" },
105
#endif
106
#ifdef __GLIBC__
107
  { "import_environment/MALLOC_MMAP_THRESHOLD_", "131072" },
108
#endif
109
#ifdef DEBUG
110
  { "import_environment/GDB", "%{env:GDB}" },
111
  { "import_environment/DEBUG_SILENT", "%{env:DEBUG_SILENT}" },
112
#endif
113
  { NULL, NULL },
114
};
115
116
const struct setting_parser_info master_service_setting_parser_info = {
117
  .name = "master_service",
118
119
  .defines = master_service_setting_defines,
120
  .defaults = &master_service_default_settings,
121
  .default_settings = master_service_default_settings_keyvalue,
122
123
  .pool_offset1 = 1 + offsetof(struct master_service_settings, pool),
124
  .struct_size = sizeof(struct master_service_settings),
125
  .check_func = master_service_settings_check
126
};
127
128
/* <settings checks> */
129
static bool
130
setting_filter_parse(const char *set_name, const char *set_value,
131
         void (*handle_filter)(struct event_filter *) ATTR_UNUSED,
132
         const char **error_r)
133
0
{
134
0
  struct event_filter *filter;
135
0
  const char *error;
136
137
0
  if (set_value[0] == '\0')
138
0
    return TRUE;
139
140
0
  filter = event_filter_create();
141
0
  if (event_filter_parse(set_value, filter, &error) < 0) {
142
0
    *error_r = t_strdup_printf("Invalid %s: %s", set_name, error);
143
0
    event_filter_unref(&filter);
144
0
    return FALSE;
145
0
  }
146
0
#ifndef CONFIG_BINARY
147
0
  handle_filter(filter);
148
0
#endif
149
0
  event_filter_unref(&filter);
150
0
  return TRUE;
151
0
}
152
153
static void
154
master_service_set_process_shutdown_filter_wrapper(struct event_filter *filter)
155
0
{
156
0
  master_service_set_process_shutdown_filter(master_service, filter);
157
0
}
158
159
static bool storage_version_check(const char *version, const char **error_r)
160
0
{
161
0
#define STORAGE_MIN_VERSION "2.3.0"
162
163
0
  if (version[0] == '\0') {
164
0
    *error_r = "dovecot_storage_version is empty";
165
0
    return FALSE;
166
0
  }
167
0
  if (!version_is_valid(version)) {
168
0
    *error_r = "Invalid dovecot_storage_version value (must be in x.y.z format)";
169
0
    return FALSE;
170
0
  }
171
0
  if (version_cmp(version, STORAGE_MIN_VERSION) < 0) {
172
0
    *error_r = t_strdup_printf(
173
0
      "dovecot_storage_version is too old - "
174
0
      "minimum supported version is %s",
175
0
      STORAGE_MIN_VERSION);
176
0
    return FALSE;
177
0
  }
178
0
  if (version_is_valid(DOVECOT_VERSION) &&
179
0
      version_cmp(version, DOVECOT_VERSION) > 0) {
180
0
    *error_r = t_strdup_printf(
181
0
      "dovecot_storage_version is too new - "
182
0
      "current version is %s", DOVECOT_VERSION);
183
0
    return FALSE;
184
0
  }
185
0
  return TRUE;
186
0
}
187
188
static bool
189
master_service_settings_check(void *_set, pool_t pool ATTR_UNUSED,
190
            const char **error_r)
191
0
{
192
0
  struct master_service_settings *set = _set;
193
0
  int facility;
194
195
0
  if (*set->log_path == '\0') {
196
    /* default to syslog logging */
197
0
    set->log_path = "syslog";
198
0
  }
199
0
  if (!syslog_facility_find(set->syslog_facility, &facility)) {
200
0
    *error_r = t_strdup_printf("Unknown syslog_facility: %s",
201
0
             set->syslog_facility);
202
0
    return FALSE;
203
0
  }
204
205
0
  if (!setting_filter_parse("log_debug", set->log_debug,
206
0
          event_set_global_debug_log_filter, error_r))
207
0
    return FALSE;
208
0
  if (!setting_filter_parse("log_core_filter", set->log_core_filter,
209
0
          event_set_global_core_log_filter, error_r))
210
0
    return FALSE;
211
0
  if (!setting_filter_parse("process_shutdown_filter",
212
0
          set->process_shutdown_filter,
213
0
          master_service_set_process_shutdown_filter_wrapper,
214
0
          error_r))
215
0
    return FALSE;
216
  /* doveconf / config checks dovecot_storage_version separately.
217
     This check shouldn't fail e.g. "doveconf -d" command. */
218
0
  if (settings_get_config_binary() == SETTINGS_BINARY_OTHER &&
219
0
      !storage_version_check(set->dovecot_storage_version, error_r))
220
0
    return FALSE;
221
0
  return TRUE;
222
0
}
223
/* </settings checks> */
224
225
static void strarr_push(ARRAY_TYPE(const_string) *argv, const char *str)
226
0
{
227
0
  array_push_back(argv, &str);
228
0
}
229
230
static void ATTR_NORETURN
231
master_service_exec_config(struct master_service *service,
232
         const struct master_service_settings_input *input)
233
0
{
234
0
  ARRAY_TYPE(const_string) conf_argv;
235
0
  const char *binary_path = service->argv[0];
236
0
  const char *error = NULL;
237
238
0
  if (!t_binary_abspath(&binary_path, &error)) {
239
0
    i_fatal("t_binary_abspath(%s) failed: %s", binary_path, error);
240
0
  }
241
242
0
  if (!service->keep_environment && !input->preserve_environment) {
243
0
    if (input->preserve_home)
244
0
      master_service_import_environment("HOME");
245
0
    if (input->preserve_user)
246
0
      master_service_import_environment("USER");
247
0
    if ((service->flags & MASTER_SERVICE_FLAG_STANDALONE) != 0)
248
0
      master_service_import_environment(DOVECOT_LOG_STDERR_TIMESTAMP_ENV);
249
250
    /* doveconf empties the environment before exec()ing us back
251
       if DOVECOT_PRESERVE_ENVS is set, so make sure it is. */
252
0
    if (getenv(DOVECOT_PRESERVE_ENVS_ENV) == NULL)
253
0
      env_put(DOVECOT_PRESERVE_ENVS_ENV, "");
254
0
  } else {
255
    /* make sure doveconf doesn't remove any environment */
256
0
    env_remove(DOVECOT_PRESERVE_ENVS_ENV);
257
0
  }
258
0
  if (input->use_sysexits)
259
0
    env_put("USE_SYSEXITS", "1");
260
261
0
  if (input->protocol != NULL)
262
0
    env_put("DOVECONF_PROTOCOL", input->protocol);
263
264
0
  t_array_init(&conf_argv, 11 + (service->argc + 1) + 1);
265
0
  const char *config_bin_path = getenv(DOVECOT_CONFIG_BIN_PATH_ENV);
266
0
  if (config_bin_path == NULL)
267
0
    config_bin_path = DOVECOT_CONFIG_BIN_PATH;
268
0
  strarr_push(&conf_argv, config_bin_path);
269
0
  if ((service->flags & MASTER_SERVICE_FLAG_CONFIG_DEFAULTS) != 0)
270
0
    strarr_push(&conf_argv, "-d");
271
0
  else {
272
0
    strarr_push(&conf_argv, "-c");
273
0
    strarr_push(&conf_argv, service->config_path);
274
0
  }
275
276
0
  if (input->check_full_config)
277
0
    strarr_push(&conf_argv, "-C");
278
0
  if (input->hide_obsolete_warnings)
279
0
    strarr_push(&conf_argv, "-w");
280
0
  strarr_push(&conf_argv, "-F");
281
0
  strarr_push(&conf_argv, binary_path);
282
0
  array_append(&conf_argv, (const char *const *)service->argv + 1,
283
0
         service->argc);
284
0
  array_append_zero(&conf_argv);
285
286
0
  const char *const *argv = array_front(&conf_argv);
287
0
  execv_const(argv[0], argv);
288
0
}
289
290
static void
291
config_error_update_path_source(struct master_service *service,
292
        const struct master_service_settings_input *input,
293
        const char **error)
294
0
{
295
0
  if (input->config_path == NULL && service->config_path_from_master) {
296
0
    *error = t_strdup_printf("%s (path is from %s environment)",
297
0
           *error, MASTER_CONFIG_FILE_ENV);
298
0
  }
299
0
}
300
301
static void
302
config_exec_fallback(struct master_service *service,
303
         const struct master_service_settings_input *input,
304
         const char **error)
305
0
{
306
0
  const char *path, *stat_error;
307
0
  struct stat st;
308
0
  int saved_errno = errno;
309
310
0
  if (input->never_exec) {
311
0
    *error = t_strdup_printf(
312
0
      "%s - doveconf execution fallback is disabled", *error);
313
0
    return;
314
0
  }
315
316
0
  path = input->config_path != NULL ? input->config_path :
317
0
    master_service_get_config_path(service);
318
0
  if (stat(path, &st) < 0)
319
0
    stat_error = t_strdup_printf("stat(%s) failed: %m", path);
320
0
  else if (S_ISSOCK(st.st_mode))
321
0
    stat_error = t_strdup_printf("%s is a UNIX socket", path);
322
0
  else if (S_ISFIFO(st.st_mode))
323
0
    stat_error = t_strdup_printf("%s is a FIFO", path);
324
0
  else {
325
    /* it's a file, not a socket/pipe */
326
0
    master_service_exec_config(service, input);
327
0
  }
328
0
  *error = t_strdup_printf(
329
0
    "%s - Also failed to read config by executing doveconf: %s",
330
0
    *error, stat_error);
331
0
  config_error_update_path_source(service, input, error);
332
0
  errno = saved_errno;
333
0
}
334
335
static int master_service_binary_config_cache_get(const char *cache_dir,
336
              const char *input_path)
337
0
{
338
0
  if (cache_dir == NULL)
339
0
    return -1;
340
341
0
  const char *cache_path =
342
0
    master_service_get_binary_config_cache_path(cache_dir, input_path);
343
0
  int fd = open(cache_path, O_RDONLY);
344
0
  if (fd == -1 && errno != ENOENT)
345
0
    i_error("Binary config cache: open(%s) failed: %m", cache_path);
346
0
  else if (fd != -1)
347
0
    fd_close_on_exec(fd, TRUE);
348
0
  return fd;
349
0
}
350
351
static int
352
master_service_open_config(struct master_service *service,
353
         const struct master_service_settings_input *input,
354
         const char *cache_dir,
355
         const char **path_r, bool *cached_config_r,
356
         const char **error_r)
357
0
{
358
0
  struct stat st;
359
0
  const char *path;
360
0
  int fd = -1;
361
362
0
  if ((service->flags & MASTER_SERVICE_FLAG_CONFIG_DEFAULTS) != 0)
363
0
    path = MASTER_SERVICE_BINARY_CONFIG_DEFAULTS;
364
0
  else if (input->config_path != NULL)
365
0
    path = input->config_path;
366
0
  else
367
0
    path = master_service_get_config_path(service);
368
0
  *path_r = path;
369
0
  *cached_config_r = FALSE;
370
371
0
  if ((fd = master_service_binary_config_cache_get(cache_dir, path)) != -1) {
372
0
    *cached_config_r = TRUE;
373
0
    return fd;
374
0
  }
375
376
0
  if ((service->flags & MASTER_SERVICE_FLAG_CONFIG_DEFAULTS) != 0)
377
0
    master_service_exec_config(service, input);
378
379
0
  if (!service->config_path_from_master &&
380
0
      !service->config_path_changed_with_param &&
381
0
      !input->always_exec &&
382
0
      input->config_path == NULL) {
383
    /* first try to connect to the default config socket.
384
       configuration may contain secrets, so in default config
385
       this fails because the socket is 0600. it's useful for
386
       developers though. :) */
387
0
    fd = net_connect_unix(DOVECOT_CONFIG_SOCKET_PATH);
388
0
    if (fd >= 0)
389
0
      *path_r = DOVECOT_CONFIG_SOCKET_PATH;
390
0
    else {
391
      /* fallback to executing doveconf */
392
0
    }
393
0
  }
394
395
0
  if (fd == -1) {
396
0
    if (stat(path, &st) < 0) {
397
0
      *error_r = errno == EACCES ?
398
0
        eacces_error_get("stat", path) :
399
0
        t_strdup_printf("stat(%s) failed: %m", path);
400
0
      config_error_update_path_source(service, input, error_r);
401
0
      return -1;
402
0
    }
403
404
0
    if (!S_ISSOCK(st.st_mode) && !S_ISFIFO(st.st_mode)) {
405
      /* it's not an UNIX socket, don't even try to connect */
406
0
      fd = -1;
407
0
      errno = ENOTSOCK;
408
0
    } else {
409
0
      fd = net_connect_unix_with_retries(path, 1000);
410
0
    }
411
0
    if (fd < 0) {
412
0
      *error_r = t_strdup_printf(
413
0
        "net_connect_unix(%s) failed: %m", path);
414
0
      config_exec_fallback(service, input, error_r);
415
0
      return -1;
416
0
    }
417
0
  }
418
0
  net_set_nonblock(fd, FALSE);
419
0
  string_t *str = t_str_new(128);
420
0
  str_append(str, CONFIG_HANDSHAKE"REQ");
421
0
  if (input->reload_config)
422
0
    str_append(str, "\treload");
423
0
  str_append_c(str, '\n');
424
0
  alarm(CONFIG_READ_TIMEOUT_SECS);
425
0
  int ret = write_full(fd, str_data(str), str_len(str));
426
0
  if (ret < 0)
427
0
    *error_r = t_strdup_printf("write_full(%s) failed: %m", path);
428
0
  else
429
0
    *error_r = NULL;
430
431
0
  int config_fd = -1;
432
0
  if (ret == 0) {
433
    /* read the config fd as reply */
434
0
    char buf[1024];
435
0
    ret = fd_read(fd, buf, sizeof(buf)-1, &config_fd);
436
0
    if (ret < 0)
437
0
      *error_r = t_strdup_printf("fd_read() failed: %m");
438
0
    else if (ret > 0 && buf[0] == '+' && buf[1] == '\n') {
439
      /* success, if fd was received */
440
0
      if (config_fd == -1)
441
0
        *error_r = "Failed to read config: FD not received";
442
0
    } else if (ret > 0 && buf[0] == '-') {
443
0
      buf[ret] = '\0';
444
0
      *error_r = t_strdup_printf("Failed to read config: %s",
445
0
               t_strcut(buf+1, '\n'));
446
0
      i_close_fd(&config_fd);
447
0
    } else {
448
0
      buf[ret] = '\0';
449
0
      *error_r = t_strdup_printf(
450
0
        "Failed to read config: Unexpected reply '%s'",
451
0
        t_strcut(buf, '\n'));
452
0
      i_close_fd(&config_fd);
453
0
    }
454
0
  }
455
0
  alarm(0);
456
0
  i_close_fd(&fd);
457
458
0
  if (config_fd == -1) {
459
0
    i_assert(*error_r != NULL);
460
0
    config_exec_fallback(service, input, error_r);
461
0
    return -1;
462
0
  }
463
0
  return config_fd;
464
0
}
465
466
static void
467
master_service_append_config_overrides(struct master_service *service)
468
0
{
469
0
  const char *const *cli_overrides;
470
0
  unsigned int i, count;
471
472
0
  if (!array_is_created(&service->config_overrides))
473
0
    return;
474
475
0
  cli_overrides = array_get(&service->config_overrides, &count);
476
0
  for (i = 0; i < count; i++) {
477
0
    const char *key, *value;
478
0
    t_split_key_value_eq(cli_overrides[i], &key, &value);
479
480
0
    settings_root_override(service->settings_root, key, value,
481
0
               SETTINGS_OVERRIDE_TYPE_CLI_PARAM);
482
0
  }
483
0
}
484
485
static int
486
master_service_settings_read_int(struct master_service *service,
487
         const struct master_service_settings_input *input,
488
         const char *cache_dir,
489
         struct master_service_settings_output *output_r,
490
         const char **error_r)
491
0
{
492
0
  const char *path = NULL, *value, *error;
493
0
  bool cached_config = FALSE;
494
0
  bool import_environment_missing = FALSE;
495
0
  int ret, fd = -1;
496
497
0
  i_zero(output_r);
498
0
  output_r->config_fd = -1;
499
500
0
  if (input->config_fd > 0) {
501
    /* unit test */
502
0
    fd = input->config_fd;
503
0
    path = t_strdup_printf("<input fd %d>", fd);
504
0
  } else if (settings_has_mmap(service->settings_root) &&
505
0
       !input->reload_config) {
506
    /* config was already read once */
507
0
  } else if ((value = getenv(DOVECOT_CONFIG_FD_ENV)) != NULL) {
508
    /* doveconf -F parameter already executed us back.
509
       The configuration is in DOVECOT_CONFIG_FD. */
510
0
    if (str_to_int(value, &fd) < 0 || fd < 0)
511
0
      i_fatal("Invalid "DOVECOT_CONFIG_FD_ENV": %s", value);
512
0
    path = t_strdup_printf("<"DOVECOT_CONFIG_FD_ENV" %d>", fd);
513
0
  } else if ((service->flags & MASTER_SERVICE_FLAG_CONFIG_BUILTIN) == 0) {
514
    /* Open config via socket if possible. If it doesn't work,
515
       execute doveconf -F. */
516
0
    T_BEGIN {
517
0
      fd = master_service_open_config(service, input,
518
0
              cache_dir, &path,
519
0
              &cached_config, &error);
520
0
    } T_END_PASS_STR_IF(fd == -1, &error);
521
0
    if (fd == -1) {
522
0
      if (errno == EACCES)
523
0
        output_r->permission_denied = TRUE;
524
0
      *error_r = t_strdup_printf(
525
0
        "Failed to read configuration: %s", error);
526
0
      return -1;
527
0
    }
528
0
    if (getenv(MASTER_IS_PARENT_ENV) == NULL) {
529
      /* Standalone program read the config via socket or
530
         config cache (not via executing doveconf).
531
         import_environment setting still needs to be
532
         processed. */
533
0
      import_environment_missing = TRUE;
534
0
    }
535
0
  } else if (!settings_has_mmap(service->settings_root)) {
536
    /* Use default settings. Set dovecot_storage_version to the
537
       latest version, so it won't cause a failure. Use userdb
538
       type, so it can still be overridden with -o parameter.
539
540
       When building from git we don't know the latest version, so
541
       just use 9999. The version validity checks are disabled for
542
       git builds, so this should work. */
543
0
    const char *version = version_is_valid(DOVECOT_VERSION) ?
544
0
      DOVECOT_VERSION : "9999";
545
0
    settings_root_override(service->settings_root,
546
0
               "dovecot_storage_version", version,
547
0
               SETTINGS_OVERRIDE_TYPE_USERDB);
548
0
  }
549
0
  if (!settings_has_mmap(service->settings_root)) {
550
    /* first time reading settings */
551
0
    master_service_append_config_overrides(service);
552
0
  }
553
0
  if (fd != -1) {
554
0
    const char *service_name = input->no_service_filter ?
555
0
      NULL : service->name;
556
0
    const char *protocol_name = input->protocol != NULL ?
557
0
      input->protocol : service->name;
558
0
    enum settings_read_flags read_flags =
559
0
      !input->no_protocol_filter ? 0 :
560
0
      SETTINGS_READ_NO_PROTOCOL_FILTER;
561
0
    if (cached_config)
562
0
      read_flags |= SETTINGS_READ_CHECK_CACHE_TIMESTAMPS;
563
0
    ret = settings_read(service->settings_root, fd, path,
564
0
            service_name, protocol_name, read_flags,
565
0
            &output_r->specific_protocols,
566
0
            &error);
567
0
    if (ret == 0 && cached_config) {
568
      /* out-of-date binary config cache */
569
0
      i_close_fd(&fd);
570
0
      return master_service_settings_read_int(service, input,
571
0
            NULL, output_r, error_r);
572
0
    }
573
0
    if (input->return_config_fd)
574
0
      output_r->config_fd = fd;
575
0
    else
576
0
      i_close_fd(&fd);
577
0
    if (ret < 0) {
578
0
      if (getenv(DOVECOT_CONFIG_FD_ENV) != NULL) {
579
0
        i_fatal("Failed to parse config from fd %d: %s",
580
0
          fd, error);
581
0
      }
582
0
      *error_r = t_strdup_printf(
583
0
        "Failed to parse configuration: %s", error);
584
0
      return -1;
585
0
    }
586
0
    env_remove(DOVECOT_CONFIG_FD_ENV);
587
0
  }
588
589
  /* Create event for matching config filters */
590
0
  struct event *event = event_create(NULL);
591
0
  event_add_str(event, "protocol", input->protocol != NULL ?
592
0
          input->protocol : service->name);
593
594
0
  settings_free(service->set);
595
0
  ret = settings_get(event, &master_service_setting_parser_info,
596
0
         !input->no_key_validation ? 0 :
597
0
         SETTINGS_GET_NO_KEY_VALIDATION,
598
0
         &service->set, error_r);
599
0
  event_unref(&event);
600
0
  if (ret < 0)
601
0
    return -1;
602
603
0
  if (service->set->version_ignore &&
604
0
      (service->flags & MASTER_SERVICE_FLAG_STANDALONE) != 0) {
605
    /* running standalone. we want to ignore plugin versions. */
606
0
    service->version_string = NULL;
607
0
  }
608
0
  if ((service->flags & MASTER_SERVICE_FLAG_DONT_SEND_STATS) == 0 &&
609
0
      (service->flags & MASTER_SERVICE_FLAG_STANDALONE) != 0) {
610
    /* When running standalone (e.g. doveadm) try to connect to the
611
       stats socket, but don't log an error if it's not running.
612
       It may be intentional. Non-standalone stats-client
613
       initialization was already done earlier. */
614
0
    master_service_init_stats_client(service, TRUE);
615
0
  }
616
617
0
  if (service->set->shutdown_clients)
618
0
    master_service_set_die_with_master(master_service, TRUE);
619
620
0
  if (import_environment_missing) {
621
0
    const char *import_environment =
622
0
      master_service_get_import_environment_keyvals(service);
623
0
    master_service_import_environment(import_environment);
624
    /* DOVECOT_HOST* environments may have changed */
625
0
    hostpid_init();
626
0
  }
627
0
  return 0;
628
0
}
629
630
int master_service_settings_read(struct master_service *service,
631
         const struct master_service_settings_input *input,
632
         struct master_service_settings_output *output_r,
633
         const char **error_r)
634
0
{
635
0
  return master_service_settings_read_int(service, input,
636
0
            getenv("DOVECOT_CONFIG_CACHE"),
637
0
            output_r, error_r);
638
0
}
639
640
int master_service_settings_read_simple(struct master_service *service,
641
          const char **error_r)
642
0
{
643
0
  struct master_service_settings_input input;
644
0
  struct master_service_settings_output output;
645
646
0
  i_zero(&input);
647
0
  return master_service_settings_read(service, &input, &output, error_r);
648
0
}
649
650
const struct master_service_settings *
651
master_service_get_service_settings(struct master_service *service)
652
0
{
653
0
  return service->set;
654
0
}
655
656
const char *
657
master_service_get_import_environment_keyvals(struct master_service *service)
658
0
{
659
0
  ARRAY_TYPE(const_string) arr = service->set->import_environment;
660
0
  unsigned int len = array_count(&arr);
661
0
  string_t *keyvals = t_str_new(64);
662
0
  for (unsigned int i = 0; i < len; i += 2) {
663
0
    const char *const *key = array_idx(&arr, i);
664
0
    const char *const *val = array_idx(&arr, i + 1);
665
0
    str_append_tabescaped(keyvals, *key);
666
0
    str_append_c(keyvals, '=');
667
0
    str_append_tabescaped(keyvals, *val);
668
669
0
    if (i + 2 < len)
670
0
      str_append_c(keyvals, '\t');
671
0
  }
672
0
  return str_c(keyvals);
673
0
}
674
675
const char *
676
master_service_get_binary_config_cache_path(const char *cache_dir,
677
              const char *main_path)
678
0
{
679
0
  struct sha256_ctx hash;
680
0
  sha256_init(&hash);
681
0
  sha256_loop(&hash, main_path, strlen(main_path) + 1);
682
0
  uid_t euid = geteuid();
683
0
  sha256_loop(&hash, &euid, sizeof(euid));
684
0
  gid_t egid = getegid();
685
0
  sha256_loop(&hash, &egid, sizeof(egid));
686
0
  unsigned int groups_count;
687
0
  const gid_t *groups = restrict_get_groups_list(&groups_count);
688
0
  sha256_loop(&hash, groups, sizeof(*groups) * groups_count);
689
0
  unsigned char digest[SHA256_RESULTLEN];
690
0
  sha256_result(&hash, digest);
691
692
0
  string_t *cache_path = t_str_new(128);
693
0
  str_append(cache_path, cache_dir);
694
0
  str_append(cache_path, "/binary.conf.");
695
0
  binary_to_hex_append(cache_path, digest, sizeof(digest));
696
0
  return str_c(cache_path);
697
0
}