master-service.c revision 8d7eb4104707c60ca7e9d0228b37c5133476907b
5f5870385cff47efd2f58e7892f251cf13761528Timo Sirainen/* Copyright (C) 2005-2009 Timo Sirainen */
a24f6b02ed8d0dde933a715be1c86f01977bf610Timo Sirainen#define DEFAULT_CONFIG_FILE_PATH SYSCONFDIR"/dovecot.conf"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen/* getenv(MASTER_CONFIG_FILE_ENV) provides path to configuration file/socket */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen/* getenv(MASTER_DOVECOT_VERSION_ENV) provides master's version number */
bbce20cb4e5739e9a06058cf8ee1f38a7f6884f6Timo Sirainen#define MASTER_DOVECOT_VERSION_ENV "DOVECOT_VERSION"
bbce20cb4e5739e9a06058cf8ee1f38a7f6884f6Timo Sirainenstatic void io_listeners_add(struct master_service *service);
bbce20cb4e5739e9a06058cf8ee1f38a7f6884f6Timo Sirainenstatic void io_listeners_remove(struct master_service *service);
bbce20cb4e5739e9a06058cf8ee1f38a7f6884f6Timo Sirainenstatic void master_status_update(struct master_service *service);
bbce20cb4e5739e9a06058cf8ee1f38a7f6884f6Timo Sirainen return "c:ko:Os:L";
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainenstatic void sig_die(const siginfo_t *si, void *context)
93b29720c5141f787bd1861796867e4595c9d084Timo Sirainen /* warn about being killed because of some signal, except SIGINT (^C)
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen which is too common at least while testing :) */
93b29720c5141f787bd1861796867e4595c9d084Timo Sirainen i_warning("Killed with signal %d (by pid=%s uid=%s code=%s)",
93b29720c5141f787bd1861796867e4595c9d084Timo Sirainen lib_signal_code_to_str(si->si_signo, si->si_code));
a24f6b02ed8d0dde933a715be1c86f01977bf610Timo Sirainenstatic void master_service_verify_version(struct master_service *service)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen strcmp(service->version_string, PACKAGE_VERSION) != 0) {
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen "(if you don't care, set version_ignore=yes)",
1cf72a848805fcf014b01c9d3665b6a157846a21Timo Sirainenmaster_service_init(const char *name, enum master_service_flags flags,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const char *str;
a050ca9def13949dbaa67bd6574a41c4f397ae26Timo Sirainen (flags & MASTER_SERVICE_FLAG_STANDALONE) == 0) {
993e6c2caaae971dd3c34913a42d854e3b623261Timo Sirainen fd_debug_verify_leaks(MASTER_LISTEN_FD_FIRST + count, 1024);
993e6c2caaae971dd3c34913a42d854e3b623261Timo Sirainen /* NOTE: we start rooted, so keep the code minimal until
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen restrict_access_by_env() is called */
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen /* Set a logging prefix temporarily. This will be ignored once the log
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen is properly initialized */
993e6c2caaae971dd3c34913a42d854e3b623261Timo Sirainen i_set_failure_prefix(t_strdup_printf("%s(init): ", name));
9df8c9225140d9d1df5ddf4c6c9da61662ae6c44Timo Sirainen service->service_count_left = (unsigned int)-1;
9df8c9225140d9d1df5ddf4c6c9da61662ae6c44Timo Sirainen service->config_path = getenv(MASTER_CONFIG_FILE_ENV);
9df8c9225140d9d1df5ddf4c6c9da61662ae6c44Timo Sirainen service->config_path = DEFAULT_CONFIG_FILE_PATH;
9df8c9225140d9d1df5ddf4c6c9da61662ae6c44Timo Sirainen if ((flags & MASTER_SERVICE_FLAG_STANDALONE) == 0) {
40ef82c46f6652412b068ebcdac7c3e74840a284Timo Sirainen service->version_string = getenv(MASTER_DOVECOT_VERSION_ENV);
993e6c2caaae971dd3c34913a42d854e3b623261Timo Sirainen /* set up some kind of logging until we know exactly how and where
a4d209d480d453566d331e870b8d0c99af7716c8Timo Sirainen we want to log */
993e6c2caaae971dd3c34913a42d854e3b623261Timo Sirainen i_set_failure_prefix(t_strdup_printf("%s(%s): ",
a4d209d480d453566d331e870b8d0c99af7716c8Timo Sirainen i_set_failure_prefix(t_strdup_printf("%s: ", name));
f537e7efaec891d6b3320ca94331d09ca8c4a4dbTimo Sirainenvoid master_service_init_log(struct master_service *service, const char *prefix,
fc7b17677ac1a5fa3f7fe13d5ef7dcfea8d9b4a1Timo Sirainen if ((service->flags & MASTER_SERVICE_FLAG_STANDALONE) != 0 &&
fc7b17677ac1a5fa3f7fe13d5ef7dcfea8d9b4a1Timo Sirainen (service->flags & MASTER_SERVICE_FLAG_DONT_LOG_TO_STDERR) == 0) {
fc7b17677ac1a5fa3f7fe13d5ef7dcfea8d9b4a1Timo Sirainen if (getenv("LOG_SERVICE") != NULL && !service->log_directly) {
fc7b17677ac1a5fa3f7fe13d5ef7dcfea8d9b4a1Timo Sirainen /* logging via log service */
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen /* log to syslog */
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen if (!syslog_facility_find(service->set->syslog_facility,
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen i_set_failure_syslog("dovecot", LOG_NDELAY, facility);
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen /* log to file or stderr */
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen path = home_expand(service->set->info_log_path);
e86d0d34fe365da4c7ca4312d575bfcbf3a01c0eTimo Sirainen i_set_failure_timestamp_format(service->set->log_timestamp);
1cf72a848805fcf014b01c9d3665b6a157846a21Timo Sirainenbool master_service_parse_option(struct master_service *service,
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen if (!array_is_created(&service->config_overrides))
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen array_append(&service->config_overrides, &arg, 1);
1cf72a848805fcf014b01c9d3665b6a157846a21Timo Sirainenstatic void master_service_error(struct master_service *service)
1cf72a848805fcf014b01c9d3665b6a157846a21Timo Sirainen /* status fd is a write-only pipe, so if we're here it means the
f2786c07cbd4a7a0a6a46c3e06dc4545aaf2f278Timo Sirainen master wants us to die (or died itself). don't die until all
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen service connections are finished. */
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen /* the log fd may also be closed already, don't die when trying to
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainenvoid master_service_init_finish(struct master_service *service)
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen unsigned int count;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_assert(service->total_available_count == 0);
7471f28b16b81f9af413c879b3efb16eeafd2bd9Timo Sirainen i_assert(service->service_count_left == (unsigned int)-1);
7471f28b16b81f9af413c879b3efb16eeafd2bd9Timo Sirainen /* set default signal handlers */
7471f28b16b81f9af413c879b3efb16eeafd2bd9Timo Sirainen lib_signals_set_handler(SIGINT, TRUE, sig_die, service);
7471f28b16b81f9af413c879b3efb16eeafd2bd9Timo Sirainen lib_signals_set_handler(SIGTERM, TRUE, sig_die, service);
7471f28b16b81f9af413c879b3efb16eeafd2bd9Timo Sirainen if ((service->flags & MASTER_SERVICE_FLAG_STANDALONE) == 0) {
1cf72a848805fcf014b01c9d3665b6a157846a21Timo Sirainen if (fstat(MASTER_STATUS_FD, &st) < 0 || !S_ISFIFO(st.st_mode))
1cf72a848805fcf014b01c9d3665b6a157846a21Timo Sirainen i_fatal("Must be started by dovecot master process");
1cf72a848805fcf014b01c9d3665b6a157846a21Timo Sirainen /* initialize master_status structure */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* set the default limit */
f2786c07cbd4a7a0a6a46c3e06dc4545aaf2f278Timo Sirainen master_service_set_client_limit(service, count);
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen /* start listening errors for status fd, it means master died */
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen service->io_status_error = io_add(MASTER_STATUS_FD, IO_ERROR,
6d3bb841d4ec2c12ac2d4ecf6146ef8d8a7dd731Timo Sirainen if ((service->flags & MASTER_SERVICE_FLAG_STD_CLIENT) != 0) {
3ea86ed7cf06ba04e4aa6cd1c4df9be336c06cd3Timo Sirainen /* we already have a connection to be served */
6d3bb841d4ec2c12ac2d4ecf6146ef8d8a7dd731Timo Sirainenvoid master_service_env_clean(bool preserve_home)
6d3bb841d4ec2c12ac2d4ecf6146ef8d8a7dd731Timo Sirainen /* Note that if the original environment was set with env_put(), the
6d3bb841d4ec2c12ac2d4ecf6146ef8d8a7dd731Timo Sirainen environment strings will be invalid after env_clean(). That's why
6d3bb841d4ec2c12ac2d4ecf6146ef8d8a7dd731Timo Sirainen we t_strconcat() them above. */
6d3bb841d4ec2c12ac2d4ecf6146ef8d8a7dd731Timo Sirainenvoid master_service_set_client_limit(struct master_service *service,
6d3bb841d4ec2c12ac2d4ecf6146ef8d8a7dd731Timo Sirainen i_assert(service->master_status.available_count ==
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen service->total_available_count = client_limit;
c680a6b35b459045e92814778908da5a93922107Timo Sirainen service->master_status.available_count = client_limit;
0fa842717a8b163252e55c229c37ca0c5d7ff056Timo Sirainenunsigned int master_service_get_client_limit(struct master_service *service)
096953143c4032bad154637f687551856f7946cbTimo Sirainenvoid master_service_set_service_count(struct master_service *service,
096953143c4032bad154637f687551856f7946cbTimo Sirainen unsigned int count)
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen unsigned int used;
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen service->master_status.available_count = count - used;
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainenunsigned int master_service_get_service_count(struct master_service *service)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenunsigned int master_service_get_socket_count(struct master_service *service)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenconst char *master_service_get_config_path(struct master_service *service)
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainenconst char *master_service_get_version_string(struct master_service *service)
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainenconst char *master_service_get_name(struct master_service *service)
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainenvoid master_service_run(struct master_service *service,
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen master_service_connection_callback_t *callback)
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainenvoid master_service_stop(struct master_service *service)
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainenvoid master_service_anvil_send(struct master_service *service, const char *cmd)
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen if ((service->flags & MASTER_SERVICE_FLAG_STANDALONE) != 0)
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen ret = write(MASTER_ANVIL_FD, cmd, strlen(cmd));
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen else if (ret == 0)
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainenvoid master_service_client_connection_destroyed(struct master_service *service)
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen /* we can listen again */
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen if (service->service_count_left != service->total_available_count) {
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen i_assert(service->service_count_left == (unsigned int)-1);
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen /* we have only limited amount of service requests left */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_assert(service->master_status.available_count ==
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen if ((service->io_status_error == NULL || service->listeners == NULL) &&
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* we've finished handling all clients, and
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen a) master has closed the connection
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen b) there are no listeners (std-client?) */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenvoid master_service_deinit(struct master_service **_service)
7f7533a29d0e65876acc0ab9dd56b3d7840c3ee7Timo Sirainen if (array_is_created(&service->config_overrides))
7f7533a29d0e65876acc0ab9dd56b3d7840c3ee7Timo Sirainenstatic void master_service_listen(struct master_service_listener *l)
7f7533a29d0e65876acc0ab9dd56b3d7840c3ee7Timo Sirainen if (l->service->master_status.available_count == 0) {
7f7533a29d0e65876acc0ab9dd56b3d7840c3ee7Timo Sirainen /* we are full. stop listening for now. */
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen conn.fd = net_accept(l->fd, &conn.remote_ip, &conn.remote_port);
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen /* it's not a socket. probably a fifo. use the "listener"
b5ac20e30146562322ceb7939f044d52d1e51184Timo Sirainen as the connection fd and stop the listener. */
a5b331e18b220fac557480b569b85215a1b3bd8eTimo Sirainenstatic void io_listeners_init(struct master_service *service)
a5b331e18b220fac557480b569b85215a1b3bd8eTimo Sirainen unsigned int i;
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen i_new(struct master_service_listener, service->socket_count);
a5b331e18b220fac557480b569b85215a1b3bd8eTimo Sirainen struct master_service_listener *l = &service->listeners[i];
a5b331e18b220fac557480b569b85215a1b3bd8eTimo Sirainen if (i >= service->socket_count - service->ssl_socket_count)
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainenstatic void io_listeners_add(struct master_service *service)
6f0bb992c9f55de912295493892651d57e5e9827Timo Sirainen unsigned int i;
0c909e3461607eadcd66f4eac69b7f34e37fccf1Timo Sirainen struct master_service_listener *l = &service->listeners[i];
6f0bb992c9f55de912295493892651d57e5e9827Timo Sirainen l->io = io_add(MASTER_LISTEN_FD_FIRST + i, IO_READ,
6f0bb992c9f55de912295493892651d57e5e9827Timo Sirainenstatic void io_listeners_remove(struct master_service *service)
0c909e3461607eadcd66f4eac69b7f34e37fccf1Timo Sirainen unsigned int i;
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainenstatic bool master_status_update_is_important(struct master_service *service)
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen if (service->master_status.available_count == 0)
d200ae87140a1985fe527e6527bc4bd3035189b1Timo Sirainenstatic void master_status_update(struct master_service *service)
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen return; /* closed */
8e361d2906b0e44f7175a20981f8d2280645b58bTimo Sirainen ret = write(MASTER_STATUS_FD, &service->master_status,
8e361d2906b0e44f7175a20981f8d2280645b58bTimo Sirainen /* success */
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen /* delayed important update sent successfully */
8e361d2906b0e44f7175a20981f8d2280645b58bTimo Sirainen } else if (ret == 0) {
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen /* shouldn't happen? */
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen i_error("write(master_status_fd) returned 0");
cf05507f63b12bd1ee4efffc2f316ebcd9fd7089Timo Sirainen /* failure */
cf05507f63b12bd1ee4efffc2f316ebcd9fd7089Timo Sirainen i_error("write(master_status_fd) failed: %m");
cf05507f63b12bd1ee4efffc2f316ebcd9fd7089Timo Sirainen } else if (master_status_update_is_important(service)) {
cf05507f63b12bd1ee4efffc2f316ebcd9fd7089Timo Sirainen /* reader is busy, but it's important to get this notification
cf05507f63b12bd1ee4efffc2f316ebcd9fd7089Timo Sirainen through. send it when possible. */