master-service.c revision 0c96e2994ab4e25c9042ce21e9d39ca5054df3b6
2454dfa32c93c20a8522c6ed42fe057baaac9f9aStephan Bosch/* Copyright (c) 2005-2012 Dovecot authors, see the included COPYING file */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "lib.h"
35316602eabbae7dcb86dd74c71e04cce45ba7c7Timo Sirainen#include "lib-signals.h"
16f816d3f3c32ae3351834253f52ddd0212bcbf3Timo Sirainen#include "ioloop.h"
26e5bdf37d7d0deed1e2e8483366c83631b9d251Aki Tuomi#include "abspath.h"
27bc15088a485a8047fca9b0d24d2904c6dda919Timo Sirainen#include "array.h"
ef4d0eafab4d26bba047551db1e23ceff8aa9404Timo Sirainen#include "strescape.h"
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen#include "env-util.h"
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen#include "home-expand.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "process-title.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "restrict-access.h"
a102d189881a35d72ac3106a9e7e00577ae69310Aki Tuomi#include "fd-close-on-exec.h"
2b8ff102f5117f917248f98ccbdc8e6a6af83c87Aki Tuomi#include "settings-parser.h"
2b8ff102f5117f917248f98ccbdc8e6a6af83c87Aki Tuomi#include "syslog-util.h"
a102d189881a35d72ac3106a9e7e00577ae69310Aki Tuomi#include "master-instance.h"
2b8ff102f5117f917248f98ccbdc8e6a6af83c87Aki Tuomi#include "master-login.h"
2b8ff102f5117f917248f98ccbdc8e6a6af83c87Aki Tuomi#include "master-service-private.h"
2b8ff102f5117f917248f98ccbdc8e6a6af83c87Aki Tuomi#include "master-service-settings.h"
2b8ff102f5117f917248f98ccbdc8e6a6af83c87Aki Tuomi
2b8ff102f5117f917248f98ccbdc8e6a6af83c87Aki Tuomi#include <stdlib.h>
2b8ff102f5117f917248f98ccbdc8e6a6af83c87Aki Tuomi#include <unistd.h>
a102d189881a35d72ac3106a9e7e00577ae69310Aki Tuomi#include <sys/stat.h>
2b8ff102f5117f917248f98ccbdc8e6a6af83c87Aki Tuomi#include <syslog.h>
2b8ff102f5117f917248f98ccbdc8e6a6af83c87Aki Tuomi
2b8ff102f5117f917248f98ccbdc8e6a6af83c87Aki Tuomi#define DEFAULT_CONFIG_FILE_PATH SYSCONFDIR"/dovecot.conf"
2b8ff102f5117f917248f98ccbdc8e6a6af83c87Aki Tuomi
2b8ff102f5117f917248f98ccbdc8e6a6af83c87Aki Tuomi/* getenv(MASTER_CONFIG_FILE_ENV) provides path to configuration file/socket */
2b8ff102f5117f917248f98ccbdc8e6a6af83c87Aki Tuomi#define MASTER_CONFIG_FILE_ENV "CONFIG_FILE"
a102d189881a35d72ac3106a9e7e00577ae69310Aki Tuomi
2b8ff102f5117f917248f98ccbdc8e6a6af83c87Aki Tuomi/* getenv(MASTER_DOVECOT_VERSION_ENV) provides master's version number */
2b8ff102f5117f917248f98ccbdc8e6a6af83c87Aki Tuomi#define MASTER_DOVECOT_VERSION_ENV "DOVECOT_VERSION"
2b8ff102f5117f917248f98ccbdc8e6a6af83c87Aki Tuomi
2b8ff102f5117f917248f98ccbdc8e6a6af83c87Aki Tuomi/* when we're full of connections, how often to check if login state has
2b8ff102f5117f917248f98ccbdc8e6a6af83c87Aki Tuomi changed. we normally notice it immediately because of a signal, so this is
2b8ff102f5117f917248f98ccbdc8e6a6af83c87Aki Tuomi just a fallback against race conditions. */
2b8ff102f5117f917248f98ccbdc8e6a6af83c87Aki Tuomi#define MASTER_SERVICE_STATE_CHECK_MSECS 1000
2b8ff102f5117f917248f98ccbdc8e6a6af83c87Aki Tuomi
2b8ff102f5117f917248f98ccbdc8e6a6af83c87Aki Tuomi/* If die callback hasn't managed to stop the service for this many seconds,
2b8ff102f5117f917248f98ccbdc8e6a6af83c87Aki Tuomi force it. */
2b8ff102f5117f917248f98ccbdc8e6a6af83c87Aki Tuomi#define MASTER_SERVICE_DIE_TIMEOUT_MSECS (30*1000)
b346610430690398b8c840006004a2df4aa8ce92Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstruct master_service *master_service;
48136ae5a0eb49daa44e343553f3688a500307e2Timo Sirainen
2674b4f0cf8f3c203d8e56b29735f5e267038dafTimo Sirainenstatic void master_service_io_listeners_close(struct master_service *service);
48136ae5a0eb49daa44e343553f3688a500307e2Timo Sirainenstatic void master_service_refresh_login_state(struct master_service *service);
48136ae5a0eb49daa44e343553f3688a500307e2Timo Sirainen
48136ae5a0eb49daa44e343553f3688a500307e2Timo Sirainenconst char *master_service_getopt_string(void)
48136ae5a0eb49daa44e343553f3688a500307e2Timo Sirainen{
e376693bfa3985232c41df99c7010fca22612c89Timo Sirainen return "c:i:ko:OL";
e376693bfa3985232c41df99c7010fca22612c89Timo Sirainen}
e376693bfa3985232c41df99c7010fca22612c89Timo Sirainen
0c909e3461607eadcd66f4eac69b7f34e37fccf1Timo Sirainenstatic void sig_die(const siginfo_t *si, void *context)
0c909e3461607eadcd66f4eac69b7f34e37fccf1Timo Sirainen{
0c909e3461607eadcd66f4eac69b7f34e37fccf1Timo Sirainen struct master_service *service = context;
0c909e3461607eadcd66f4eac69b7f34e37fccf1Timo Sirainen
0c909e3461607eadcd66f4eac69b7f34e37fccf1Timo Sirainen /* SIGINT comes either from master process or from keyboard. we don't
0c909e3461607eadcd66f4eac69b7f34e37fccf1Timo Sirainen want to log it in either case.*/
0c909e3461607eadcd66f4eac69b7f34e37fccf1Timo Sirainen if (si->si_signo != SIGINT) {
0c909e3461607eadcd66f4eac69b7f34e37fccf1Timo Sirainen i_warning("Killed with signal %d (by pid=%s uid=%s code=%s)",
0c909e3461607eadcd66f4eac69b7f34e37fccf1Timo Sirainen si->si_signo, dec2str(si->si_pid),
0c909e3461607eadcd66f4eac69b7f34e37fccf1Timo Sirainen dec2str(si->si_uid),
0c909e3461607eadcd66f4eac69b7f34e37fccf1Timo Sirainen lib_signal_code_to_str(si->si_signo, si->si_code));
e376693bfa3985232c41df99c7010fca22612c89Timo Sirainen } else if ((service->flags & MASTER_SERVICE_FLAG_NO_IDLE_DIE) != 0) {
e376693bfa3985232c41df99c7010fca22612c89Timo Sirainen /* never die when idling */
09c3a491f4f6ccebe290c7709bdc0d79a187610bTimo Sirainen return;
09c3a491f4f6ccebe290c7709bdc0d79a187610bTimo Sirainen } else if ((service->flags & MASTER_SERVICE_FLAG_STANDALONE) == 0) {
09c3a491f4f6ccebe290c7709bdc0d79a187610bTimo Sirainen /* SIGINT came from master. die only if we're not handling
09c3a491f4f6ccebe290c7709bdc0d79a187610bTimo Sirainen any clients currently. */
09c3a491f4f6ccebe290c7709bdc0d79a187610bTimo Sirainen if (service->master_status.available_count !=
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen service->total_available_count)
09c3a491f4f6ccebe290c7709bdc0d79a187610bTimo Sirainen return;
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen if (service->idle_die_callback != NULL &&
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen !service->idle_die_callback())
b346610430690398b8c840006004a2df4aa8ce92Timo Sirainen return;
b346610430690398b8c840006004a2df4aa8ce92Timo Sirainen }
b346610430690398b8c840006004a2df4aa8ce92Timo Sirainen
741d705983e10046f07ef372b760bcdd169b068aTimo Sirainen service->killed = TRUE;
b346610430690398b8c840006004a2df4aa8ce92Timo Sirainen io_loop_stop(service->ioloop);
ef4d0eafab4d26bba047551db1e23ceff8aa9404Timo Sirainen}
b346610430690398b8c840006004a2df4aa8ce92Timo Sirainen
b346610430690398b8c840006004a2df4aa8ce92Timo Sirainenstatic void
515f81466f673c1b4f72e053f1a9686e6fca6b61Timo Sirainensig_state_changed(const siginfo_t *si ATTR_UNUSED, void *context)
515f81466f673c1b4f72e053f1a9686e6fca6b61Timo Sirainen{
b346610430690398b8c840006004a2df4aa8ce92Timo Sirainen struct master_service *service = context;
b346610430690398b8c840006004a2df4aa8ce92Timo Sirainen
09c3a491f4f6ccebe290c7709bdc0d79a187610bTimo Sirainen master_service_refresh_login_state(service);
09c3a491f4f6ccebe290c7709bdc0d79a187610bTimo Sirainen}
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen
659fe5d24825b160cae512538088020d97a60239Timo Sirainenstatic void master_service_verify_version_string(struct master_service *service)
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen{
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen if (service->version_string != NULL &&
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen strcmp(service->version_string, PACKAGE_VERSION) != 0) {
7212243efb0d8fa1cd8b2e37b7498323540b9e97Timo Sirainen i_fatal("Dovecot version mismatch: "
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen "Master is v%s, %s is v"PACKAGE_VERSION" "
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen "(if you don't care, set version_ignore=yes)",
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen service->version_string, service->name);
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen }
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen}
d1baa8c6f97cdb1b3c2c44a73cc21f9dfc7a963fTimo Sirainen
d1baa8c6f97cdb1b3c2c44a73cc21f9dfc7a963fTimo Sirainenstruct master_service *
d1baa8c6f97cdb1b3c2c44a73cc21f9dfc7a963fTimo Sirainenmaster_service_init(const char *name, enum master_service_flags flags,
d1baa8c6f97cdb1b3c2c44a73cc21f9dfc7a963fTimo Sirainen int *argc, char **argv[], const char *getopt_str)
d1baa8c6f97cdb1b3c2c44a73cc21f9dfc7a963fTimo Sirainen{
7212243efb0d8fa1cd8b2e37b7498323540b9e97Timo Sirainen struct master_service *service;
7212243efb0d8fa1cd8b2e37b7498323540b9e97Timo Sirainen const char *value;
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen unsigned int count;
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen i_assert(name != NULL);
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen#ifdef DEBUG
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen if (getenv("GDB") == NULL &&
7212243efb0d8fa1cd8b2e37b7498323540b9e97Timo Sirainen (flags & MASTER_SERVICE_FLAG_STANDALONE) == 0) {
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen int count;
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen value = getenv("SOCKET_COUNT");
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen count = value == NULL ? 0 : atoi(value);
7212243efb0d8fa1cd8b2e37b7498323540b9e97Timo Sirainen fd_debug_verify_leaks(MASTER_LISTEN_FD_FIRST + count, 1024);
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen }
7212243efb0d8fa1cd8b2e37b7498323540b9e97Timo Sirainen#endif
7212243efb0d8fa1cd8b2e37b7498323540b9e97Timo Sirainen if ((flags & MASTER_SERVICE_FLAG_STANDALONE) == 0) {
515f81466f673c1b4f72e053f1a9686e6fca6b61Timo Sirainen /* make sure we can dump core, at least until
515f81466f673c1b4f72e053f1a9686e6fca6b61Timo Sirainen privileges are dropped. (i'm not really sure why this
515f81466f673c1b4f72e053f1a9686e6fca6b61Timo Sirainen is needed, because doing the same just before exec
515f81466f673c1b4f72e053f1a9686e6fca6b61Timo Sirainen doesn't help, and exec shouldn't affect this with
515f81466f673c1b4f72e053f1a9686e6fca6b61Timo Sirainen non-setuid/gid binaries..) */
515f81466f673c1b4f72e053f1a9686e6fca6b61Timo Sirainen restrict_access_allow_coredumps(TRUE);
4253a4a66323bc5ff1103af7d7f77fe7c78b9b2bTimo Sirainen }
4253a4a66323bc5ff1103af7d7f77fe7c78b9b2bTimo Sirainen
4253a4a66323bc5ff1103af7d7f77fe7c78b9b2bTimo Sirainen /* NOTE: we start rooted, so keep the code minimal until
4253a4a66323bc5ff1103af7d7f77fe7c78b9b2bTimo Sirainen restrict_access_by_env() is called */
4253a4a66323bc5ff1103af7d7f77fe7c78b9b2bTimo Sirainen lib_init();
515f81466f673c1b4f72e053f1a9686e6fca6b61Timo Sirainen /* Set a logging prefix temporarily. This will be ignored once the log
b66d803de86bfb411165b3465b0d9ef64ecfe2a1Timo Sirainen is properly initialized */
515f81466f673c1b4f72e053f1a9686e6fca6b61Timo Sirainen i_set_failure_prefix(t_strdup_printf("%s(init): ", name));
515f81466f673c1b4f72e053f1a9686e6fca6b61Timo Sirainen
93d08c32afb545ba32e9a1d973b34756c4b01983Timo Sirainen /* ignore these signals as early as possible */
93d08c32afb545ba32e9a1d973b34756c4b01983Timo Sirainen lib_signals_ignore(SIGPIPE, TRUE);
4253a4a66323bc5ff1103af7d7f77fe7c78b9b2bTimo Sirainen lib_signals_ignore(SIGALRM, FALSE);
4253a4a66323bc5ff1103af7d7f77fe7c78b9b2bTimo Sirainen
515f81466f673c1b4f72e053f1a9686e6fca6b61Timo Sirainen if (getenv(MASTER_UID_ENV) == NULL)
515f81466f673c1b4f72e053f1a9686e6fca6b61Timo Sirainen flags |= MASTER_SERVICE_FLAG_STANDALONE;
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen process_title_init(argv);
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen
7212243efb0d8fa1cd8b2e37b7498323540b9e97Timo Sirainen service = i_new(struct master_service, 1);
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen service->argc = *argc;
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen service->argv = *argv;
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen service->name = i_strdup(name);
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen /* keep getopt_str first in case it contains "+" */
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen service->getopt_str = getopt_str == NULL ?
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen i_strdup(master_service_getopt_string()) :
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen i_strconcat(getopt_str, master_service_getopt_string(), NULL);
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen service->flags = flags;
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen service->ioloop = io_loop_create();
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen service->service_count_left = (unsigned int)-1;
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen service->config_fd = -1;
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen
8e7da21696c9f8a6d5e601243fb6172ec85d47b2Timo Sirainen service->config_path = i_strdup(getenv(MASTER_CONFIG_FILE_ENV));
659fe5d24825b160cae512538088020d97a60239Timo Sirainen if (service->config_path == NULL) {
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen service->config_path = i_strdup(DEFAULT_CONFIG_FILE_PATH);
659fe5d24825b160cae512538088020d97a60239Timo Sirainen service->config_path_is_default = TRUE;
5e88e4624aa6d482b5b195acd2f4e02aeb385f20Timo Sirainen }
5e88e4624aa6d482b5b195acd2f4e02aeb385f20Timo Sirainen
5e88e4624aa6d482b5b195acd2f4e02aeb385f20Timo Sirainen if ((flags & MASTER_SERVICE_FLAG_STANDALONE) == 0) {
5e88e4624aa6d482b5b195acd2f4e02aeb385f20Timo Sirainen service->version_string = getenv(MASTER_DOVECOT_VERSION_ENV);
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen service->socket_count = 1;
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen } else {
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen service->version_string = PACKAGE_VERSION;
e6f0cbdb1eb604f21a65cd45072febe678187054Timo Sirainen }
e6f0cbdb1eb604f21a65cd45072febe678187054Timo Sirainen value = getenv("SOCKET_COUNT");
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainen if (value != NULL)
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainen service->socket_count = atoi(value);
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainen value = getenv("SSL_SOCKET_COUNT");
287ba82a8da3eaa473b5735d4eeac2fb4c5d8117Timo Sirainen if (value != NULL)
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen service->ssl_socket_count = atoi(value);
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen value = getenv("SOCKET_NAMES");
e169102fb38ce788b76c2a344bee7d77079dea05Timo Sirainen if (value != NULL) {
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen service->listener_names =
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen p_strsplit_tabescaped(default_pool, value);
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen service->listener_names_count =
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen str_array_length((void *)service->listener_names);
e169102fb38ce788b76c2a344bee7d77079dea05Timo Sirainen }
e169102fb38ce788b76c2a344bee7d77079dea05Timo Sirainen
e169102fb38ce788b76c2a344bee7d77079dea05Timo Sirainen /* set up some kind of logging until we know exactly how and where
e169102fb38ce788b76c2a344bee7d77079dea05Timo Sirainen we want to log */
e169102fb38ce788b76c2a344bee7d77079dea05Timo Sirainen if (getenv("LOG_SERVICE") != NULL)
e169102fb38ce788b76c2a344bee7d77079dea05Timo Sirainen i_set_failure_internal();
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen if (getenv("USER") != NULL) {
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen i_set_failure_prefix(t_strdup_printf("%s(%s): ",
9e6d83a3ef6abb393eeebca423cfd0d8cb08d430Timo Sirainen name, getenv("USER")));
9e6d83a3ef6abb393eeebca423cfd0d8cb08d430Timo Sirainen } else {
9e6d83a3ef6abb393eeebca423cfd0d8cb08d430Timo Sirainen i_set_failure_prefix(t_strdup_printf("%s: ", name));
9e6d83a3ef6abb393eeebca423cfd0d8cb08d430Timo Sirainen }
d2470b3dfe91ca07459185384ee25080b42a1636Timo Sirainen
5e88e4624aa6d482b5b195acd2f4e02aeb385f20Timo Sirainen if ((flags & MASTER_SERVICE_FLAG_STANDALONE) == 0) {
5e88e4624aa6d482b5b195acd2f4e02aeb385f20Timo Sirainen /* initialize master_status structure */
d2470b3dfe91ca07459185384ee25080b42a1636Timo Sirainen value = getenv(MASTER_UID_ENV);
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen if (value == NULL ||
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen str_to_uint(value, &service->master_status.uid) < 0)
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen i_fatal(MASTER_UID_ENV" missing");
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen service->master_status.pid = getpid();
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen /* set the default limit */
406393bc328f056c49df0804f894ac2070aa5846Timo Sirainen value = getenv(MASTER_CLIENT_LIMIT_ENV);
9e6d83a3ef6abb393eeebca423cfd0d8cb08d430Timo Sirainen if (value == NULL || str_to_uint(value, &count) < 0 ||
9e6d83a3ef6abb393eeebca423cfd0d8cb08d430Timo Sirainen count == 0)
9e6d83a3ef6abb393eeebca423cfd0d8cb08d430Timo Sirainen i_fatal(MASTER_CLIENT_LIMIT_ENV" missing");
9e6d83a3ef6abb393eeebca423cfd0d8cb08d430Timo Sirainen master_service_set_client_limit(service, count);
3b426f49d36187895debdda67fff09f97941881cTimo Sirainen
3b426f49d36187895debdda67fff09f97941881cTimo Sirainen /* seve the process limit */
3b426f49d36187895debdda67fff09f97941881cTimo Sirainen value = getenv(MASTER_PROCESS_LIMIT_ENV);
46b823ac3bce2c0f9f0fc73911e48d3a77b04fbeTimo Sirainen if (value != NULL && str_to_uint(value, &count) == 0 &&
3b426f49d36187895debdda67fff09f97941881cTimo Sirainen count > 0)
3b426f49d36187895debdda67fff09f97941881cTimo Sirainen service->process_limit = count;
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainen
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainen /* set the default service count */
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen value = getenv(MASTER_SERVICE_COUNT_ENV);
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen if (value != NULL && str_to_uint(value, &count) == 0 &&
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen count > 0)
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen master_service_set_service_count(service, count);
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen /* set the idle kill timeout */
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen value = getenv(MASTER_SERVICE_IDLE_KILL_ENV);
5ada3f57a970f226eb29956d30f66afc3537200dTimo Sirainen if (value != NULL && str_to_uint(value, &count) == 0)
5ada3f57a970f226eb29956d30f66afc3537200dTimo Sirainen service->idle_kill_secs = count;
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen } else {
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainen master_service_set_client_limit(service, 1);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen master_service_set_service_count(service, 1);
1279090ba03f9c176976a69ab7718f0ed77b19afTimo Sirainen }
61ddcdc28f50d9cb9994fcc4ad63f9dff0e80628Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen master_service_verify_version_string(service);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return service;
439942f89a77180719644e7af3752a8329259eb9Timo Sirainen}
439942f89a77180719644e7af3752a8329259eb9Timo Sirainen
439942f89a77180719644e7af3752a8329259eb9Timo Sirainenint master_getopt(struct master_service *service)
61ddcdc28f50d9cb9994fcc4ad63f9dff0e80628Timo Sirainen{
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainen int c;
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainen
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainen while ((c = getopt(service->argc, service->argv,
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainen service->getopt_str)) > 0) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (!master_service_parse_option(service, c, optarg))
61ddcdc28f50d9cb9994fcc4ad63f9dff0e80628Timo Sirainen break;
5d1833b98fa85d8061626aa986f38dcbcd70553eTimo Sirainen }
5d1833b98fa85d8061626aa986f38dcbcd70553eTimo Sirainen return c;
5d1833b98fa85d8061626aa986f38dcbcd70553eTimo Sirainen}
5d1833b98fa85d8061626aa986f38dcbcd70553eTimo Sirainen
5d1833b98fa85d8061626aa986f38dcbcd70553eTimo Sirainenvoid master_service_init_log(struct master_service *service,
5d1833b98fa85d8061626aa986f38dcbcd70553eTimo Sirainen const char *prefix)
5d1833b98fa85d8061626aa986f38dcbcd70553eTimo Sirainen{
5d1833b98fa85d8061626aa986f38dcbcd70553eTimo Sirainen const char *path;
5d1833b98fa85d8061626aa986f38dcbcd70553eTimo Sirainen
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainen if ((service->flags & MASTER_SERVICE_FLAG_STANDALONE) != 0 &&
b66d803de86bfb411165b3465b0d9ef64ecfe2a1Timo Sirainen (service->flags & MASTER_SERVICE_FLAG_DONT_LOG_TO_STDERR) == 0) {
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainen i_set_failure_file("/dev/stderr", "");
1f68b6db9b8d02b0f4116e42ac82c4aac5579574Timo Sirainen return;
1f68b6db9b8d02b0f4116e42ac82c4aac5579574Timo Sirainen }
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (getenv("LOG_SERVICE") != NULL && !service->log_directly) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* logging via log service */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_set_failure_internal();
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen i_set_failure_prefix(prefix);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return;
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen }
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen if (service->set == NULL) {
9404a7b90dcb80d31bd37ee2493f03751acdb1bdTimo Sirainen i_set_failure_file("/dev/stderr", prefix);
9404a7b90dcb80d31bd37ee2493f03751acdb1bdTimo Sirainen return;
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainen }
9404a7b90dcb80d31bd37ee2493f03751acdb1bdTimo Sirainen
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainen if (strcmp(service->set->log_path, "syslog") != 0) {
9404a7b90dcb80d31bd37ee2493f03751acdb1bdTimo Sirainen /* error logging goes to file or stderr */
9404a7b90dcb80d31bd37ee2493f03751acdb1bdTimo Sirainen path = home_expand(service->set->log_path);
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainen i_set_failure_file(path, prefix);
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainen }
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen if (strcmp(service->set->log_path, "syslog") == 0 ||
8872e5c991430f96138a46e36b7f3c2c40d8e5c2Timo Sirainen strcmp(service->set->info_log_path, "syslog") == 0 ||
8872e5c991430f96138a46e36b7f3c2c40d8e5c2Timo Sirainen strcmp(service->set->debug_log_path, "syslog") == 0) {
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen /* something gets logged to syslog */
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen int facility;
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen if (!syslog_facility_find(service->set->syslog_facility,
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen &facility))
491178793199e62320f7bc6292e7b8fd843ae5bcTimo Sirainen facility = LOG_MAIL;
8872e5c991430f96138a46e36b7f3c2c40d8e5c2Timo Sirainen i_set_failure_syslog("dovecot", LOG_NDELAY, facility);
8872e5c991430f96138a46e36b7f3c2c40d8e5c2Timo Sirainen i_set_failure_prefix(prefix);
8872e5c991430f96138a46e36b7f3c2c40d8e5c2Timo Sirainen
8872e5c991430f96138a46e36b7f3c2c40d8e5c2Timo Sirainen if (strcmp(service->set->log_path, "syslog") != 0) {
8872e5c991430f96138a46e36b7f3c2c40d8e5c2Timo Sirainen /* set error handlers back to file */
8872e5c991430f96138a46e36b7f3c2c40d8e5c2Timo Sirainen i_set_fatal_handler(NULL);
8872e5c991430f96138a46e36b7f3c2c40d8e5c2Timo Sirainen i_set_error_handler(NULL);
651fc0f1e43fef3e02e0e7b5f498973b05f641d7Timo Sirainen }
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen }
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen
efe78d3ba24fc866af1c79b9223dc0809ba26cadStephan Bosch if (*service->set->info_log_path != '\0' &&
8872e5c991430f96138a46e36b7f3c2c40d8e5c2Timo Sirainen strcmp(service->set->info_log_path, "syslog") != 0) {
8872e5c991430f96138a46e36b7f3c2c40d8e5c2Timo Sirainen path = home_expand(service->set->info_log_path);
8872e5c991430f96138a46e36b7f3c2c40d8e5c2Timo Sirainen if (*path != '\0')
8872e5c991430f96138a46e36b7f3c2c40d8e5c2Timo Sirainen i_set_info_file(path);
8872e5c991430f96138a46e36b7f3c2c40d8e5c2Timo Sirainen }
8872e5c991430f96138a46e36b7f3c2c40d8e5c2Timo Sirainen
8872e5c991430f96138a46e36b7f3c2c40d8e5c2Timo Sirainen if (*service->set->debug_log_path != '\0' &&
8872e5c991430f96138a46e36b7f3c2c40d8e5c2Timo Sirainen strcmp(service->set->debug_log_path, "syslog") != 0) {
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen path = home_expand(service->set->debug_log_path);
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen if (*path != '\0')
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen i_set_debug_file(path);
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen }
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen i_set_failure_timestamp_format(service->set->log_timestamp);
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen}
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainenvoid master_service_set_die_with_master(struct master_service *service,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen bool set)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
ab0d9eecd85f74acae18fe88529302e0776cc500Timo Sirainen service->die_with_master = set;
5e40ed3f0a2c2acddc9b8eab59670c7a850114c5Timo Sirainen}
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainenvoid master_service_set_die_callback(struct master_service *service,
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen void (*callback)(void))
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen{
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen service->die_callback = callback;
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen}
db87d16551d1081ada01f787ea21aa3ed1402c31Timo Sirainen
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainenvoid master_service_set_idle_die_callback(struct master_service *service,
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen bool (*callback)(void))
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen{
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen service->idle_die_callback = callback;
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen}
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainenstatic bool get_instance_config(const char *name, const char **config_path_r)
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen{
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen struct master_instance_list *list;
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen const struct master_instance *inst;
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen const char *path;
db87d16551d1081ada01f787ea21aa3ed1402c31Timo Sirainen
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen list = master_instance_list_init(MASTER_INSTANCE_PATH);
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen inst = master_instance_list_find_by_name(list, name);
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen if (inst != NULL) {
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen path = t_strdup_printf("%s/dovecot.conf", inst->base_dir);
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen if (t_readlink(path, config_path_r) < 0)
439942f89a77180719644e7af3752a8329259eb9Timo Sirainen i_fatal("readlink(%s) failed: %m", path);
439942f89a77180719644e7af3752a8329259eb9Timo Sirainen }
439942f89a77180719644e7af3752a8329259eb9Timo Sirainen master_instance_list_deinit(&list);
439942f89a77180719644e7af3752a8329259eb9Timo Sirainen return inst != NULL;
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen}
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen
46c31f64b9f0949f00b7819f45b22f2d64b2ea27Timo Sirainenbool master_service_parse_option(struct master_service *service,
46c31f64b9f0949f00b7819f45b22f2d64b2ea27Timo Sirainen int opt, const char *arg)
ef4d0eafab4d26bba047551db1e23ceff8aa9404Timo Sirainen{
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen const char *path;
2b8ff102f5117f917248f98ccbdc8e6a6af83c87Aki Tuomi
26e5bdf37d7d0deed1e2e8483366c83631b9d251Aki Tuomi switch (opt) {
26e5bdf37d7d0deed1e2e8483366c83631b9d251Aki Tuomi case 'c':
a102d189881a35d72ac3106a9e7e00577ae69310Aki Tuomi service->config_path = i_strdup(arg);
2b8ff102f5117f917248f98ccbdc8e6a6af83c87Aki Tuomi service->config_path_is_default = FALSE;
2b8ff102f5117f917248f98ccbdc8e6a6af83c87Aki Tuomi break;
26e5bdf37d7d0deed1e2e8483366c83631b9d251Aki Tuomi case 'i':
2b8ff102f5117f917248f98ccbdc8e6a6af83c87Aki Tuomi if (!get_instance_config(arg, &path))
26e5bdf37d7d0deed1e2e8483366c83631b9d251Aki Tuomi i_fatal("Unknown instance name: %s", arg);
26e5bdf37d7d0deed1e2e8483366c83631b9d251Aki Tuomi service->config_path = i_strdup(path);
2b8ff102f5117f917248f98ccbdc8e6a6af83c87Aki Tuomi service->keep_environment = TRUE;
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen break;
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen case 'k':
service->keep_environment = TRUE;
break;
case 'o':
if (!array_is_created(&service->config_overrides))
i_array_init(&service->config_overrides, 16);
array_append(&service->config_overrides, &arg, 1);
break;
case 'O':
service->flags |= MASTER_SERVICE_FLAG_NO_CONFIG_SETTINGS;
break;
case 'L':
service->log_directly = TRUE;
break;
default:
return FALSE;
}
return TRUE;
}
static void master_service_error(struct master_service *service)
{
master_service_stop_new_connections(service);
if (service->master_status.available_count ==
service->total_available_count || service->die_with_master) {
if (service->die_callback == NULL)
master_service_stop(service);
else {
service->to_die =
timeout_add(MASTER_SERVICE_DIE_TIMEOUT_MSECS,
master_service_stop,
service);
service->die_callback();
}
}
}
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);
master_service_error(service);
}
void master_service_init_finish(struct master_service *service)
{
enum libsig_flags sigint_flags = LIBSIG_FLAG_DELAYED;
struct stat st;
/* set default signal handlers */
lib_signals_init();
if ((service->flags & MASTER_SERVICE_FLAG_STANDALONE) == 0)
sigint_flags |= LIBSIG_FLAG_RESTART;
lib_signals_set_handler(SIGINT, sigint_flags, sig_die, service);
lib_signals_set_handler(SIGTERM, LIBSIG_FLAG_DELAYED, sig_die, service);
if ((service->flags & MASTER_SERVICE_FLAG_TRACK_LOGIN_STATE) != 0) {
lib_signals_set_handler(SIGUSR1, LIBSIG_FLAGS_SAFE,
sig_state_changed, 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");
/* start listening errors for status fd, it means master died */
service->io_status_error = io_add(MASTER_DEAD_FD, IO_ERROR,
master_status_error, service);
}
master_service_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(void)
{
const char *value = getenv(DOVECOT_PRESERVE_ENVS_ENV);
if (value == NULL || *value == '\0')
env_clean();
else T_BEGIN {
value = t_strconcat(value, " "DOVECOT_PRESERVE_ENVS_ENV, NULL);
env_clean_except(t_strsplit_spaces(value, " "));
} T_END;
}
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;
}
unsigned int master_service_get_process_limit(struct master_service *service)
{
return service->process_limit;
}
unsigned int master_service_get_idle_kill_secs(struct master_service *service)
{
return service->idle_kill_secs;
}
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_stop_new_connections(struct master_service *service)
{
unsigned int current_count;
if (service->stopping)
return;
service->stopping = TRUE;
master_service_io_listeners_remove(service);
master_service_io_listeners_close(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);
}
if (service->login != NULL)
master_login_stop(service->login);
}
bool master_service_is_killed(struct master_service *service)
{
return service->killed;
}
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_created(struct master_service *service)
{
i_assert(service->master_status.available_count > 0);
service->master_status.available_count--;
master_status_update(service);
}
void master_service_client_connection_accept(struct master_service_connection *conn)
{
conn->accepted = TRUE;
}
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);
i_assert(service->service_count_left > 0);
if (service->service_count_left == service->total_available_count) {
service->total_available_count--;
service->service_count_left--;
} else {
if (service->service_count_left != (unsigned int)-1)
service->service_count_left--;
i_assert(service->master_status.available_count <
service->total_available_count);
service->master_status.available_count++;
}
if (service->service_count_left == 0) {
i_assert(service->master_status.available_count ==
service->total_available_count);
master_service_stop(service);
} else 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);
} else {
master_status_update(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->to_status != NULL)
timeout_remove(&service->to_status);
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);
if (service->listener_names != NULL)
p_strsplit_free(default_pool, service->listener_names);
i_free(service->listeners);
i_free(service->getopt_str);
i_free(service->name);
i_free(service->config_path);
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) {
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) {
struct stat st;
int orig_errno = errno;
if (conn.fd == -1)
return;
if (errno == ENOTSOCK) {
/* it's not a socket. should be a fifo. */
} else if (errno == EINVAL &&
(fstat(l->fd, &st) == 0 && S_ISFIFO(st.st_mode))) {
/* BSDI fails accept(fifo) with EINVAL. */
} else {
errno = orig_errno;
i_error("net_accept() failed: %m");
/* try again later after one of the existing
connections has died */
master_service_io_listeners_remove(service);
return;
}
/* 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;
conn.name = l->name;
net_set_nonblock(conn.fd, TRUE);
master_service_client_connection_created(service);
service->callback(&conn);
if (!conn.accepted) {
if (close(conn.fd) < 0)
i_error("close(service connection) failed: %m");
master_service_client_connection_destroyed(service);
}
if (conn.fifo) {
/* reading FIFOs stays open forever, don't count them
as real clients */
master_service_client_connection_destroyed(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;
l->name = i < service->listener_names_count ?
service->listener_names[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 void master_service_io_listeners_close(struct master_service *service)
{
unsigned int i;
if (service->listeners != NULL) {
/* close via listeners. some fds might be pipes that are
currently handled as clients. we don't want to close them. */
for (i = 0; i < service->socket_count; i++) {
if (service->listeners[i].fd != -1) {
if (close(service->listeners[i].fd) < 0) {
i_error("close(listener %d) failed: %m",
service->listeners[i].fd);
}
}
}
} else {
for (i = 0; i < service->socket_count; i++) {
int fd = MASTER_LISTEN_FD_FIRST + i;
if (close(fd) < 0)
i_error("close(listener %d) failed: %m", fd);
}
}
}
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;
bool important_update;
if ((service->flags & MASTER_SERVICE_FLAG_UPDATE_PROCTITLE) != 0 &&
service->set != NULL && service->set->verbose_proctitle) T_BEGIN {
unsigned int used_count = service->total_available_count -
service->master_status.available_count;
process_title_set(t_strdup_printf("[%u connections]",
used_count));
} T_END;
important_update = master_status_update_is_important(service);
if (service->master_status.pid == 0 ||
service->master_status.available_count ==
service->last_sent_status_avail_count) {
/* a) closed, b) updating to same state */
if (service->to_status != NULL)
timeout_remove(&service->to_status);
if (service->io_status_write != NULL)
io_remove(&service->io_status_write);
return;
}
if (ioloop_time == service->last_sent_status_time &&
!important_update) {
/* don't spam master */
if (service->to_status != NULL)
timeout_reset(service->to_status);
else {
service->to_status =
timeout_add(1000, master_status_update,
service);
}
if (service->io_status_write != NULL)
io_remove(&service->io_status_write);
return;
}
if (service->to_status != NULL)
timeout_remove(&service->to_status);
ret = write(MASTER_STATUS_FD, &service->master_status,
sizeof(service->master_status));
if (ret == sizeof(service->master_status)) {
/* success */
if (service->io_status_write != NULL) {
/* delayed important update sent successfully */
io_remove(&service->io_status_write);
}
service->last_sent_status_time = ioloop_time;
service->last_sent_status_avail_count =
service->master_status.available_count;
service->initial_status_sent = TRUE;
} else if (ret >= 0) {
/* shouldn't happen? */
i_error("write(master_status_fd) returned %d", (int)ret);
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 (important_update) {
/* 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);
}
}
}
bool version_string_verify(const char *line, const char *service_name,
unsigned major_version)
{
unsigned int service_name_len = strlen(service_name);
bool ret;
if (strncmp(line, "VERSION\t", 8) != 0)
return FALSE;
line += 8;
if (strncmp(line, service_name, service_name_len) != 0 ||
line[service_name_len] != '\t')
return FALSE;
line += service_name_len + 1;
T_BEGIN {
ret = str_uint_equals(t_strcut(line, '\t'), major_version);
} T_END;
return ret;
}