master-service.c revision 13a0b45c0e4a776d88647346ba900cd5087ba3f0
5f5870385cff47efd2f58e7892f251cf13761528Timo Sirainen/* Copyright (c) 2005-2009 Dovecot authors, see the included COPYING file */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "lib.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "lib-signals.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "ioloop.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "array.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "env-util.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "home-expand.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "process-title.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "restrict-access.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "fd-close-on-exec.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "settings-parser.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "syslog-util.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "master-service-private.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "master-service-settings.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include <stdlib.h>
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include <unistd.h>
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include <sys/stat.h>
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include <syslog.h>
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#define DEFAULT_CONFIG_FILE_PATH SYSCONFDIR"/dovecot.conf"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen/* getenv(MASTER_CONFIG_FILE_ENV) provides path to configuration file/socket */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#define MASTER_CONFIG_FILE_ENV "CONFIG_FILE"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen/* getenv(MASTER_DOVECOT_VERSION_ENV) provides master's version number */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#define MASTER_DOVECOT_VERSION_ENV "DOVECOT_VERSION"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen/* when we're full of connections, how often to check if login state has
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen changed. we normally notice it immediately because of a signal, so this is
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen just a fallback against race conditions. */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#define MASTER_SERVICE_STATE_CHECK_MSECS 1000
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
0001f76bf725c5cf403bade8556f142dd43144eeTimo Sirainen/* If die callback hasn't managed to stop the service for this many seconds,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen force it. */
c18ff860dc22960fd37c272d929f889c7939a2c8Timo Sirainen#define MASTER_SERVICE_DIE_TIMEOUT_MSECS (30*1000)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstruct master_service *master_service;
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen
c18ff860dc22960fd37c272d929f889c7939a2c8Timo Sirainenstatic void master_service_refresh_login_state(struct master_service *service);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
4ee00532a265bdfb38539d811fcd12d51210ac35Timo Sirainenconst char *master_service_getopt_string(void)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return "c:ko:Os:L";
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen}
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainenstatic void sig_die(const siginfo_t *si, void *context)
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen{
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen struct master_service *service = context;
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen /* warn about being killed because of some signal, except SIGINT (^C)
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen which is too common at least while testing :) */
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen if (si->si_signo != SIGINT) {
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen i_warning("Killed with signal %d (by pid=%s uid=%s code=%s)",
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen si->si_signo, dec2str(si->si_pid),
cd83124e5d070a016c590bb0b1096d7828c7b6adTimo Sirainen dec2str(si->si_uid),
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen lib_signal_code_to_str(si->si_signo, si->si_code));
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen }
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen io_loop_stop(service->ioloop);
c18ff860dc22960fd37c272d929f889c7939a2c8Timo Sirainen}
714c6a150480112eb1a5f309d0cc39b60613a719Timo Sirainen
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainenstatic void
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainensig_state_changed(const siginfo_t *si ATTR_UNUSED, void *context)
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen{
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen struct master_service *service = context;
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen master_service_refresh_login_state(service);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic void master_service_verify_version(struct master_service *service)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (service->version_string != NULL &&
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen strcmp(service->version_string, PACKAGE_VERSION) != 0) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_fatal("Dovecot version mismatch: "
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen "Master is v%s, %s is v"PACKAGE_VERSION" "
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen "(if you don't care, set version_ignore=yes)",
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen service->name, service->version_string);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
9a02317c852face76737763fa6ec43b444688de5Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstruct master_service *
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenmaster_service_init(const char *name, enum master_service_flags flags,
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen int *argc, char **argv[], const char *getopt_str)
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen{
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen struct master_service *service;
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen const char *str;
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen i_assert(name != NULL);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#ifdef DEBUG
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (getenv("GDB") == NULL &&
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen (flags & MASTER_SERVICE_FLAG_STANDALONE) == 0) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen int count;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen str = getenv("SOCKET_COUNT");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen count = str == NULL ? 0 : atoi(str);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen fd_debug_verify_leaks(MASTER_LISTEN_FD_FIRST + count, 1024);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#endif
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* NOTE: we start rooted, so keep the code minimal until
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen restrict_access_by_env() is called */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen lib_init();
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* Set a logging prefix temporarily. This will be ignored once the log
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen is properly initialized */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_set_failure_prefix(t_strdup_printf("%s(init): ", name));
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
0001f76bf725c5cf403bade8556f142dd43144eeTimo Sirainen if (getenv(MASTER_UID_ENV) == NULL)
e2eac5bb5637c2d4aaf453389750740931822b92Timo Sirainen flags |= MASTER_SERVICE_FLAG_STANDALONE;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
e2bdacc34dde56aa664059ab56e8b77e82bd1805Timo Sirainen process_title_init(argv);
e2bdacc34dde56aa664059ab56e8b77e82bd1805Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen service = i_new(struct master_service, 1);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen service->argc = *argc;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen service->argv = *argv;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen service->name = i_strdup(name);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* keep getopt_str first in case it contains "+" */
cd83124e5d070a016c590bb0b1096d7828c7b6adTimo Sirainen service->getopt_str = getopt_str == NULL ?
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_strdup(master_service_getopt_string()) :
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen i_strconcat(getopt_str, master_service_getopt_string(), NULL);
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen service->flags = flags;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen service->ioloop = io_loop_create();
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen service->service_count_left = (unsigned int)-1;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen service->config_fd = -1;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen service->config_path = getenv(MASTER_CONFIG_FILE_ENV);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (service->config_path == NULL)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen service->config_path = DEFAULT_CONFIG_FILE_PATH;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if ((flags & MASTER_SERVICE_FLAG_STANDALONE) == 0) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen service->version_string = getenv(MASTER_DOVECOT_VERSION_ENV);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen service->socket_count = 1;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen } else {
5e702db5540b2303e25554dee21bbf35a4813381Timo Sirainen service->version_string = PACKAGE_VERSION;
5e702db5540b2303e25554dee21bbf35a4813381Timo Sirainen }
5e702db5540b2303e25554dee21bbf35a4813381Timo Sirainen str = getenv("SOCKET_COUNT");
5e702db5540b2303e25554dee21bbf35a4813381Timo Sirainen if (str != NULL)
5e702db5540b2303e25554dee21bbf35a4813381Timo Sirainen service->socket_count = atoi(str);
46908da82cd45dd90ca7e438c8453bc9669868ecTimo Sirainen str = getenv("SSL_SOCKET_COUNT");
46908da82cd45dd90ca7e438c8453bc9669868ecTimo Sirainen if (str != NULL)
5e702db5540b2303e25554dee21bbf35a4813381Timo Sirainen service->ssl_socket_count = atoi(str);
c18ff860dc22960fd37c272d929f889c7939a2c8Timo Sirainen
c18ff860dc22960fd37c272d929f889c7939a2c8Timo Sirainen /* set up some kind of logging until we know exactly how and where
c18ff860dc22960fd37c272d929f889c7939a2c8Timo Sirainen we want to log */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (getenv("LOG_SERVICE") != NULL)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_set_failure_internal();
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (getenv("USER") != NULL) {
e6440616c02bb1404dc35debf45d9741260c7831Timo Sirainen i_set_failure_prefix(t_strdup_printf("%s(%s): ",
e6440616c02bb1404dc35debf45d9741260c7831Timo Sirainen name, getenv("USER")));
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen } else {
0001f76bf725c5cf403bade8556f142dd43144eeTimo Sirainen i_set_failure_prefix(t_strdup_printf("%s: ", name));
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen master_service_verify_version(service);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return service;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenint master_getopt(struct master_service *service)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen int c;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen while ((c = getopt(service->argc, service->argv,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen service->getopt_str)) > 0) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (!master_service_parse_option(service, c, optarg))
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen break;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
de62ce819d59a529530da4b57be1b8d6dad13d6bTimo Sirainen return c;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenvoid master_service_init_log(struct master_service *service,
e6440616c02bb1404dc35debf45d9741260c7831Timo Sirainen const char *prefix)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const char *path;
e6440616c02bb1404dc35debf45d9741260c7831Timo Sirainen
1f19649986397419d014febd1337c6eb7b530f26Timo Sirainen if ((service->flags & MASTER_SERVICE_FLAG_STANDALONE) != 0 &&
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen (service->flags & MASTER_SERVICE_FLAG_DONT_LOG_TO_STDERR) == 0) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_set_failure_file("/dev/stderr", "");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (getenv("LOG_SERVICE") != NULL && !service->log_directly) {
e6440616c02bb1404dc35debf45d9741260c7831Timo Sirainen /* logging via log service */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_set_failure_internal();
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_set_failure_prefix(prefix);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (service->set == NULL) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_set_failure_file("/dev/stderr", prefix);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
06eb8c1371aa06478d8840b1373cab7c2752d5edTimo Sirainen if (*service->set->log_path == '\0') {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* log to syslog */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen int facility;
e6440616c02bb1404dc35debf45d9741260c7831Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (!syslog_facility_find(service->set->syslog_facility,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen &facility))
e6440616c02bb1404dc35debf45d9741260c7831Timo Sirainen facility = LOG_MAIL;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_set_failure_syslog("dovecot", LOG_NDELAY, facility);
06eb8c1371aa06478d8840b1373cab7c2752d5edTimo Sirainen i_set_failure_prefix(prefix);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen } else {
06eb8c1371aa06478d8840b1373cab7c2752d5edTimo Sirainen /* log to file or stderr */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen path = home_expand(service->set->log_path);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_set_failure_file(path, prefix);
c18ff860dc22960fd37c272d929f889c7939a2c8Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen path = home_expand(service->set->info_log_path);
06eb8c1371aa06478d8840b1373cab7c2752d5edTimo Sirainen if (*path != '\0')
06eb8c1371aa06478d8840b1373cab7c2752d5edTimo Sirainen i_set_info_file(path);
06eb8c1371aa06478d8840b1373cab7c2752d5edTimo Sirainen
06eb8c1371aa06478d8840b1373cab7c2752d5edTimo Sirainen path = home_expand(service->set->debug_log_path);
06eb8c1371aa06478d8840b1373cab7c2752d5edTimo Sirainen if (*path != '\0')
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_set_debug_file(path);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_set_failure_timestamp_format(service->set->log_timestamp);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
9047d770bfbb93ab6af5363dedb2d01363877243Timo Sirainen
836e57b1e7817d008f8ae05cd4b506f420fed80dTimo Sirainenvoid master_service_set_die_with_master(struct master_service *service,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen bool set)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen service->die_with_master = set;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenvoid master_service_set_die_callback(struct master_service *service,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen void (*callback)(void))
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen service->die_callback = callback;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenbool master_service_parse_option(struct master_service *service,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen int opt, const char *arg)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen int i;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen switch (opt) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen case 'c':
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen service->config_path = arg;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen break;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen case 'k':
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen service->keep_environment = TRUE;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen break;
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen case 'o':
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen if (!array_is_created(&service->config_overrides))
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen i_array_init(&service->config_overrides, 16);
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen array_append(&service->config_overrides, &arg, 1);
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen break;
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen case 'O':
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen service->flags |= MASTER_SERVICE_FLAG_NO_CONFIG_SETTINGS;
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen break;
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen case 's':
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen if ((i = atoi(arg)) < 0)
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen i_fatal("Invalid socket count: %s", arg);
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen service->socket_count = i;
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen break;
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen case 'L':
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen service->log_directly = TRUE;
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen break;
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen default:
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen return FALSE;
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen }
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen return TRUE;
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen}
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainenstatic void master_service_error(struct master_service *service)
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen{
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen master_service_stop_new_connections(service);
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen if (service->master_status.available_count ==
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen service->total_available_count || service->die_with_master) {
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen if (service->die_callback == NULL)
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen master_service_stop(service);
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen else {
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen service->to_die =
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen timeout_add(MASTER_SERVICE_DIE_TIMEOUT_MSECS,
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen master_service_stop,
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen service);
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen service->die_callback();
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic void master_status_error(void *context)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct master_service *service = context;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* status fd is a write-only pipe, so if we're here it means the
ddedc8b77c5bccc8d224ab5f8716d9874f5d8512Timo Sirainen master wants us to die (or died itself). don't die until all
ddedc8b77c5bccc8d224ab5f8716d9874f5d8512Timo Sirainen service connections are finished. */
ddedc8b77c5bccc8d224ab5f8716d9874f5d8512Timo Sirainen io_remove(&service->io_status_error);
ddedc8b77c5bccc8d224ab5f8716d9874f5d8512Timo Sirainen
ddedc8b77c5bccc8d224ab5f8716d9874f5d8512Timo Sirainen /* the log fd may also be closed already, don't die when trying to
ddedc8b77c5bccc8d224ab5f8716d9874f5d8512Timo Sirainen log later */
f1612f8421207632e1dc9addd6c23e7f7098a54cTimo Sirainen i_set_failure_ignore_errors(TRUE);
56aa9083e1742d0083885aaf0c5b8581577731aeTimo Sirainen
56aa9083e1742d0083885aaf0c5b8581577731aeTimo Sirainen master_service_error(service);
f1612f8421207632e1dc9addd6c23e7f7098a54cTimo Sirainen}
56aa9083e1742d0083885aaf0c5b8581577731aeTimo Sirainen
f71c2d4e6b802bf8e622bcd5df29286262d05d5aTimo Sirainenvoid master_service_init_finish(struct master_service *service)
f71c2d4e6b802bf8e622bcd5df29286262d05d5aTimo Sirainen{
f71c2d4e6b802bf8e622bcd5df29286262d05d5aTimo Sirainen struct stat st;
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen const char *value;
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen unsigned int count;
f71c2d4e6b802bf8e622bcd5df29286262d05d5aTimo Sirainen
f71c2d4e6b802bf8e622bcd5df29286262d05d5aTimo Sirainen i_assert(service->total_available_count == 0);
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen i_assert(service->service_count_left == (unsigned int)-1);
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen /* set default signal handlers */
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen lib_signals_init();
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen lib_signals_ignore(SIGPIPE, TRUE);
3c40cf45bb10548bf434aea0fca32b99acc99a35Timo Sirainen lib_signals_ignore(SIGALRM, FALSE);
3c40cf45bb10548bf434aea0fca32b99acc99a35Timo Sirainen lib_signals_set_handler(SIGINT, TRUE, sig_die, service);
3c40cf45bb10548bf434aea0fca32b99acc99a35Timo Sirainen lib_signals_set_handler(SIGTERM, TRUE, sig_die, service);
3c40cf45bb10548bf434aea0fca32b99acc99a35Timo Sirainen if ((service->flags & MASTER_SERVICE_FLAG_TRACK_LOGIN_STATE) != 0) {
3c40cf45bb10548bf434aea0fca32b99acc99a35Timo Sirainen lib_signals_set_handler(SIGUSR1, TRUE,
3c40cf45bb10548bf434aea0fca32b99acc99a35Timo Sirainen sig_state_changed, service);
3c40cf45bb10548bf434aea0fca32b99acc99a35Timo Sirainen }
3c40cf45bb10548bf434aea0fca32b99acc99a35Timo Sirainen
3c40cf45bb10548bf434aea0fca32b99acc99a35Timo Sirainen if ((service->flags & MASTER_SERVICE_FLAG_STANDALONE) == 0) {
3c40cf45bb10548bf434aea0fca32b99acc99a35Timo Sirainen if (fstat(MASTER_STATUS_FD, &st) < 0 || !S_ISFIFO(st.st_mode))
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_fatal("Must be started by dovecot master process");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* initialize master_status structure */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen value = getenv(MASTER_UID_ENV);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (value == NULL)
fb5efc6ed69da679d9da31ef62daa7024de18212Timo Sirainen i_fatal(MASTER_UID_ENV" not set");
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen service->master_status.pid = getpid();
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen service->master_status.uid =
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen (unsigned int)strtoul(value, NULL, 10);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* set the default limit */
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen value = getenv(MASTER_CLIENT_LIMIT_ENV);
c18ff860dc22960fd37c272d929f889c7939a2c8Timo Sirainen count = value == NULL ? 0 :
c18ff860dc22960fd37c272d929f889c7939a2c8Timo Sirainen (unsigned int)strtoul(value, NULL, 10);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (count == 0)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_fatal(MASTER_CLIENT_LIMIT_ENV" not set");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen master_service_set_client_limit(service, count);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* set the default service count */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen value = getenv(MASTER_SERVICE_COUNT_ENV);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen count = value == NULL ? 0 :
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen (unsigned int)strtoul(value, NULL, 10);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (count > 0)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen master_service_set_service_count(service, count);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* start listening errors for status fd, it means master died */
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainen service->io_status_error = io_add(MASTER_STATUS_FD, IO_ERROR,
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainen master_status_error, service);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen } else {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen master_service_set_client_limit(service, 1);
b780aa272b742a43579cdb523cc79cc8d4521306Timo Sirainen master_service_set_service_count(service, 1);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen master_service_io_listeners_add(service);
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainen
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainen if ((service->flags & MASTER_SERVICE_FLAG_STD_CLIENT) != 0) {
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainen /* we already have a connection to be served */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen service->master_status.available_count--;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen master_status_update(service);
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen}
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainenvoid master_service_env_clean(bool preserve_home)
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen{
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen const char *user, *tz, *home;
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen#ifdef DEBUG
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen bool gdb = getenv("GDB") != NULL;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#endif
c18ff860dc22960fd37c272d929f889c7939a2c8Timo Sirainen
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen user = getenv("USER");
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen if (user != NULL)
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen user = t_strconcat("USER=", user, NULL);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen tz = getenv("TZ");
b780aa272b742a43579cdb523cc79cc8d4521306Timo Sirainen if (tz != NULL)
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen tz = t_strconcat("TZ=", tz, NULL);
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen home = preserve_home ? getenv("HOME") : NULL;
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen if (home != NULL)
b780aa272b742a43579cdb523cc79cc8d4521306Timo Sirainen home = t_strconcat("HOME=", home, NULL);
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* Note that if the original environment was set with env_put(), the
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen environment strings will be invalid after env_clean(). That's why
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen we t_strconcat() them above. */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen env_clean();
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (user != NULL) env_put(user);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (tz != NULL) env_put(tz);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (home != NULL) env_put(home);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#ifdef DEBUG
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (gdb) env_put("GDB=1");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#endif
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
c18ff860dc22960fd37c272d929f889c7939a2c8Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenvoid master_service_set_client_limit(struct master_service *service,
c18ff860dc22960fd37c272d929f889c7939a2c8Timo Sirainen unsigned int client_limit)
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen{
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen unsigned int used;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_assert(service->master_status.available_count ==
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen service->total_available_count);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen used = service->total_available_count -
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen service->master_status.available_count;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_assert(client_limit >= used);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen service->total_available_count = client_limit;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen service->master_status.available_count = client_limit - used;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenunsigned int master_service_get_client_limit(struct master_service *service)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return service->total_available_count;
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenvoid master_service_set_service_count(struct master_service *service,
553cf4f8a8850efffdc714ec1d1ae45a5fc7905dTimo Sirainen unsigned int count)
553cf4f8a8850efffdc714ec1d1ae45a5fc7905dTimo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen unsigned int used;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen used = service->total_available_count -
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen service->master_status.available_count;
7f735cb86b2d8abd8f230089065eacfc24e9e5d6Timo Sirainen i_assert(count >= used);
553cf4f8a8850efffdc714ec1d1ae45a5fc7905dTimo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (service->total_available_count > count) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen service->total_available_count = count;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen service->master_status.available_count = count - used;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen service->service_count_left = count;
767ff4367960efd5fa868f3b56f850fd4c205c8bTimo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenunsigned int master_service_get_service_count(struct master_service *service)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
553cf4f8a8850efffdc714ec1d1ae45a5fc7905dTimo Sirainen return service->service_count_left;
553cf4f8a8850efffdc714ec1d1ae45a5fc7905dTimo Sirainen}
553cf4f8a8850efffdc714ec1d1ae45a5fc7905dTimo Sirainen
553cf4f8a8850efffdc714ec1d1ae45a5fc7905dTimo Sirainenunsigned int master_service_get_socket_count(struct master_service *service)
553cf4f8a8850efffdc714ec1d1ae45a5fc7905dTimo Sirainen{
553cf4f8a8850efffdc714ec1d1ae45a5fc7905dTimo Sirainen return service->socket_count;
61279c3c77aa4a6f8d1de82b468ab01b14418318Timo Sirainen}
61279c3c77aa4a6f8d1de82b468ab01b14418318Timo Sirainen
553cf4f8a8850efffdc714ec1d1ae45a5fc7905dTimo Sirainenvoid master_service_set_avail_overflow_callback(struct master_service *service,
553cf4f8a8850efffdc714ec1d1ae45a5fc7905dTimo Sirainen void (*callback)(void))
553cf4f8a8850efffdc714ec1d1ae45a5fc7905dTimo Sirainen{
553cf4f8a8850efffdc714ec1d1ae45a5fc7905dTimo Sirainen service->avail_overflow_callback = callback;
553cf4f8a8850efffdc714ec1d1ae45a5fc7905dTimo Sirainen}
553cf4f8a8850efffdc714ec1d1ae45a5fc7905dTimo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenconst char *master_service_get_config_path(struct master_service *service)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return service->config_path;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenconst char *master_service_get_version_string(struct master_service *service)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return service->version_string;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
553cf4f8a8850efffdc714ec1d1ae45a5fc7905dTimo Sirainenconst char *master_service_get_name(struct master_service *service)
553cf4f8a8850efffdc714ec1d1ae45a5fc7905dTimo Sirainen{
1554bed8d2b4e4286c10f7d6bcf716b246bd5bafTimo Sirainen return service->name;
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen}
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainenvoid master_service_run(struct master_service *service,
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen master_service_connection_callback_t *callback)
7631f16156aca373004953fe6b01a7f343fb47e0Timo Sirainen{
7631f16156aca373004953fe6b01a7f343fb47e0Timo Sirainen service->callback = callback;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen io_loop_run(service->ioloop);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen service->callback = NULL;
}
void master_service_stop(struct master_service *service)
{
io_loop_stop(service->ioloop);
}
void master_service_stop_new_connections(struct master_service *service)
{
unsigned int current_count;
service->stopping = TRUE;
master_service_io_listeners_remove(service);
/* make sure we stop after servicing current connections */
current_count = service->total_available_count -
service->master_status.available_count;
service->service_count_left = current_count;
service->total_available_count = current_count;
if (current_count == 0)
master_service_stop(service);
else {
/* notify master that we're not accepting any more
connections */
service->master_status.available_count = 0;
master_status_update(service);
}
}
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) {
if (errno == EPIPE) {
/* anvil process was probably recreated, don't bother
logging an error about losing connection to it */
return;
}
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)
{
/* we can listen again */
master_service_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);
i_assert(service->master_status.available_count <
service->total_available_count);
service->master_status.available_count++;
master_status_update(service);
} 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);
}
}
if ((service->io_status_error == NULL || service->listeners == NULL) &&
service->master_status.available_count ==
service->total_available_count) {
/* we've finished handling all clients, and
a) master has closed the connection
b) there are no listeners (std-client?) */
master_service_stop(service);
}
}
static void master_service_set_login_state(struct master_service *service,
enum master_login_state state)
{
if (service->to_overflow_state != NULL)
timeout_remove(&service->to_overflow_state);
switch (state) {
case MASTER_LOGIN_STATE_NONFULL:
service->call_avail_overflow = FALSE;
if (service->master_status.available_count > 0)
return;
/* some processes should now be able to handle new connections,
although we can't. but there may be race conditions, so
make sure that we'll check again soon if the state has
changed to "full" without our knowledge. */
service->to_overflow_state =
timeout_add(MASTER_SERVICE_STATE_CHECK_MSECS,
master_service_refresh_login_state,
service);
return;
case MASTER_LOGIN_STATE_FULL:
/* make sure we're listening for more connections */
service->call_avail_overflow = TRUE;
master_service_io_listeners_add(service);
return;
}
i_error("Invalid master login state: %d", state);
}
static void master_service_refresh_login_state(struct master_service *service)
{
int ret;
ret = lseek(MASTER_LOGIN_NOTIFY_FD, 0, SEEK_CUR);
if (ret < 0)
i_error("lseek(login notify fd) failed: %m");
else
master_service_set_login_state(service, ret);
}
void master_service_close_config_fd(struct master_service *service)
{
if (service->config_fd != -1) {
if (close(service->config_fd) < 0)
i_error("close(master config fd) failed: %m");
service->config_fd = -1;
}
}
void master_service_deinit(struct master_service **_service)
{
struct master_service *service = *_service;
*_service = NULL;
master_service_io_listeners_remove(service);
master_service_close_config_fd(service);
if (service->to_die != NULL)
timeout_remove(&service->to_die);
if (service->to_overflow_state != NULL)
timeout_remove(&service->to_overflow_state);
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->listeners);
i_free(service->getopt_str);
i_free(service->name);
i_free(service);
lib_deinit();
}
static void master_service_listen(struct master_service_listener *l)
{
struct master_service *service = l->service;
struct master_service_connection conn;
if (service->master_status.available_count == 0) {
/* we are full. stop listening for now, unless overflow
callback destroys one of the existing connections */
if (service->call_avail_overflow &&
service->avail_overflow_callback != NULL) {
service->delay_status_updates = TRUE;
service->avail_overflow_callback();
service->delay_status_updates = FALSE;
}
if (service->master_status.available_count == 0) {
master_service_io_listeners_remove(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");
master_service_error(service);
return;
}
/* it's not a socket. probably a fifo. use the "listener"
as the connection fd and stop the listener. */
conn.fd = l->fd;
conn.listen_fd = l->fd;
conn.fifo = TRUE;
io_remove(&l->io);
l->fd = -1;
}
conn.ssl = l->ssl;
net_set_nonblock(conn.fd, TRUE);
if (!service->login_connections) {
i_assert(service->master_status.available_count > 0);
service->master_status.available_count--;
master_status_update(service);
}
service->callback(&conn);
}
static void io_listeners_init(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;
if (i >= service->socket_count - service->ssl_socket_count)
l->ssl = TRUE;
}
}
void master_service_io_listeners_add(struct master_service *service)
{
unsigned int i;
if (service->stopping)
return;
if (service->listeners == NULL)
io_listeners_init(service);
for (i = 0; i < service->socket_count; i++) {
struct master_service_listener *l = &service->listeners[i];
if (l->io == NULL && l->fd != -1) {
l->io = io_add(MASTER_LISTEN_FD_FIRST + i, IO_READ,
master_service_listen, l);
}
}
}
void master_service_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);
}
}
}
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;
}
void master_status_update(struct master_service *service)
{
ssize_t ret;
if (service->master_status.pid == 0 || service->delay_status_updates)
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);
}
}
}