master-service.c revision 0c22bef8f5b35c645de8affd8746307fc53bd222
5f5870385cff47efd2f58e7892f251cf13761528Timo Sirainen/* Copyright (C) 2005-2009 Timo Sirainen */
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen#include "lib.h"
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen#include "lib-signals.h"
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen#include "ioloop.h"
3343a61404603b21c246783a7963b77833095f31Timo Sirainen#include "array.h"
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen#include "env-util.h"
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen#include "home-expand.h"
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen#include "restrict-access.h"
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen#include "fd-close-on-exec.h"
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen#include "settings-parser.h"
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen#include "syslog-util.h"
3ed2d0f6b5e67e2663d44489d9da3176823789a8Timo Sirainen#include "master-service-private.h"
573f0491a5733fe21fa062a455acb4790b4e0499Timo Sirainen#include "master-service-settings.h"
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen#include <stdlib.h>
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen#include <unistd.h>
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen#include <sys/stat.h>
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen#include <syslog.h>
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen#define DEFAULT_CONFIG_FILE_PATH SYSCONFDIR"/dovecot.conf"
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen
04ab375449dd97eed50ada88dd0df2abab01cfeeTimo Sirainen/* getenv(MASTER_CONFIG_FILE_ENV) provides path to configuration file/socket */
c000c8eca8f24b2a0c76393ec4bbf76a505a4983Timo Sirainen#define MASTER_CONFIG_FILE_ENV "CONFIG_FILE"
04ab375449dd97eed50ada88dd0df2abab01cfeeTimo Sirainen
04ab375449dd97eed50ada88dd0df2abab01cfeeTimo Sirainen/* getenv(MASTER_DOVECOT_VERSION_ENV) provides master's version number */
04ab375449dd97eed50ada88dd0df2abab01cfeeTimo Sirainen#define MASTER_DOVECOT_VERSION_ENV "DOVECOT_VERSION"
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen
c000c8eca8f24b2a0c76393ec4bbf76a505a4983Timo Sirainenstruct master_service *master_service;
c000c8eca8f24b2a0c76393ec4bbf76a505a4983Timo Sirainen
c000c8eca8f24b2a0c76393ec4bbf76a505a4983Timo Sirainenstatic void io_listeners_add(struct master_service *service);
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainenstatic void io_listeners_remove(struct master_service *service);
3f190f4cbb9233a3a6830956cb5c7ae56a577b79Timo Sirainenstatic void master_status_update(struct master_service *service);
3f190f4cbb9233a3a6830956cb5c7ae56a577b79Timo Sirainen
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainenconst char *master_service_getopt_string(void)
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen{
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen return "c:ko:Os:L";
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen}
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainenstatic void sig_die(const siginfo_t *si, void *context)
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen{
4307c886579381dbb1897ea1388ae6978c96f560Timo Sirainen struct master_service *service = context;
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen /* warn about being killed because of some signal, except SIGINT (^C)
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen which is too common at least while testing :) */
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen if (si->si_signo != SIGINT) {
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen i_warning("Killed with signal %d (by pid=%s uid=%s code=%s)",
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen si->si_signo, dec2str(si->si_pid),
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen dec2str(si->si_uid),
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen lib_signal_code_to_str(si->si_signo, si->si_code));
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen }
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen io_loop_stop(service->ioloop);
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen}
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainenstatic void master_service_verify_version(struct master_service *service)
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen{
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen if (service->version_string != NULL &&
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen strcmp(service->version_string, PACKAGE_VERSION) != 0) {
0d16525a729011f4fced989a3da74d755ea49e6dTimo Sirainen i_fatal("Dovecot version mismatch: "
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen "Master is v%s, %s is v"PACKAGE_VERSION" "
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen "(if you don't care, set version_ignore=yes)",
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen service->name, service->version_string);
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen }
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen}
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainenstruct master_service *
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainenmaster_service_init(const char *name, enum master_service_flags flags,
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen int argc, char *argv[])
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen{
04ab375449dd97eed50ada88dd0df2abab01cfeeTimo Sirainen struct master_service *service;
c000c8eca8f24b2a0c76393ec4bbf76a505a4983Timo Sirainen const char *str;
8ed8c821ba8aab0b4ed0375f87d48737ef0e0d8eTimo Sirainen
8ed8c821ba8aab0b4ed0375f87d48737ef0e0d8eTimo Sirainen i_assert(name != NULL);
8ed8c821ba8aab0b4ed0375f87d48737ef0e0d8eTimo Sirainen
8ed8c821ba8aab0b4ed0375f87d48737ef0e0d8eTimo Sirainen#ifdef DEBUG
8ed8c821ba8aab0b4ed0375f87d48737ef0e0d8eTimo Sirainen if (getenv("GDB") == NULL &&
04ab375449dd97eed50ada88dd0df2abab01cfeeTimo Sirainen (flags & MASTER_SERVICE_FLAG_STANDALONE) == 0) {
8ed8c821ba8aab0b4ed0375f87d48737ef0e0d8eTimo Sirainen int count;
8ed8c821ba8aab0b4ed0375f87d48737ef0e0d8eTimo Sirainen
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen str = getenv("SOCKET_COUNT");
8ed8c821ba8aab0b4ed0375f87d48737ef0e0d8eTimo Sirainen count = str == NULL ? 0 : atoi(str);
8ed8c821ba8aab0b4ed0375f87d48737ef0e0d8eTimo Sirainen fd_debug_verify_leaks(MASTER_LISTEN_FD_FIRST + count, 1024);
8ed8c821ba8aab0b4ed0375f87d48737ef0e0d8eTimo Sirainen }
573f0491a5733fe21fa062a455acb4790b4e0499Timo Sirainen#endif
573f0491a5733fe21fa062a455acb4790b4e0499Timo Sirainen
d42eb03b3a4e79a2da22a1be2de59b95660af2beTimo Sirainen /* NOTE: we start rooted, so keep the code minimal until
573f0491a5733fe21fa062a455acb4790b4e0499Timo Sirainen restrict_access_by_env() is called */
8ed8c821ba8aab0b4ed0375f87d48737ef0e0d8eTimo Sirainen lib_init();
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen /* Set a logging prefix temporarily. This will be ignored once the log
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen is properly initialized */
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen i_set_failure_prefix(t_strdup_printf("%s(init): ", name));
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen
f0a2d04321ba456e5c5ba821c0d1ed9e8e0e2e08Timo Sirainen if (getenv(MASTER_UID_ENV) == NULL)
8ed8c821ba8aab0b4ed0375f87d48737ef0e0d8eTimo Sirainen flags |= MASTER_SERVICE_FLAG_STANDALONE;
8ed8c821ba8aab0b4ed0375f87d48737ef0e0d8eTimo Sirainen
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen service = i_new(struct master_service, 1);
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen service->argc = argc;
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen service->argv = argv;
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen service->name = i_strdup(name);
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen service->flags = flags;
d22301419109ed4a38351715e6760011421dadecTimo Sirainen service->ioloop = io_loop_create();
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen service->service_count_left = (unsigned int)-1;
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen service->config_fd = -1;
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen
d22301419109ed4a38351715e6760011421dadecTimo Sirainen service->config_path = getenv(MASTER_CONFIG_FILE_ENV);
4145cbac82bfc0c8bfeceeca0ef841700117930cTimo Sirainen if (service->config_path == NULL)
c000c8eca8f24b2a0c76393ec4bbf76a505a4983Timo Sirainen service->config_path = DEFAULT_CONFIG_FILE_PATH;
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen if ((flags & MASTER_SERVICE_FLAG_STANDALONE) == 0) {
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen service->version_string = getenv(MASTER_DOVECOT_VERSION_ENV);
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen service->socket_count = 1;
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen } else {
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen service->version_string = PACKAGE_VERSION;
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen }
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen str = getenv("SOCKET_COUNT");
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen if (str != NULL)
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen service->socket_count = atoi(str);
3d4c24127f4f83259c0f81851184abc34793dbe0Timo Sirainen str = getenv("SSL_SOCKET_COUNT");
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen if (str != NULL)
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen service->ssl_socket_count = atoi(str);
3d4c24127f4f83259c0f81851184abc34793dbe0Timo Sirainen
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen /* set up some kind of logging until we know exactly how and where
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen we want to log */
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen if (getenv("LOG_SERVICE") != NULL)
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen i_set_failure_internal();
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen if (getenv("USER") != NULL) {
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen i_set_failure_prefix(t_strdup_printf("%s(%s): ",
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen name, getenv("USER")));
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen } else {
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen i_set_failure_prefix(t_strdup_printf("%s: ", name));
cd56a23e21f1df3f79648cf07e2f4385e2fadebbTimo Sirainen }
04ab375449dd97eed50ada88dd0df2abab01cfeeTimo Sirainen
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen master_service_verify_version(service);
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen return service;
04ab375449dd97eed50ada88dd0df2abab01cfeeTimo Sirainen}
c000c8eca8f24b2a0c76393ec4bbf76a505a4983Timo Sirainen
424236b2b88a5a7bbde5cf6a6b32189ca3437629Timo Sirainenvoid master_service_init_log(struct master_service *service, const char *prefix,
04ab375449dd97eed50ada88dd0df2abab01cfeeTimo Sirainen unsigned int max_lines_per_sec)
cd56a23e21f1df3f79648cf07e2f4385e2fadebbTimo Sirainen{
04ab375449dd97eed50ada88dd0df2abab01cfeeTimo Sirainen const char *path;
04ab375449dd97eed50ada88dd0df2abab01cfeeTimo Sirainen
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen if ((service->flags & MASTER_SERVICE_FLAG_STANDALONE) != 0 &&
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen (service->flags & MASTER_SERVICE_FLAG_DONT_LOG_TO_STDERR) == 0) {
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen i_set_failure_file("/dev/stderr", "");
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen return;
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen }
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen if (getenv("LOG_SERVICE") != NULL && !service->log_directly) {
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen /* logging via log service */
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen i_set_failure_internal();
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen i_set_failure_prefix(prefix);
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen return;
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen }
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen
7501b9f694460101b41d1d708ebc3ec2b0400b1cTimo Sirainen if (service->set == NULL) {
ccc895c0358108d2304239063e940b7d75f364abTimo Sirainen i_set_failure_file("/dev/stderr", prefix);
7501b9f694460101b41d1d708ebc3ec2b0400b1cTimo Sirainen return;
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen }
690bafa70767e3f6e98bbfd62ad4a26be2387ea9Timo Sirainen
690bafa70767e3f6e98bbfd62ad4a26be2387ea9Timo Sirainen if (*service->set->log_path == '\0') {
6060b7c8edf8fce73470d0df6a2479b69b01c537Timo Sirainen /* log to syslog */
7501b9f694460101b41d1d708ebc3ec2b0400b1cTimo Sirainen int facility;
7501b9f694460101b41d1d708ebc3ec2b0400b1cTimo Sirainen
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen if (!syslog_facility_find(service->set->syslog_facility,
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen &facility))
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen facility = LOG_MAIL;
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen i_set_failure_syslog("dovecot", LOG_NDELAY, facility);
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen i_set_failure_prefix(prefix);
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen } else {
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen /* log to file or stderr */
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen path = home_expand(service->set->log_path);
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen i_set_failure_file(path, prefix);
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen }
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen path = home_expand(service->set->info_log_path);
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen if (*path != '\0')
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen i_set_info_file(path);
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen i_set_failure_timestamp_format(service->set->log_timestamp);
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen}
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainenbool master_service_parse_option(struct master_service *service,
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen int opt, const char *arg)
e5afebd2df1d4990f7bec2a839260ff2e6d78168Timo Sirainen{
ccc895c0358108d2304239063e940b7d75f364abTimo Sirainen int i;
ff7056842f14fd3b30a2d327dfab165b9d15dd30Timo Sirainen
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen switch (opt) {
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen case 'c':
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen service->config_path = arg;
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen break;
690bafa70767e3f6e98bbfd62ad4a26be2387ea9Timo Sirainen case 'k':
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen service->keep_environment = TRUE;
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen break;
ccc895c0358108d2304239063e940b7d75f364abTimo Sirainen case 'o':
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen if (!array_is_created(&service->config_overrides))
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen i_array_init(&service->config_overrides, 16);
690bafa70767e3f6e98bbfd62ad4a26be2387ea9Timo Sirainen array_append(&service->config_overrides, &arg, 1);
690bafa70767e3f6e98bbfd62ad4a26be2387ea9Timo Sirainen break;
6060b7c8edf8fce73470d0df6a2479b69b01c537Timo Sirainen case 'O':
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen service->default_settings = TRUE;
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen break;
690bafa70767e3f6e98bbfd62ad4a26be2387ea9Timo Sirainen case 's':
3343a61404603b21c246783a7963b77833095f31Timo Sirainen if ((i = atoi(arg)) < 0)
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen i_fatal("Invalid socket count: %s", arg);
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen service->socket_count = i;
3343a61404603b21c246783a7963b77833095f31Timo Sirainen break;
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen case 'L':
3343a61404603b21c246783a7963b77833095f31Timo Sirainen service->log_directly = TRUE;
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen break;
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen default:
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen return FALSE;
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen }
return TRUE;
}
static void master_status_error(void *context)
{
struct master_service *service = context;
/* status fd is a write-only pipe, so if we're here it means the
master wants us to die (or died itself). don't die until all
service connections are finished. */
io_remove(&service->io_status_error);
/* the log fd may also be closed already, don't die when trying to
log later */
i_set_failure_ignore_errors(TRUE);
if (service->master_status.available_count ==
service->total_available_count)
master_service_stop(service);
}
void master_service_init_finish(struct master_service *service)
{
struct stat st;
const char *value;
unsigned int count;
i_assert(service->total_available_count == 0);
i_assert(service->service_count_left == (unsigned int)-1);
/* set default signal handlers */
lib_signals_init();
lib_signals_ignore(SIGPIPE, TRUE);
lib_signals_ignore(SIGALRM, FALSE);
lib_signals_set_handler(SIGINT, TRUE, sig_die, service);
lib_signals_set_handler(SIGTERM, TRUE, sig_die, service);
if ((service->flags & MASTER_SERVICE_FLAG_STANDALONE) == 0) {
if (fstat(MASTER_STATUS_FD, &st) < 0 || !S_ISFIFO(st.st_mode))
i_fatal("Must be started by dovecot master process");
/* initialize master_status structure */
value = getenv(MASTER_UID_ENV);
if (value == NULL)
i_fatal(MASTER_UID_ENV" not set");
service->master_status.pid = getpid();
service->master_status.uid =
(unsigned int)strtoul(value, NULL, 10);
/* set the default limit */
value = getenv(MASTER_CLIENT_LIMIT_ENV);
count = value == NULL ? 0 :
(unsigned int)strtoul(value, NULL, 10);
if (count == 0)
i_fatal(MASTER_CLIENT_LIMIT_ENV" not set");
master_service_set_client_limit(service, count);
/* start listening errors for status fd, it means master died */
service->io_status_error = io_add(MASTER_STATUS_FD, IO_ERROR,
master_status_error, service);
} else {
master_service_set_client_limit(service, 1);
master_service_set_service_count(service, 1);
}
io_listeners_add(service);
if ((service->flags & MASTER_SERVICE_FLAG_STD_CLIENT) != 0) {
/* we already have a connection to be served */
service->master_status.available_count--;
}
master_status_update(service);
}
void master_service_env_clean(bool preserve_home)
{
const char *user, *tz, *home;
#ifdef DEBUG
bool gdb = getenv("GDB") != NULL;
#endif
user = getenv("USER");
if (user != NULL)
user = t_strconcat("USER=", user, NULL);
tz = getenv("TZ");
if (tz != NULL)
tz = t_strconcat("TZ=", tz, NULL);
home = preserve_home ? getenv("HOME") : NULL;
if (home != NULL)
home = t_strconcat("HOME=", home, NULL);
/* Note that if the original environment was set with env_put(), the
environment strings will be invalid after env_clean(). That's why
we t_strconcat() them above. */
env_clean();
if (user != NULL) env_put(user);
if (tz != NULL) env_put(tz);
if (home != NULL) env_put(home);
#ifdef DEBUG
if (gdb) env_put("GDB=1");
#endif
}
void master_service_set_client_limit(struct master_service *service,
unsigned int client_limit)
{
i_assert(service->master_status.available_count ==
service->total_available_count);
service->total_available_count = client_limit;
service->master_status.available_count = client_limit;
}
unsigned int master_service_get_client_limit(struct master_service *service)
{
return service->total_available_count;
}
void master_service_set_service_count(struct master_service *service,
unsigned int count)
{
unsigned int used;
used = service->total_available_count -
service->master_status.available_count;
i_assert(count >= used);
if (service->total_available_count > count) {
service->total_available_count = count;
service->master_status.available_count = count - used;
}
service->service_count_left = count;
}
unsigned int master_service_get_service_count(struct master_service *service)
{
return service->service_count_left;
}
unsigned int master_service_get_socket_count(struct master_service *service)
{
return service->socket_count;
}
const char *master_service_get_config_path(struct master_service *service)
{
return service->config_path;
}
const char *master_service_get_version_string(struct master_service *service)
{
return service->version_string;
}
void master_service_run(struct master_service *service,
master_service_connection_callback_t *callback)
{
service->callback = callback;
io_loop_run(service->ioloop);
service->callback = NULL;
}
void master_service_stop(struct master_service *service)
{
io_loop_stop(service->ioloop);
}
void master_service_anvil_send(struct master_service *service, const char *cmd)
{
ssize_t ret;
if ((service->flags & MASTER_SERVICE_FLAG_STANDALONE) != 0)
return;
ret = write(MASTER_ANVIL_FD, cmd, strlen(cmd));
if (ret < 0)
i_error("write(anvil) failed: %m");
else if (ret == 0)
i_error("write(anvil) failed: EOF");
else {
i_assert((size_t)ret == strlen(cmd));
}
}
void master_service_client_connection_destroyed(struct master_service *service)
{
if (service->listeners == NULL) {
/* we can listen again */
io_listeners_add(service);
}
i_assert(service->total_available_count > 0);
if (service->service_count_left != service->total_available_count) {
i_assert(service->service_count_left == (unsigned int)-1);
service->master_status.available_count++;
} else {
/* we have only limited amount of service requests left */
i_assert(service->service_count_left > 0);
service->service_count_left--;
service->total_available_count--;
if (service->service_count_left == 0) {
i_assert(service->master_status.available_count ==
service->total_available_count);
master_service_stop(service);
}
}
master_status_update(service);
if (service->io_status_error == NULL &&
service->master_status.available_count ==
service->total_available_count) {
/* master has closed the connection and we have nothing else
to do anymore. */
master_service_stop(service);
}
}
void master_service_deinit(struct master_service **_service)
{
struct master_service *service = *_service;
*_service = NULL;
io_listeners_remove(service);
if (service->io_status_error != NULL)
io_remove(&service->io_status_error);
if (service->io_status_write != NULL)
io_remove(&service->io_status_write);
if (array_is_created(&service->config_overrides))
array_free(&service->config_overrides);
if (service->set_parser != NULL) {
settings_parser_deinit(&service->set_parser);
pool_unref(&service->set_pool);
}
lib_signals_deinit();
io_loop_destroy(&service->ioloop);
i_free(service->name);
i_free(service);
lib_deinit();
}
static void master_service_listen(struct master_service_listener *l)
{
struct master_service_connection conn;
if (l->service->master_status.available_count == 0) {
/* we are full. stop listening for now. */
io_listeners_remove(l->service);
return;
}
memset(&conn, 0, sizeof(conn));
conn.listen_fd = l->fd;
conn.fd = net_accept(l->fd, &conn.remote_ip, &conn.remote_port);
if (conn.fd < 0) {
if (conn.fd == -1)
return;
if (errno != ENOTSOCK) {
i_error("net_accept() failed: %m");
io_listeners_remove(l->service);
return;
}
/* it's not a socket. probably a fifo. use the "listener"
as the connection fd */
io_remove(&l->io);
conn.fd = l->fd;
}
conn.ssl = l->ssl;
net_set_nonblock(conn.fd, TRUE);
l->service->master_status.available_count--;
master_status_update(l->service);
l->service->callback(&conn);
}
static void io_listeners_add(struct master_service *service)
{
unsigned int i;
if (service->socket_count == 0)
return;
service->listeners =
i_new(struct master_service_listener, service->socket_count);
for (i = 0; i < service->socket_count; i++) {
struct master_service_listener *l = &service->listeners[i];
l->service = service;
l->fd = MASTER_LISTEN_FD_FIRST + i;
l->io = io_add(MASTER_LISTEN_FD_FIRST + i, IO_READ,
master_service_listen, l);
if (i >= service->socket_count - service->ssl_socket_count)
l->ssl = TRUE;
}
}
static void io_listeners_remove(struct master_service *service)
{
unsigned int i;
if (service->listeners != NULL) {
for (i = 0; i < service->socket_count; i++) {
if (service->listeners[i].io != NULL)
io_remove(&service->listeners[i].io);
}
i_free_and_null(service->listeners);
}
}
static bool master_status_update_is_important(struct master_service *service)
{
if (service->master_status.available_count == 0)
return TRUE;
if (!service->initial_status_sent)
return TRUE;
return FALSE;
}
static void master_status_update(struct master_service *service)
{
ssize_t ret;
if (service->master_status.pid == 0)
return; /* closed */
ret = write(MASTER_STATUS_FD, &service->master_status,
sizeof(service->master_status));
if (ret > 0) {
/* success */
if (service->io_status_write != NULL) {
/* delayed important update sent successfully */
io_remove(&service->io_status_write);
}
service->initial_status_sent = TRUE;
} else if (ret == 0) {
/* shouldn't happen? */
i_error("write(master_status_fd) returned 0");
service->master_status.pid = 0;
} else if (errno != EAGAIN) {
/* failure */
if (errno != EPIPE)
i_error("write(master_status_fd) failed: %m");
service->master_status.pid = 0;
} else if (master_status_update_is_important(service)) {
/* reader is busy, but it's important to get this notification
through. send it when possible. */
if (service->io_status_write == NULL) {
service->io_status_write =
io_add(MASTER_STATUS_FD, IO_WRITE,
master_status_update, service);
}
}
}