master-service.c revision 047c00cd3f7f403672f81569413669238df8c15a
c25356d5978632df6203437e1953bcb29e0c736fTimo Sirainen/* Copyright (c) 2005-2009 Dovecot authors, see the included COPYING file */
c25356d5978632df6203437e1953bcb29e0c736fTimo Sirainen
c0a708fa3f7b8f4fbca32052da5faf7a0125189dTimo Sirainen#include "lib.h"
c5e62353a11087958ea4e619660e084a613e1a37Timo Sirainen#include "lib-signals.h"
c5e62353a11087958ea4e619660e084a613e1a37Timo Sirainen#include "ioloop.h"
bdd36cfdba3ff66d25570a9ff568d69e1eb543cfTimo Sirainen#include "array.h"
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#include "env-util.h"
c49a19168dab6fda80aee16ad799a8a56d3bc18fTimo Sirainen#include "home-expand.h"
b55f914c0ade77252cfd798ea8eb9a84bda56315Timo Sirainen#include "restrict-access.h"
c0a708fa3f7b8f4fbca32052da5faf7a0125189dTimo Sirainen#include "fd-close-on-exec.h"
82fb1ead38f4e1197993adb2f9c12e26531e9c92Timo Sirainen#include "settings-parser.h"
decb23442f9e6cd5c4845a9cb162029b8c6d5f0fTimo Sirainen#include "syslog-util.h"
2f7c73483ff5474a74a83a646f82e1b60f687680Aki Tuomi#include "master-service-private.h"
decb23442f9e6cd5c4845a9cb162029b8c6d5f0fTimo Sirainen#include "master-service-settings.h"
cd94aeaa294f7cc507206b4b2075852f00e14d61Timo Sirainen
cd94aeaa294f7cc507206b4b2075852f00e14d61Timo Sirainen#include <stdlib.h>
decb23442f9e6cd5c4845a9cb162029b8c6d5f0fTimo Sirainen#include <unistd.h>
decb23442f9e6cd5c4845a9cb162029b8c6d5f0fTimo Sirainen#include <sys/stat.h>
cd94aeaa294f7cc507206b4b2075852f00e14d61Timo Sirainen#include <syslog.h>
cd94aeaa294f7cc507206b4b2075852f00e14d61Timo Sirainen
decb23442f9e6cd5c4845a9cb162029b8c6d5f0fTimo Sirainen#define DEFAULT_CONFIG_FILE_PATH SYSCONFDIR"/dovecot.conf"
b55f914c0ade77252cfd798ea8eb9a84bda56315Timo Sirainen
b55f914c0ade77252cfd798ea8eb9a84bda56315Timo Sirainen/* getenv(MASTER_CONFIG_FILE_ENV) provides path to configuration file/socket */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#define MASTER_CONFIG_FILE_ENV "CONFIG_FILE"
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen/* getenv(MASTER_DOVECOT_VERSION_ENV) provides master's version number */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#define MASTER_DOVECOT_VERSION_ENV "DOVECOT_VERSION"
7fa573e6ea36024f618492e7d3649a69c1b41028Timo Sirainen
7fa573e6ea36024f618492e7d3649a69c1b41028Timo Sirainenstruct master_service *master_service;
7fa573e6ea36024f618492e7d3649a69c1b41028Timo Sirainen
5a9e240ebf8d0daaf029973973b52e415148070bTimo Sirainenstatic void io_listeners_remove(struct master_service *service);
5a9e240ebf8d0daaf029973973b52e415148070bTimo Sirainenstatic void master_status_update(struct master_service *service);
d99107ddf4d9bccb710994482daf65276a9d6321Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenconst char *master_service_getopt_string(void)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return "c:ko:Os:L";
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
cd94aeaa294f7cc507206b4b2075852f00e14d61Timo Sirainenstatic void sig_die(const siginfo_t *si, void *context)
a05fec120ecd8c4ed6331c42100cba42adf22893Stephan Bosch{
a05fec120ecd8c4ed6331c42100cba42adf22893Stephan Bosch struct master_service *service = context;
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen /* warn about being killed because of some signal, except SIGINT (^C)
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen which is too common at least while testing :) */
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen if (si->si_signo != SIGINT) {
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen i_warning("Killed with signal %d (by pid=%s uid=%s code=%s)",
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen si->si_signo, dec2str(si->si_pid),
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen dec2str(si->si_uid),
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch lib_signal_code_to_str(si->si_signo, si->si_code));
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch }
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch io_loop_stop(service->ioloop);
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch}
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Boschstatic void master_service_verify_version(struct master_service *service)
8ac6623677005256bf99ab33a2ed98c69c1d656cStephan Bosch{
f32d0295c90ed810889504cdfa5e1a25a415f65fStephan Bosch if (service->version_string != NULL &&
04eb0abcf8f8b0c014499b5c5bae89484553613fStephan Bosch strcmp(service->version_string, PACKAGE_VERSION) != 0) {
04eb0abcf8f8b0c014499b5c5bae89484553613fStephan Bosch i_fatal("Dovecot version mismatch: "
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch "Master is v%s, %s is v"PACKAGE_VERSION" "
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch "(if you don't care, set version_ignore=yes)",
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen service->name, service->version_string);
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen }
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen}
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainenstruct master_service *
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainenmaster_service_init(const char *name, enum master_service_flags flags,
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen int argc, char *argv[])
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen{
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen struct master_service *service;
529944a3554da75c2e6231a25fe489d815519b22Stephan Bosch const char *str;
f32d0295c90ed810889504cdfa5e1a25a415f65fStephan Bosch
8ac6623677005256bf99ab33a2ed98c69c1d656cStephan Bosch i_assert(name != NULL);
f32d0295c90ed810889504cdfa5e1a25a415f65fStephan Bosch
04eb0abcf8f8b0c014499b5c5bae89484553613fStephan Bosch#ifdef DEBUG
04eb0abcf8f8b0c014499b5c5bae89484553613fStephan Bosch if (getenv("GDB") == NULL &&
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen (flags & MASTER_SERVICE_FLAG_STANDALONE) == 0) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen int count;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen str = getenv("SOCKET_COUNT");
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch count = str == NULL ? 0 : atoi(str);
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch fd_debug_verify_leaks(MASTER_LISTEN_FD_FIRST + count, 1024);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
d694a52bce62c52080c3f87a56dcf77030fd2712Timo Sirainen#endif
d694a52bce62c52080c3f87a56dcf77030fd2712Timo Sirainen
009217abb57a24a4076092e8e4e165545747839eStephan Bosch /* NOTE: we start rooted, so keep the code minimal until
2598b2f36365b52d9754b9348a5be29569293e46Timo Sirainen restrict_access_by_env() is called */
6303191abcb37164f435ccdc56e9dbddf1288851Timo Sirainen lib_init();
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* Set a logging prefix temporarily. This will be ignored once the log
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen is properly initialized */
fd3d068169c6ec587c9c446f2ee45560a444334aTimo Sirainen i_set_failure_prefix(t_strdup_printf("%s(init): ", name));
fd3d068169c6ec587c9c446f2ee45560a444334aTimo Sirainen
fd3d068169c6ec587c9c446f2ee45560a444334aTimo Sirainen if (getenv(MASTER_UID_ENV) == NULL)
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen flags |= MASTER_SERVICE_FLAG_STANDALONE;
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen
6d24551e169c0808695db35d7a228f1970a84c75Timo Sirainen service = i_new(struct master_service, 1);
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen service->argc = argc;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen service->argv = argv;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen service->name = i_strdup(name);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen service->flags = flags;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen service->ioloop = io_loop_create();
f059a046515f4b2b15a6c2a10a6f12f6166e39a5Timo Sirainen service->service_count_left = (unsigned int)-1;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen service->config_fd = -1;
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen service->config_path = getenv(MASTER_CONFIG_FILE_ENV);
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen if (service->config_path == NULL)
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen service->config_path = DEFAULT_CONFIG_FILE_PATH;
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen if ((flags & MASTER_SERVICE_FLAG_STANDALONE) == 0) {
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen service->version_string = getenv(MASTER_DOVECOT_VERSION_ENV);
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen service->socket_count = 1;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen } else {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen service->version_string = PACKAGE_VERSION;
46ec792dd4ccf6c34706c4774228301fafde6aa9Timo Sirainen }
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen str = getenv("SOCKET_COUNT");
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen if (str != NULL)
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen service->socket_count = atoi(str);
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen str = getenv("SSL_SOCKET_COUNT");
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen if (str != NULL)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen service->ssl_socket_count = atoi(str);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen /* set up some kind of logging until we know exactly how and where
2b96880f2d789d125aff6a95eaa7b51f558a6a1cTimo Sirainen we want to log */
ae797f3368ebb86e7786ca25d7c9c703f672b9f5Timo Sirainen if (getenv("LOG_SERVICE") != NULL)
ae797f3368ebb86e7786ca25d7c9c703f672b9f5Timo Sirainen i_set_failure_internal();
edfdc577ffe7408fd6463eb9dba11260d380ab53Timo Sirainen if (getenv("USER") != NULL) {
b84eff65e25ae86dfd6f798386577209b94838c6Timo Sirainen i_set_failure_prefix(t_strdup_printf("%s(%s): ",
00fa8dcbc66f56daa737487c9dec7166c37de79eTimo Sirainen name, getenv("USER")));
00fa8dcbc66f56daa737487c9dec7166c37de79eTimo Sirainen } else {
c0a708fa3f7b8f4fbca32052da5faf7a0125189dTimo Sirainen i_set_failure_prefix(t_strdup_printf("%s: ", name));
ad004e44be109684521494b5af2ad1da39b8bb27Timo Sirainen }
97db4761382024093f441e4bc78ba8b6a056504dTimo Sirainen
b045b66988bfbaa2795791e42ee724fae6f0db1cAki Tuomi master_service_verify_version(service);
b045b66988bfbaa2795791e42ee724fae6f0db1cAki Tuomi return service;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
c5e62353a11087958ea4e619660e084a613e1a37Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenvoid master_service_init_log(struct master_service *service,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen const char *prefix)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
ad004e44be109684521494b5af2ad1da39b8bb27Timo Sirainen const char *path;
64e244defe74f513ce94f33d000a048ddbe2ea23Timo Sirainen
c0a708fa3f7b8f4fbca32052da5faf7a0125189dTimo Sirainen if ((service->flags & MASTER_SERVICE_FLAG_STANDALONE) != 0 &&
325d17cdbb7a338f7c413788f5e8e42d2e80a7f8Timo Sirainen (service->flags & MASTER_SERVICE_FLAG_DONT_LOG_TO_STDERR) == 0) {
009217abb57a24a4076092e8e4e165545747839eStephan Bosch i_set_failure_file("/dev/stderr", "");
009217abb57a24a4076092e8e4e165545747839eStephan Bosch return;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
97db4761382024093f441e4bc78ba8b6a056504dTimo Sirainen
f29756821a4c6b12b73e4a2a3e1c230117a43773Timo Sirainen if (getenv("LOG_SERVICE") != NULL && !service->log_directly) {
8ce3071e80b9973230048ecadfcb073fb82cc69fTimo Sirainen /* logging via log service */
fe791e96fdf796f7d8997ee0515b163dc5eddd72Aki Tuomi i_set_failure_internal();
a8dac1be6a0c3adbbce5887ca395f418194c6c06Aki Tuomi i_set_failure_prefix(prefix);
53f97800b16ab3a8d263c5331132dec1e8fea9a0Aki Tuomi return;
bf72c930996df0691932fb1143f360d260f27a06Timo Sirainen }
c0a708fa3f7b8f4fbca32052da5faf7a0125189dTimo Sirainen
cd94aeaa294f7cc507206b4b2075852f00e14d61Timo Sirainen if (service->set == NULL) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen i_set_failure_file("/dev/stderr", prefix);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return;
660b99a7059824676b2b8d6f79b8e15d47df25a2Timo Sirainen }
5a9e240ebf8d0daaf029973973b52e415148070bTimo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (*service->set->log_path == '\0') {
bbba7d0fce1b6ce5baa2d7ef946eb1b63e2ab518Timo Sirainen /* log to syslog */
bbba7d0fce1b6ce5baa2d7ef946eb1b63e2ab518Timo Sirainen int facility;
c0a708fa3f7b8f4fbca32052da5faf7a0125189dTimo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (!syslog_facility_find(service->set->syslog_facility,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen &facility))
de754cb78f75e8b3b994cddafe41c9ed1467c33dTimo Sirainen facility = LOG_MAIL;
de754cb78f75e8b3b994cddafe41c9ed1467c33dTimo Sirainen i_set_failure_syslog("dovecot", LOG_NDELAY, facility);
ddbdc644a15f56f4b43596f1b8c0fc196c101445Timo Sirainen i_set_failure_prefix(prefix);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen } else {
c49a19168dab6fda80aee16ad799a8a56d3bc18fTimo Sirainen /* log to file or stderr */
9137c55411aa39d41c1e705ddc34d5bd26c65021Timo Sirainen path = home_expand(service->set->log_path);
a75907609d7c410c9e17beedfafbf28b4439fa8aTimo Sirainen i_set_failure_file(path, prefix);
7a94f950fd1dcc81537acfc8adb030b5e703d722Timo Sirainen }
aebfda1f6e3a924c31e8f54237b81fabf4b2264cTimo Sirainen
2dfd08e8aa16dfcc975d8a62bc8d20b2ef849d71Timo Sirainen path = home_expand(service->set->info_log_path);
c49a19168dab6fda80aee16ad799a8a56d3bc18fTimo Sirainen if (*path != '\0')
38cb3d139aefb7c65919cf4aba5ded7b5fd50e6fTimo Sirainen i_set_info_file(path);
2a90d8a14b0e7cc1508814bc87d3dfa598ef46a8Timo Sirainen
c49a19168dab6fda80aee16ad799a8a56d3bc18fTimo Sirainen path = home_expand(service->set->debug_log_path);
bf72c930996df0691932fb1143f360d260f27a06Timo Sirainen if (*path != '\0')
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen i_set_debug_file(path);
843640f0ca224bb9999acb290bca5f76037ab984Timo Sirainen i_set_failure_timestamp_format(service->set->log_timestamp);
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch}
087939d3fa9c4056419386c9d6c81f147de534cdTimo Sirainen
02ccba3d3be96444abd15b5254864c9151bbeb30Timo Sirainenvoid master_service_set_die_with_master(struct master_service *service,
9132f9df4e12ed5293c70957813aa3736444a13cTimo Sirainen bool set)
9132f9df4e12ed5293c70957813aa3736444a13cTimo Sirainen{
9132f9df4e12ed5293c70957813aa3736444a13cTimo Sirainen service->die_with_master = set;
2f90189c6ee66a17f7bf838a8eb8a69868630fb8Timo Sirainen}
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainenbool master_service_parse_option(struct master_service *service,
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen int opt, const char *arg)
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen{
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen int i;
ec9429535e925610513bd6bfde6729e273082ccbTimo Sirainen
ec9429535e925610513bd6bfde6729e273082ccbTimo Sirainen switch (opt) {
ec9429535e925610513bd6bfde6729e273082ccbTimo Sirainen case 'c':
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen service->config_path = arg;
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen break;
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen case 'k':
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen service->keep_environment = TRUE;
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen break;
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen case 'o':
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen if (!array_is_created(&service->config_overrides))
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen i_array_init(&service->config_overrides, 16);
56af9dd10e7e6caeaca64395bad3f882b28ecdffTimo Sirainen array_append(&service->config_overrides, &arg, 1);
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen break;
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen case 'O':
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen service->flags |= MASTER_SERVICE_FLAG_NO_CONFIG_SETTINGS;
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen break;
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen case 's':
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen if ((i = atoi(arg)) < 0)
6d24551e169c0808695db35d7a228f1970a84c75Timo Sirainen i_fatal("Invalid socket count: %s", arg);
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen service->socket_count = i;
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen break;
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen case 'L':
c0a708fa3f7b8f4fbca32052da5faf7a0125189dTimo Sirainen service->log_directly = TRUE;
c0a708fa3f7b8f4fbca32052da5faf7a0125189dTimo Sirainen break;
c0a708fa3f7b8f4fbca32052da5faf7a0125189dTimo Sirainen default:
9132f9df4e12ed5293c70957813aa3736444a13cTimo Sirainen return FALSE;
9132f9df4e12ed5293c70957813aa3736444a13cTimo Sirainen }
9132f9df4e12ed5293c70957813aa3736444a13cTimo Sirainen return TRUE;
9132f9df4e12ed5293c70957813aa3736444a13cTimo Sirainen}
9132f9df4e12ed5293c70957813aa3736444a13cTimo Sirainen
c5e62353a11087958ea4e619660e084a613e1a37Timo Sirainenstatic void master_service_error(struct master_service *service)
c5e62353a11087958ea4e619660e084a613e1a37Timo Sirainen{
c5e62353a11087958ea4e619660e084a613e1a37Timo Sirainen if (service->master_status.available_count ==
c5e62353a11087958ea4e619660e084a613e1a37Timo Sirainen service->total_available_count || service->die_with_master)
ad004e44be109684521494b5af2ad1da39b8bb27Timo Sirainen master_service_stop(service);
ad004e44be109684521494b5af2ad1da39b8bb27Timo Sirainen else
9132f9df4e12ed5293c70957813aa3736444a13cTimo Sirainen io_listeners_remove(service);
9132f9df4e12ed5293c70957813aa3736444a13cTimo Sirainen}
c5e62353a11087958ea4e619660e084a613e1a37Timo Sirainen
c5e62353a11087958ea4e619660e084a613e1a37Timo Sirainenstatic void master_status_error(void *context)
c5e62353a11087958ea4e619660e084a613e1a37Timo Sirainen{
9132f9df4e12ed5293c70957813aa3736444a13cTimo Sirainen struct master_service *service = context;
f059a046515f4b2b15a6c2a10a6f12f6166e39a5Timo Sirainen
f059a046515f4b2b15a6c2a10a6f12f6166e39a5Timo Sirainen /* status fd is a write-only pipe, so if we're here it means the
a05fec120ecd8c4ed6331c42100cba42adf22893Stephan Bosch master wants us to die (or died itself). don't die until all
f29756821a4c6b12b73e4a2a3e1c230117a43773Timo Sirainen service connections are finished. */
f29756821a4c6b12b73e4a2a3e1c230117a43773Timo Sirainen io_remove(&service->io_status_error);
a05fec120ecd8c4ed6331c42100cba42adf22893Stephan Bosch
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* the log fd may also be closed already, don't die when trying to
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen log later */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen i_set_failure_ignore_errors(TRUE);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen master_service_error(service);
b66d803de86bfb411165b3465b0d9ef64ecfe2a1Timo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenvoid master_service_init_finish(struct master_service *service)
c0a708fa3f7b8f4fbca32052da5faf7a0125189dTimo Sirainen{
68a4946b12583b88fa802e52ebee45cd96056772Timo Sirainen struct stat st;
ad004e44be109684521494b5af2ad1da39b8bb27Timo Sirainen const char *value;
c4ec7cb598805b1387dc3aab59ec8f32d8cc24e1Timo Sirainen unsigned int count;
c4ec7cb598805b1387dc3aab59ec8f32d8cc24e1Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen i_assert(service->total_available_count == 0);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen i_assert(service->service_count_left == (unsigned int)-1);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
b9c76fe9d9ca194816606342da1ddbd9be6bc8abTimo Sirainen /* set default signal handlers */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen lib_signals_init();
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen lib_signals_ignore(SIGPIPE, TRUE);
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen lib_signals_ignore(SIGALRM, FALSE);
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen lib_signals_set_handler(SIGINT, TRUE, sig_die, service);
6a9e034441607c0c5a61858ff559af4615ac31caTimo Sirainen lib_signals_set_handler(SIGTERM, TRUE, sig_die, service);
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen
b55f914c0ade77252cfd798ea8eb9a84bda56315Timo Sirainen if ((service->flags & MASTER_SERVICE_FLAG_STANDALONE) == 0) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (fstat(MASTER_STATUS_FD, &st) < 0 || !S_ISFIFO(st.st_mode))
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen i_fatal("Must be started by dovecot master process");
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* initialize master_status structure */
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen value = getenv(MASTER_UID_ENV);
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen if (value == NULL)
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen i_fatal(MASTER_UID_ENV" not set");
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen service->master_status.pid = getpid();
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen service->master_status.uid =
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen (unsigned int)strtoul(value, NULL, 10);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* set the default limit */
ae797f3368ebb86e7786ca25d7c9c703f672b9f5Timo Sirainen value = getenv(MASTER_CLIENT_LIMIT_ENV);
ae797f3368ebb86e7786ca25d7c9c703f672b9f5Timo Sirainen count = value == NULL ? 0 :
b84eff65e25ae86dfd6f798386577209b94838c6Timo Sirainen (unsigned int)strtoul(value, NULL, 10);
00fa8dcbc66f56daa737487c9dec7166c37de79eTimo Sirainen if (count == 0)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen i_fatal(MASTER_CLIENT_LIMIT_ENV" not set");
46ec792dd4ccf6c34706c4774228301fafde6aa9Timo Sirainen master_service_set_client_limit(service, count);
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* set the default service count */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen value = getenv(MASTER_SERVICE_COUNT_ENV);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen count = value == NULL ? 0 :
decb23442f9e6cd5c4845a9cb162029b8c6d5f0fTimo Sirainen (unsigned int)strtoul(value, NULL, 10);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (count > 0)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen master_service_set_service_count(service, count);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* start listening errors for status fd, it means master died */
2b96880f2d789d125aff6a95eaa7b51f558a6a1cTimo Sirainen service->io_status_error = io_add(MASTER_STATUS_FD, IO_ERROR,
c49a19168dab6fda80aee16ad799a8a56d3bc18fTimo Sirainen master_status_error, service);
2a90d8a14b0e7cc1508814bc87d3dfa598ef46a8Timo Sirainen } else {
71f1783adc89b4fe3588c72b23e059b320da8fadTimo Sirainen master_service_set_client_limit(service, 1);
c0a708fa3f7b8f4fbca32052da5faf7a0125189dTimo Sirainen master_service_set_service_count(service, 1);
717bb0dbaf4bd3f745669570647845e6d493bfe0Timo Sirainen }
c0a708fa3f7b8f4fbca32052da5faf7a0125189dTimo Sirainen
c5e62353a11087958ea4e619660e084a613e1a37Timo Sirainen master_service_io_listeners_add(service);
c5e62353a11087958ea4e619660e084a613e1a37Timo Sirainen
c5e62353a11087958ea4e619660e084a613e1a37Timo Sirainen if ((service->flags & MASTER_SERVICE_FLAG_STD_CLIENT) != 0) {
c0a708fa3f7b8f4fbca32052da5faf7a0125189dTimo Sirainen /* 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)
{
unsigned int used;
i_assert(service->master_status.available_count ==
service->total_available_count);
used = service->total_available_count -
service->master_status.available_count;
i_assert(client_limit >= used);
service->total_available_count = client_limit;
service->master_status.available_count = client_limit - used;
}
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;
}
void master_service_set_avail_overflow_callback(struct master_service *service,
void (*callback)(void))
{
service->avail_overflow_callback = callback;
}
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;
}
const char *master_service_get_name(struct master_service *service)
{
return service->name;
}
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) {
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++;
} 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->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_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;
io_listeners_remove(service);
master_service_close_config_fd(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->listeners);
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->avail_overflow_callback();
if (service->master_status.available_count == 0) {
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);
service->master_status.available_count--;
master_status_update(service);
service->callback(&conn);
if (service->master_status.available_count == 0 &&
service->service_count_left == 1) {
/* we're dying as soon as this connection closes. */
master_service_close_config_fd(service);
}
}
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->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);
}
}
}
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);
}
}
}
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);
}
}
}