master-service.c revision 56ba5a9b62e3ce527e898a8fe3b1a015ab30ed54
e59faf65ce864fe95dc00f5d52b8323cdbd0608aTimo Sirainen/* Copyright (c) 2005-2010 Dovecot authors, see the included COPYING file */
f0a2d04321ba456e5c5ba821c0d1ed9e8e0e2e08Timo Sirainen#define DEFAULT_CONFIG_FILE_PATH SYSCONFDIR"/dovecot.conf"
f0a2d04321ba456e5c5ba821c0d1ed9e8e0e2e08Timo Sirainen/* getenv(MASTER_CONFIG_FILE_ENV) provides path to configuration file/socket */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen/* getenv(MASTER_DOVECOT_VERSION_ENV) provides master's version number */
f0a2d04321ba456e5c5ba821c0d1ed9e8e0e2e08Timo Sirainen#define MASTER_DOVECOT_VERSION_ENV "DOVECOT_VERSION"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen/* when we're full of connections, how often to check if login state has
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen changed. we normally notice it immediately because of a signal, so this is
98922c5675bbbfadc84d58768bef867fe82256c2Timo Sirainen just a fallback against race conditions. */
98922c5675bbbfadc84d58768bef867fe82256c2Timo Sirainen/* If die callback hasn't managed to stop the service for this many seconds,
98922c5675bbbfadc84d58768bef867fe82256c2Timo Sirainen#define MASTER_SERVICE_DIE_TIMEOUT_MSECS (30*1000)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic void master_service_io_listeners_close(struct master_service *service);
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainenstatic void master_service_refresh_login_state(struct master_service *service);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return "c:ko:Os:L";
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic void sig_die(const siginfo_t *si, void *context)
98922c5675bbbfadc84d58768bef867fe82256c2Timo Sirainen /* SIGINT comes either from master process or from keyboard. we don't
98922c5675bbbfadc84d58768bef867fe82256c2Timo Sirainen want to log it in either case.*/
98922c5675bbbfadc84d58768bef867fe82256c2Timo Sirainen i_warning("Killed with signal %d (by pid=%s uid=%s code=%s)",
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen lib_signal_code_to_str(si->si_signo, si->si_code));
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen } else if ((service->flags & MASTER_SERVICE_FLAG_NO_IDLE_DIE) != 0) {
b6a7e0a7899e7f5d60c23cdaa50e025e4c67d05fTimo Sirainen /* never die when idling */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen } else if ((service->flags & MASTER_SERVICE_FLAG_STANDALONE) == 0) {
b0a901f1dbe9e05ac1c92a0974af6bce0274f31aTimo Sirainen /* SIGINT came from master. die only if we're not handling
d42eb03b3a4e79a2da22a1be2de59b95660af2beTimo Sirainen any clients currently. */
f0a2d04321ba456e5c5ba821c0d1ed9e8e0e2e08Timo Sirainensig_state_changed(const siginfo_t *si ATTR_UNUSED, void *context)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic void master_service_verify_version_string(struct master_service *service)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen strcmp(service->version_string, PACKAGE_VERSION) != 0) {
f0a2d04321ba456e5c5ba821c0d1ed9e8e0e2e08Timo Sirainen "(if you don't care, set version_ignore=yes)",
c60d1eda4df179d83d531647732d5e3e45064219Timo Sirainenmaster_service_init(const char *name, enum master_service_flags flags,
c60d1eda4df179d83d531647732d5e3e45064219Timo Sirainen int *argc, char **argv[], const char *getopt_str)
f0a2d04321ba456e5c5ba821c0d1ed9e8e0e2e08Timo Sirainen const char *str;
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen (flags & MASTER_SERVICE_FLAG_STANDALONE) == 0) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen fd_debug_verify_leaks(MASTER_LISTEN_FD_FIRST + count, 1024);
ebe6df72f1309135f02b6a4d2aef1e81a073f91cTimo Sirainen if ((flags & MASTER_SERVICE_FLAG_STANDALONE) == 0) {
b0a901f1dbe9e05ac1c92a0974af6bce0274f31aTimo Sirainen /* make sure we can dump core, at least until
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen privileges are dropped. (i'm not really sure why this
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen is needed, because doing the same just before exec
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen doesn't help, and exec shouldn't affect this with
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen non-setuid/gid binaries..) */
1eb17e61d3d38372674aa0c55caedb0185a985f5Timo Sirainen /* NOTE: we start rooted, so keep the code minimal until
ee1a3e217279dcd0f1cccbd3442e481b7dc90f05Timo Sirainen restrict_access_by_env() is called */
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen /* Set a logging prefix temporarily. This will be ignored once the log
ee1a3e217279dcd0f1cccbd3442e481b7dc90f05Timo Sirainen is properly initialized */
3eb63515855f386449c22233d1f1baf1ddfe8a2dTimo Sirainen i_set_failure_prefix(t_strdup_printf("%s(init): ", name));
e9d29ae46d435aee85514decfe6ee27399ebf794Timo Sirainen /* ignore these signals as early as possible */
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen /* keep getopt_str first in case it contains "+" */
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen i_strconcat(getopt_str, master_service_getopt_string(), NULL);
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen service->service_count_left = (unsigned int)-1;
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen service->config_path = getenv(MASTER_CONFIG_FILE_ENV);
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen service->config_path = DEFAULT_CONFIG_FILE_PATH;
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen if ((flags & MASTER_SERVICE_FLAG_STANDALONE) == 0) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen service->version_string = getenv(MASTER_DOVECOT_VERSION_ENV);
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen /* set up some kind of logging until we know exactly how and where
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen we want to log */
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen i_set_failure_prefix(t_strdup_printf("%s(%s): ",
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen i_set_failure_prefix(t_strdup_printf("%s: ", name));
3ec2c1f31631bb5ff86f5fc93a563c33e5cae90dTimo Sirainen master_service_verify_version_string(service);
9334fbad0aabb2fed88f40b2205d0d6f80bdffa2Timo Sirainenint master_getopt(struct master_service *service)
7ede6554e451ec039a67beec7d6ee4aff61d386eTimo Sirainen while ((c = getopt(service->argc, service->argv,
7ede6554e451ec039a67beec7d6ee4aff61d386eTimo Sirainen if (!master_service_parse_option(service, c, optarg))
9334fbad0aabb2fed88f40b2205d0d6f80bdffa2Timo Sirainenvoid master_service_init_log(struct master_service *service,
9334fbad0aabb2fed88f40b2205d0d6f80bdffa2Timo Sirainen if ((service->flags & MASTER_SERVICE_FLAG_STANDALONE) != 0 &&
9334fbad0aabb2fed88f40b2205d0d6f80bdffa2Timo Sirainen (service->flags & MASTER_SERVICE_FLAG_DONT_LOG_TO_STDERR) == 0) {
9334fbad0aabb2fed88f40b2205d0d6f80bdffa2Timo Sirainen if (getenv("LOG_SERVICE") != NULL && !service->log_directly) {
3ec2c1f31631bb5ff86f5fc93a563c33e5cae90dTimo Sirainen /* logging via log service */
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen if (strcmp(service->set->log_path, "syslog") != 0) {
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen /* error logging goes to file or stderr */
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen if (strcmp(service->set->log_path, "syslog") == 0 ||
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen strcmp(service->set->info_log_path, "syslog") == 0 ||
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen strcmp(service->set->debug_log_path, "syslog") == 0) {
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen /* something gets logged to syslog */
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen if (!syslog_facility_find(service->set->syslog_facility,
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen i_set_failure_syslog("dovecot", LOG_NDELAY, facility);
d22301419109ed4a38351715e6760011421dadecTimo Sirainen if (strcmp(service->set->log_path, "syslog") != 0) {
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen /* set error handlers back to file */
d22301419109ed4a38351715e6760011421dadecTimo Sirainen strcmp(service->set->info_log_path, "syslog") != 0) {
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen path = home_expand(service->set->info_log_path);
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen strcmp(service->set->debug_log_path, "syslog") != 0) {
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen path = home_expand(service->set->debug_log_path);
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen i_set_failure_timestamp_format(service->set->log_timestamp);
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainenvoid master_service_set_die_with_master(struct master_service *service,
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainenvoid master_service_set_die_callback(struct master_service *service,
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen void (*callback)(void))
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainenbool master_service_parse_option(struct master_service *service,
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen if (!array_is_created(&service->config_overrides))
b142deb9a831c89b1bb9129ada655f3e56b9d4ccTimo Sirainen array_append(&service->config_overrides, &arg, 1);
b142deb9a831c89b1bb9129ada655f3e56b9d4ccTimo Sirainen service->flags |= MASTER_SERVICE_FLAG_NO_CONFIG_SETTINGS;
3eb63515855f386449c22233d1f1baf1ddfe8a2dTimo Sirainenstatic void master_service_error(struct master_service *service)
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen service->total_available_count || service->die_with_master) {
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen /* status fd is a write-only pipe, so if we're here it means the
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen master wants us to die (or died itself). don't die until all
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen service connections are finished. */
7aeaf23f760d86aad525d831efcac9f860a55a39Timo Sirainen /* the log fd may also be closed already, don't die when trying to
63aaafe7e6b201d6633f8c25610ecd30c9cda99cTimo Sirainenvoid master_service_init_finish(struct master_service *service)
d22301419109ed4a38351715e6760011421dadecTimo Sirainen unsigned int count;
cf49fc07f541c0f74578ac6c3b334ddade143aa1Timo Sirainen i_assert(service->total_available_count == 0);
63aaafe7e6b201d6633f8c25610ecd30c9cda99cTimo Sirainen i_assert(service->service_count_left == (unsigned int)-1);
2ff23d6fb7e2ff85aa23b7f4769aeac1d0316a1bTimo Sirainen /* set default signal handlers */
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen lib_signals_set_handler(SIGINT, TRUE, sig_die, service);
7aeaf23f760d86aad525d831efcac9f860a55a39Timo Sirainen lib_signals_set_handler(SIGTERM, TRUE, sig_die, service);
7aeaf23f760d86aad525d831efcac9f860a55a39Timo Sirainen if ((service->flags & MASTER_SERVICE_FLAG_TRACK_LOGIN_STATE) != 0) {
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen if ((service->flags & MASTER_SERVICE_FLAG_STANDALONE) == 0) {
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen if (fstat(MASTER_STATUS_FD, &st) < 0 || !S_ISFIFO(st.st_mode))
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen i_fatal("Must be started by dovecot master process");
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen /* initialize master_status structure */
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen str_to_uint(value, &service->master_status.uid) < 0)
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen /* set the default limit */
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen if (value == NULL || str_to_uint(value, &count) < 0 ||
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen master_service_set_client_limit(service, count);
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen /* set the default service count */
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen if (value != NULL && str_to_uint(value, &count) == 0 &&
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen master_service_set_service_count(service, count);
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen /* start listening errors for status fd, it means master died */
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen service->io_status_error = io_add(MASTER_STATUS_FD, IO_ERROR,
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen if ((service->flags & MASTER_SERVICE_FLAG_STD_CLIENT) != 0) {
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen /* we already have a connection to be served */
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainenvoid master_service_env_clean(bool preserve_home)
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen /* Note that if the original environment was set with env_put(), the
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen environment strings will be invalid after env_clean(). That's why
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen we t_strconcat() them above. */
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainenvoid master_service_set_client_limit(struct master_service *service,
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen unsigned int used;
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen i_assert(service->master_status.available_count ==
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen service->total_available_count = client_limit;
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen service->master_status.available_count = client_limit - used;
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainenunsigned int master_service_get_client_limit(struct master_service *service)
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainenvoid master_service_set_service_count(struct master_service *service,
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen unsigned int count)
366d6311c9d5bac6613e3cd64619eb878adce9ecTimo Sirainen unsigned int used;
24cd47a2c8f7507e555459b7e841de771ba3c318Timo Sirainen service->master_status.available_count = count - used;
24cd47a2c8f7507e555459b7e841de771ba3c318Timo Sirainenunsigned int master_service_get_service_count(struct master_service *service)
e4e7475f646d66a257d682738fbff1f206ce4924Timo Sirainenunsigned int master_service_get_socket_count(struct master_service *service)
129cc29ae6d8ad64d6d2b72f18da18fa134d0f3eTimo Sirainenvoid master_service_set_avail_overflow_callback(struct master_service *service,
129cc29ae6d8ad64d6d2b72f18da18fa134d0f3eTimo Sirainen void (*callback)(void))
24cd47a2c8f7507e555459b7e841de771ba3c318Timo Sirainenconst char *master_service_get_config_path(struct master_service *service)
b142deb9a831c89b1bb9129ada655f3e56b9d4ccTimo Sirainenconst char *master_service_get_version_string(struct master_service *service)
8754bb7a1f24705ffa5434f9e10d57e0b3b88d6eTimo Sirainenconst char *master_service_get_name(struct master_service *service)
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainenvoid master_service_run(struct master_service *service,
b19a1420da0618a10edf67c2cfd13c8c8633057aTimo Sirainen master_service_connection_callback_t *callback)
89fb98e9eb7e95255a579c8e9d865383c2334a74Timo Sirainenvoid master_service_stop(struct master_service *service)
89fb98e9eb7e95255a579c8e9d865383c2334a74Timo Sirainenvoid master_service_stop_new_connections(struct master_service *service)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* make sure we stop after servicing current connections */
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen current_count = service->total_available_count -
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen service->total_available_count = current_count;
910fa4e4204a73d3d24c03f3059dd24e727ca057Timo Sirainen /* notify master that we're not accepting any more
910fa4e4204a73d3d24c03f3059dd24e727ca057Timo Sirainen connections */
225e82df5dd1e765f4e52b80c954558f00e5a7dfTimo Sirainenbool master_service_is_killed(struct master_service *service)
225e82df5dd1e765f4e52b80c954558f00e5a7dfTimo Sirainenvoid master_service_anvil_send(struct master_service *service, const char *cmd)
225e82df5dd1e765f4e52b80c954558f00e5a7dfTimo Sirainen if ((service->flags & MASTER_SERVICE_FLAG_STANDALONE) != 0)
225e82df5dd1e765f4e52b80c954558f00e5a7dfTimo Sirainen ret = write(MASTER_ANVIL_FD, cmd, strlen(cmd));
225e82df5dd1e765f4e52b80c954558f00e5a7dfTimo Sirainen /* anvil process was probably recreated, don't bother
225e82df5dd1e765f4e52b80c954558f00e5a7dfTimo Sirainen logging an error about losing connection to it */
d22301419109ed4a38351715e6760011421dadecTimo Sirainen } else if (ret == 0)
8c9e48cd6de80da0fa32b9c0dee003472c9a7c0dTimo Sirainenvoid master_service_client_connection_accept(struct master_service_connection *conn)
7ede6554e451ec039a67beec7d6ee4aff61d386eTimo Sirainenvoid master_service_client_connection_destroyed(struct master_service *service)
8c9e48cd6de80da0fa32b9c0dee003472c9a7c0dTimo Sirainen /* we can listen again */
d22301419109ed4a38351715e6760011421dadecTimo Sirainen if (service->service_count_left == service->total_available_count) {
8c9e48cd6de80da0fa32b9c0dee003472c9a7c0dTimo Sirainen if (service->service_count_left != (unsigned int)-1)
8c9e48cd6de80da0fa32b9c0dee003472c9a7c0dTimo Sirainen i_assert(service->master_status.available_count <
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen i_assert(service->master_status.available_count ==
aa247243412a49f9bdebf7255e131dc6ece4ed46Timo Sirainen } else if ((service->io_status_error == NULL ||
a757f31393b9d6fc7760a9dec8363404ab3ae576Timo Sirainen /* we've finished handling all clients, and
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen a) master has closed the connection
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen b) there are no listeners (std-client?) */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic void master_service_set_login_state(struct master_service *service,
838e367716bbd5e44b4a1691db9cbf72af53e9f0Timo Sirainen if (service->master_status.available_count > 0)
service);
case MASTER_LOGIN_STATE_FULL:
int ret;
if (ret < 0)
lib_deinit();
return TRUE;
return TRUE;
return FALSE;
bool important_update;
!important_update) {
service);
} else if (ret >= 0) {
} else if (important_update) {
unsigned major_version)
bool ret;
return FALSE;
return FALSE;
T_BEGIN {
} T_END;
return ret;