master-service.c revision 885e1b36da370a674c0fd3b85db53740d7dcbd9b
5a580c3a38ced62d4bcc95b8ac7c4f2935b5d294Timo Sirainen/* Copyright (C) 2005-2009 Timo Sirainen */
afd0d073a0afd1c8cf6473b4ae76919586eaa1faTimo Sirainen#define DEFAULT_CONFIG_FILE_PATH SYSCONFDIR"/dovecot.conf"
afd0d073a0afd1c8cf6473b4ae76919586eaa1faTimo Sirainen/* getenv(MASTER_CONFIG_FILE_ENV) provides path to configuration file/socket */
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen/* getenv(MASTER_DOVECOT_VERSION_ENV) provides master's version number */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#define MASTER_DOVECOT_VERSION_ENV "DOVECOT_VERSION"
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainenstatic void io_listeners_add(struct master_service *service);
7d7b5c98f086ffa8ac9c90f21db17748ca607202Timo Sirainenstatic void io_listeners_remove(struct master_service *service);
7d7b5c98f086ffa8ac9c90f21db17748ca607202Timo Sirainenstatic void master_status_update(struct master_service *service);
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen return "c:ko:Os:L";
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainenstatic void sig_die(const siginfo_t *si, void *context)
0d70a702dec63d22535684fec6a7247c5f153208Timo Sirainen /* warn about being killed because of some signal, except SIGINT (^C)
0d70a702dec63d22535684fec6a7247c5f153208Timo Sirainen which is too common at least while testing :) */
ff7257145f317d6ca44a9402427bb74c34b999a9Timo Sirainen i_warning("Killed with signal %d (by pid=%s uid=%s code=%s)",
1727610dbc69920b7f0d0622b4e5d7127c59093dTimo Sirainen lib_signal_code_to_str(si->si_signo, si->si_code));
1b3bb8d39686ed24730cbc31cc9a33dc62c8c6c3Timo Sirainenstatic void master_service_verify_version(struct master_service *service)
1b3bb8d39686ed24730cbc31cc9a33dc62c8c6c3Timo Sirainen strcmp(service->version_string, PACKAGE_VERSION) != 0) {
1b3bb8d39686ed24730cbc31cc9a33dc62c8c6c3Timo Sirainen "(if you don't care, set version_ignore=yes)",
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenmaster_service_init(const char *name, enum master_service_flags flags,
4c8b1c4aa0582c6ca43a4d1cbd210741e7fff952Timo Sirainen const char *str;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen (flags & MASTER_SERVICE_FLAG_STANDALONE) == 0) {
3b80595fcf2001cf7b2fcc6290823e38f4a142fcTimo Sirainen fd_debug_verify_leaks(MASTER_LISTEN_FD_FIRST + count, 1024);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* NOTE: we start rooted, so keep the code minimal until
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen restrict_access_by_env() is called */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* Set a logging prefix temporarily. This will be ignored once the log
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen is properly initialized */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_set_failure_prefix(t_strdup_printf("%s(init): ", name));
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen service->service_count_left = (unsigned int)-1;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen service->config_path = getenv(MASTER_CONFIG_FILE_ENV);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen service->config_path = DEFAULT_CONFIG_FILE_PATH;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if ((flags & MASTER_SERVICE_FLAG_STANDALONE) == 0) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen service->version_string = getenv(MASTER_DOVECOT_VERSION_ENV);
4c8b1c4aa0582c6ca43a4d1cbd210741e7fff952Timo Sirainen /* set up some kind of logging until we know exactly how and where
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen we want to log */
91dca97b367c54a139c268b56a0c67f564bd9197Timo Sirainen i_set_failure_prefix(t_strdup_printf("%s(%s): ",
8afec4d1a32b78f540257a27769b372aad753384Timo Sirainen i_set_failure_prefix(t_strdup_printf("%s: ", name));
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainenvoid master_service_init_log(struct master_service *service, const char *prefix,
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen if ((service->flags & MASTER_SERVICE_FLAG_STANDALONE) != 0 &&
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen (service->flags & MASTER_SERVICE_FLAG_DONT_LOG_TO_STDERR) == 0) {
4c8b1c4aa0582c6ca43a4d1cbd210741e7fff952Timo Sirainen if (getenv("LOG_SERVICE") != NULL && !service->log_directly) {
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen /* logging via log service */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* log to syslog */
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen if (!syslog_facility_find(service->set->syslog_facility,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_set_failure_syslog("dovecot", LOG_NDELAY, facility);
df4018ae2f0a95be602f724ca70df7e0e3bd6a7dTimo Sirainen /* log to file or stderr */
3b80595fcf2001cf7b2fcc6290823e38f4a142fcTimo Sirainen path = home_expand(service->set->info_log_path);
3b80595fcf2001cf7b2fcc6290823e38f4a142fcTimo Sirainen i_set_failure_timestamp_format(service->set->log_timestamp);
3b80595fcf2001cf7b2fcc6290823e38f4a142fcTimo Sirainenbool master_service_parse_option(struct master_service *service,
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen if (!array_is_created(&service->config_overrides))
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen array_append(&service->config_overrides, &arg, 1);
024815ea2ffdda9ea79919f18e865663977f73eaTimo Sirainenstatic void master_service_error(struct master_service *service)
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen /* status fd is a write-only pipe, so if we're here it means the
e49e973f363a56ad186fce372310d5ec6d83d8faTimo Sirainen master wants us to die (or died itself). don't die until all
e49e973f363a56ad186fce372310d5ec6d83d8faTimo Sirainen service connections are finished. */
8afec4d1a32b78f540257a27769b372aad753384Timo Sirainen /* the log fd may also be closed already, don't die when trying to
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenvoid master_service_init_finish(struct master_service *service)
df4018ae2f0a95be602f724ca70df7e0e3bd6a7dTimo Sirainen unsigned int count;
7e94cf9d70ce9fdeccb7a85ff400b899e6386f36Timo Sirainen i_assert(service->total_available_count == 0);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_assert(service->service_count_left == (unsigned int)-1);
3da614c39dd29f536c485089e67839b4cf89fed3Timo Sirainen /* set default signal handlers */
c24c0f0a208e5ffc35dc8be19a9b504a5326467aTimo Sirainen lib_signals_set_handler(SIGINT, TRUE, sig_die, service);
c24c0f0a208e5ffc35dc8be19a9b504a5326467aTimo Sirainen lib_signals_set_handler(SIGTERM, TRUE, sig_die, service);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if ((service->flags & MASTER_SERVICE_FLAG_STANDALONE) == 0) {
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen if (fstat(MASTER_STATUS_FD, &st) < 0 || !S_ISFIFO(st.st_mode))
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen i_fatal("Must be started by dovecot master process");
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* initialize master_status structure */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* set the default limit */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen master_service_set_client_limit(service, count);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* set the default service count */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen master_service_set_service_count(service, count);
f7992ce3ff735b8eb2f59b07f1d565dafcc0452eTimo Sirainen /* start listening errors for status fd, it means master died */
7e94cf9d70ce9fdeccb7a85ff400b899e6386f36Timo Sirainen service->io_status_error = io_add(MASTER_STATUS_FD, IO_ERROR,
3fe9483b2b412a14493e3120751b0e99ecfe9388Timo Sirainen if ((service->flags & MASTER_SERVICE_FLAG_STD_CLIENT) != 0) {
3fe9483b2b412a14493e3120751b0e99ecfe9388Timo Sirainen /* we already have a connection to be served */
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainenvoid master_service_env_clean(bool preserve_home)
3fe9483b2b412a14493e3120751b0e99ecfe9388Timo Sirainen /* Note that if the original environment was set with env_put(), the
3fe9483b2b412a14493e3120751b0e99ecfe9388Timo Sirainen environment strings will be invalid after env_clean(). That's why
3fe9483b2b412a14493e3120751b0e99ecfe9388Timo Sirainen we t_strconcat() them above. */
3fe9483b2b412a14493e3120751b0e99ecfe9388Timo Sirainenvoid master_service_set_client_limit(struct master_service *service,
3fe9483b2b412a14493e3120751b0e99ecfe9388Timo Sirainen unsigned int used;
3fe9483b2b412a14493e3120751b0e99ecfe9388Timo Sirainen i_assert(service->master_status.available_count ==
3fe9483b2b412a14493e3120751b0e99ecfe9388Timo Sirainen service->total_available_count = client_limit;
3fe9483b2b412a14493e3120751b0e99ecfe9388Timo Sirainen service->master_status.available_count = client_limit - used;
3fe9483b2b412a14493e3120751b0e99ecfe9388Timo Sirainenunsigned int master_service_get_client_limit(struct master_service *service)
dd9712b013e5a14939deed84b2e391d89897d2cfTimo Sirainenvoid master_service_set_service_count(struct master_service *service,
dd9712b013e5a14939deed84b2e391d89897d2cfTimo Sirainen unsigned int count)
dd9712b013e5a14939deed84b2e391d89897d2cfTimo Sirainen unsigned int used;
dd9712b013e5a14939deed84b2e391d89897d2cfTimo Sirainen service->master_status.available_count = count - used;
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainenunsigned int master_service_get_service_count(struct master_service *service)
dd9712b013e5a14939deed84b2e391d89897d2cfTimo Sirainenunsigned int master_service_get_socket_count(struct master_service *service)
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainenconst char *master_service_get_config_path(struct master_service *service)
bb2b91b4c5363348b737237893d414639510a561Timo Sirainenconst char *master_service_get_version_string(struct master_service *service)
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainenconst char *master_service_get_name(struct master_service *service)
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainenvoid master_service_run(struct master_service *service,
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen master_service_connection_callback_t *callback)
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainenvoid master_service_stop(struct master_service *service)
5278c93bd7105c32ac7ec37f36015d5950f6cbcaTimo Sirainenvoid master_service_anvil_send(struct master_service *service, const char *cmd)
44c5e644cb413a6559bf2d4179cbe48f9a82f366Timo Sirainen if ((service->flags & MASTER_SERVICE_FLAG_STANDALONE) != 0)
5278c93bd7105c32ac7ec37f36015d5950f6cbcaTimo Sirainen ret = write(MASTER_ANVIL_FD, cmd, strlen(cmd));
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen else if (ret == 0)
c3b36993785d9567a63bdad1933031060c9460cfTimo Sirainenvoid master_service_client_connection_destroyed(struct master_service *service)
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen /* we can listen again */
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen if (service->service_count_left != service->total_available_count) {
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen i_assert(service->service_count_left == (unsigned int)-1);
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen i_assert(service->master_status.available_count <
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen /* we have only limited amount of service requests left */
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen i_assert(service->master_status.available_count ==
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen if ((service->io_status_error == NULL || service->listeners == NULL) &&
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen /* we've finished handling all clients, and
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen a) master has closed the connection
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen b) there are no listeners (std-client?) */
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainenvoid master_service_deinit(struct master_service **_service)
c4db5f0fb7cea8160dc10b5f0ab57ab7c02bf8a5Timo Sirainen if (array_is_created(&service->config_overrides))
c4db5f0fb7cea8160dc10b5f0ab57ab7c02bf8a5Timo Sirainenstatic void master_service_listen(struct master_service_listener *l)
dd9712b013e5a14939deed84b2e391d89897d2cfTimo Sirainen if (l->service->master_status.available_count == 0) {
dd9712b013e5a14939deed84b2e391d89897d2cfTimo Sirainen /* we are full. stop listening for now. */
dd9712b013e5a14939deed84b2e391d89897d2cfTimo Sirainen conn.fd = net_accept(l->fd, &conn.remote_ip, &conn.remote_port);
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen /* it's not a socket. probably a fifo. use the "listener"
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen as the connection fd and stop the listener. */
88187ee880b4829443e0d55ea7d145d9d5880217Timo Sirainenstatic void io_listeners_init(struct master_service *service)
88187ee880b4829443e0d55ea7d145d9d5880217Timo Sirainen unsigned int i;
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen i_new(struct master_service_listener, service->socket_count);
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen struct master_service_listener *l = &service->listeners[i];
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen if (i >= service->socket_count - service->ssl_socket_count)
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainenstatic void io_listeners_add(struct master_service *service)
88187ee880b4829443e0d55ea7d145d9d5880217Timo Sirainen unsigned int i;
88187ee880b4829443e0d55ea7d145d9d5880217Timo Sirainen struct master_service_listener *l = &service->listeners[i];
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen l->io = io_add(MASTER_LISTEN_FD_FIRST + i, IO_READ,
5278c93bd7105c32ac7ec37f36015d5950f6cbcaTimo Sirainenstatic void io_listeners_remove(struct master_service *service)
44c5e644cb413a6559bf2d4179cbe48f9a82f366Timo Sirainen unsigned int i;
bb2b91b4c5363348b737237893d414639510a561Timo Sirainenstatic bool master_status_update_is_important(struct master_service *service)
bb2b91b4c5363348b737237893d414639510a561Timo Sirainen if (service->master_status.available_count == 0)
bb2b91b4c5363348b737237893d414639510a561Timo Sirainenstatic void master_status_update(struct master_service *service)
bb2b91b4c5363348b737237893d414639510a561Timo Sirainen return; /* closed */
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen ret = write(MASTER_STATUS_FD, &service->master_status,
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen /* success */
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen /* delayed important update sent successfully */
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen } else if (ret == 0) {
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen /* shouldn't happen? */
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen i_error("write(master_status_fd) returned 0");
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen /* failure */
14c474d9f4591c397ed0b5206af6537c7b52c924Timo Sirainen i_error("write(master_status_fd) failed: %m");
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen } else if (master_status_update_is_important(service)) {
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen /* reader is busy, but it's important to get this notification
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen through. send it when possible. */