service-process.c revision 2047bbce87d1a1adc281a875814390bdc9532946
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina/* Copyright (c) 2005-2018 Dovecot authors, see the included COPYING file */
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina#include "common.h"
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina#include "array.h"
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina#include "aqueue.h"
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina#include "ioloop.h"
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina#include "istream.h"
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina#include "ostream.h"
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina#include "write-full.h"
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina#include "base64.h"
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina#include "hash.h"
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina#include "str.h"
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina#include "strescape.h"
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina#include "llist.h"
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina#include "hostpid.h"
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina#include "env-util.h"
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina#include "restrict-access.h"
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina#include "restrict-process-size.h"
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina#include "eacces-error.h"
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina#include "master-service.h"
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina#include "master-service-settings.h"
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina#include "dup2-array.h"
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina#include "service.h"
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina#include "service-anvil.h"
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina#include "service-listen.h"
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina#include "service-log.h"
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina#include "service-process-notify.h"
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina#include "service-process.h"
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina
ca6dd8e7ac91c7f8e3b4d55206d4f39791ab7149Pavel Březina#include <unistd.h>
ca6dd8e7ac91c7f8e3b4d55206d4f39791ab7149Pavel Březina#include <fcntl.h>
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina#include <syslog.h>
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina#include <signal.h>
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina#include <sys/wait.h>
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březinastatic void service_reopen_inet_listeners(struct service *service)
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina{
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina struct service_listener *const *listeners;
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina unsigned int i, count;
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina int old_fd;
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina listeners = array_get(&service->listeners, &count);
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina for (i = 0; i < count; i++) {
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina if (!listeners[i]->reuse_port || listeners[i]->fd == -1)
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina continue;
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina old_fd = listeners[i]->fd;
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina listeners[i]->fd = -1;
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina if (service_listener_listen(listeners[i]) < 0)
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina listeners[i]->fd = old_fd;
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina }
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina}
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březinastatic void
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březinaservice_dup_fds(struct service *service)
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina{
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina struct service_listener *const *listeners;
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina ARRAY_TYPE(dup2) dups;
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina string_t *listener_settings;
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina int fd = MASTER_LISTEN_FD_FIRST;
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina unsigned int i, count, socket_listener_count;
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina /* stdin/stdout is already redirected to /dev/null. Other master fds
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina should have been opened with fd_close_on_exec() so we don't have to
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina worry about them.
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina because the destination fd might be another one's source fd we have
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina to be careful not to overwrite anything. dup() the fd when needed */
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina socket_listener_count = 0;
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina listeners = array_get(&service->listeners, &count);
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina t_array_init(&dups, count + 10);
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina switch (service->type) {
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina case SERVICE_TYPE_LOG:
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina i_assert(fd == MASTER_LISTEN_FD_FIRST);
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina services_log_dup2(&dups, service->list, fd,
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina &socket_listener_count);
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina fd += socket_listener_count;
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina break;
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina case SERVICE_TYPE_ANVIL:
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina dup2_append(&dups, service_anvil_global->log_fdpass_fd[0],
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina MASTER_ANVIL_LOG_FDPASS_FD);
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina /* nonblocking anvil fd must be the first one. anvil treats it
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina as the master's fd */
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina dup2_append(&dups, service_anvil_global->nonblocking_fd[0], fd++);
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina dup2_append(&dups, service_anvil_global->blocking_fd[0], fd++);
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina socket_listener_count += 2;
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina break;
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina default:
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina break;
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina }
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina /* add listeners */
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina listener_settings = t_str_new(256);
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina for (i = 0; i < count; i++) {
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina if (listeners[i]->fd != -1) {
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina str_truncate(listener_settings, 0);
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina str_append_tabescaped(listener_settings, listeners[i]->name);
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina if (listeners[i]->type == SERVICE_LISTENER_INET) {
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina if (listeners[i]->set.inetset.set->ssl)
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina str_append(listener_settings, "\tssl");
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina if (listeners[i]->set.inetset.set->haproxy)
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina str_append(listener_settings, "\thaproxy");
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina }
ca6dd8e7ac91c7f8e3b4d55206d4f39791ab7149Pavel Březina
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina dup2_append(&dups, listeners[i]->fd, fd++);
ca6dd8e7ac91c7f8e3b4d55206d4f39791ab7149Pavel Březina
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina env_put(t_strdup_printf("SOCKET%d_SETTINGS=%s",
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina socket_listener_count, str_c(listener_settings)));
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina socket_listener_count++;
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina }
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina }
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina if (service->login_notify_fd != -1) {
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina dup2_append(&dups, service->login_notify_fd,
ca6dd8e7ac91c7f8e3b4d55206d4f39791ab7149Pavel Březina MASTER_LOGIN_NOTIFY_FD);
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina }
ca6dd8e7ac91c7f8e3b4d55206d4f39791ab7149Pavel Březina switch (service->type) {
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina case SERVICE_TYPE_LOG:
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina case SERVICE_TYPE_ANVIL:
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina case SERVICE_TYPE_CONFIG:
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina dup2_append(&dups, dev_null_fd, MASTER_ANVIL_FD);
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina break;
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina case SERVICE_TYPE_UNKNOWN:
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina case SERVICE_TYPE_LOGIN:
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina case SERVICE_TYPE_STARTUP:
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina dup2_append(&dups, service_anvil_global->blocking_fd[1],
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina MASTER_ANVIL_FD);
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina break;
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina }
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina dup2_append(&dups, service->status_fd[1], MASTER_STATUS_FD);
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina if (service->type != SERVICE_TYPE_ANVIL) {
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina dup2_append(&dups, service->master_dead_pipe_fd[1],
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina MASTER_DEAD_FD);
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina } else {
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina dup2_append(&dups, global_master_dead_pipe_fd[1],
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina MASTER_DEAD_FD);
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina }
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina if (service->type == SERVICE_TYPE_LOG) {
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina /* keep stderr as-is. this is especially important when
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina log_path=/dev/stderr, but might be helpful even in other
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina situations for logging startup errors */
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina } else {
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina /* set log file to stderr. dup2() here immediately so that
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina we can set up logging to it without causing any log messages
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina to be lost. */
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina i_assert(service->log_fd[1] != -1);
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina env_put("LOG_SERVICE=1");
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina if (dup2(service->log_fd[1], STDERR_FILENO) < 0)
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina i_fatal("dup2(log fd) failed: %m");
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina i_set_failure_internal();
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina }
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina /* make sure we don't leak syslog fd. try to do it as late as possible,
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina but also before dup2()s in case syslog fd is one of them. */
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina closelog();
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina if (dup2_array(&dups) < 0)
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina i_fatal("service(%s): dup2s failed", service->set->name);
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina i_assert(fd == MASTER_LISTEN_FD_FIRST + (int)socket_listener_count);
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina env_put(t_strdup_printf("SOCKET_COUNT=%d", socket_listener_count));
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina}
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březinastatic void
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březinadrop_privileges(struct service *service)
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina{
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina struct restrict_access_settings rset;
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina bool allow_root;
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina size_t len;
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina if (service->vsz_limit != 0)
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina restrict_process_size(service->vsz_limit);
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina restrict_access_init(&rset);
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina rset.uid = service->uid;
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina rset.gid = service->gid;
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina rset.privileged_gid = service->privileged_gid;
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina rset.chroot_dir = *service->set->chroot == '\0' ? NULL :
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina service->set->chroot;
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina if (rset.chroot_dir != NULL) {
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina /* drop trailing / if it exists */
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina len = strlen(rset.chroot_dir);
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina if (rset.chroot_dir[len-1] == '/')
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina rset.chroot_dir = t_strndup(rset.chroot_dir, len-1);
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina }
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina rset.extra_groups = service->extra_gids;
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina restrict_access_set_env(&rset);
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina if (service->set->drop_priv_before_exec) {
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina allow_root = service->type != SERVICE_TYPE_LOGIN;
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina restrict_access(&rset,
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina allow_root ? RESTRICT_ACCESS_FLAG_ALLOW_ROOT : 0,
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina NULL);
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina }
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina}
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březinastatic void service_process_setup_config_environment(struct service *service)
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina{
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina const struct master_service_settings *set = service->list->service_set;
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina switch (service->type) {
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina case SERVICE_TYPE_CONFIG:
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina env_put(t_strconcat(MASTER_CONFIG_FILE_ENV"=",
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina service->config_file_path, NULL));
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina break;
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina case SERVICE_TYPE_LOG:
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina /* give the log's configuration directly, so it won't depend
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina on config process */
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina env_put("DOVECONF_ENV=1");
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina env_put(t_strconcat("LOG_PATH=", set->log_path, NULL));
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina env_put(t_strconcat("INFO_LOG_PATH=", set->info_log_path, NULL));
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina env_put(t_strconcat("DEBUG_LOG_PATH=", set->debug_log_path, NULL));
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina env_put(t_strconcat("LOG_TIMESTAMP=", set->log_timestamp, NULL));
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina env_put(t_strconcat("SYSLOG_FACILITY=", set->syslog_facility, NULL));
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina if (set->verbose_proctitle)
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina env_put("VERBOSE_PROCTITLE=1");
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina env_put("SSL=no");
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina break;
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina default:
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina env_put(t_strconcat(MASTER_CONFIG_FILE_ENV"=",
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina services_get_config_socket_path(service->list), NULL));
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina break;
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina }
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina}
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březinastatic void
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březinaservice_process_setup_environment(struct service *service, unsigned int uid,
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina const char *hostdomain)
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina{
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina master_service_env_clean();
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina env_put(MASTER_IS_PARENT_ENV"=1");
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina service_process_setup_config_environment(service);
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina env_put(t_strdup_printf(MASTER_CLIENT_LIMIT_ENV"=%u",
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina service->client_limit));
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina env_put(t_strdup_printf(MASTER_PROCESS_LIMIT_ENV"=%u",
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina service->process_limit));
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina env_put(t_strdup_printf(MASTER_PROCESS_MIN_AVAIL_ENV"=%u",
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina service->set->process_min_avail));
ca6dd8e7ac91c7f8e3b4d55206d4f39791ab7149Pavel Březina env_put(t_strdup_printf(MASTER_SERVICE_IDLE_KILL_ENV"=%u",
ca6dd8e7ac91c7f8e3b4d55206d4f39791ab7149Pavel Březina service->idle_kill));
ca6dd8e7ac91c7f8e3b4d55206d4f39791ab7149Pavel Březina if (service->set->service_count != 0) {
ca6dd8e7ac91c7f8e3b4d55206d4f39791ab7149Pavel Březina env_put(t_strdup_printf(MASTER_SERVICE_COUNT_ENV"=%u",
ca6dd8e7ac91c7f8e3b4d55206d4f39791ab7149Pavel Březina service->set->service_count));
ca6dd8e7ac91c7f8e3b4d55206d4f39791ab7149Pavel Březina }
ca6dd8e7ac91c7f8e3b4d55206d4f39791ab7149Pavel Březina env_put(t_strdup_printf(MASTER_UID_ENV"=%u", uid));
ca6dd8e7ac91c7f8e3b4d55206d4f39791ab7149Pavel Březina env_put(t_strdup_printf(MY_HOSTNAME_ENV"=%s", my_hostname));
ca6dd8e7ac91c7f8e3b4d55206d4f39791ab7149Pavel Březina env_put(t_strdup_printf(MY_HOSTDOMAIN_ENV"=%s", hostdomain));
ca6dd8e7ac91c7f8e3b4d55206d4f39791ab7149Pavel Březina
ca6dd8e7ac91c7f8e3b4d55206d4f39791ab7149Pavel Březina if (!service->set->master_set->version_ignore)
ca6dd8e7ac91c7f8e3b4d55206d4f39791ab7149Pavel Březina env_put(MASTER_DOVECOT_VERSION_ENV"="PACKAGE_VERSION);
ca6dd8e7ac91c7f8e3b4d55206d4f39791ab7149Pavel Březina
ca6dd8e7ac91c7f8e3b4d55206d4f39791ab7149Pavel Březina if (ssl_manual_key_password != NULL && service->have_inet_listeners) {
ca6dd8e7ac91c7f8e3b4d55206d4f39791ab7149Pavel Březina /* manually given SSL password. give it only to services
ca6dd8e7ac91c7f8e3b4d55206d4f39791ab7149Pavel Březina that have inet listeners. */
ca6dd8e7ac91c7f8e3b4d55206d4f39791ab7149Pavel Březina env_put(t_strconcat(MASTER_SSL_KEY_PASSWORD_ENV"=",
ca6dd8e7ac91c7f8e3b4d55206d4f39791ab7149Pavel Březina ssl_manual_key_password, NULL));
ca6dd8e7ac91c7f8e3b4d55206d4f39791ab7149Pavel Březina }
ca6dd8e7ac91c7f8e3b4d55206d4f39791ab7149Pavel Březina if (service->type == SERVICE_TYPE_ANVIL &&
ca6dd8e7ac91c7f8e3b4d55206d4f39791ab7149Pavel Březina service_anvil_global->restarted)
ca6dd8e7ac91c7f8e3b4d55206d4f39791ab7149Pavel Březina env_put("ANVIL_RESTARTED=1");
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina env_put(t_strconcat(DOVECOT_LOG_DEBUG_ENV"=",
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina service->list->service_set->log_debug, NULL));
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina}
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březinastatic void service_process_status_timeout(struct service_process *process)
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina{
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina service_error(process->service,
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina "Initial status notification not received in %d "
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina "seconds, killing the process",
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina SERVICE_FIRST_STATUS_TIMEOUT_SECS);
4e5e846de22407f825fe3b4040d79606818a2419Jakub Hrozek if (kill(process->pid, SIGKILL) < 0 && errno != ESRCH) {
4e5e846de22407f825fe3b4040d79606818a2419Jakub Hrozek service_error(process->service, "kill(%s, SIGKILL) failed: %m",
4e5e846de22407f825fe3b4040d79606818a2419Jakub Hrozek dec2str(process->pid));
4e5e846de22407f825fe3b4040d79606818a2419Jakub Hrozek }
4e5e846de22407f825fe3b4040d79606818a2419Jakub Hrozek timeout_remove(&process->to_status);
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina}
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březinastruct service_process *service_process_create(struct service *service)
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina{
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina static unsigned int uid_counter = 0;
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina struct service_process *process;
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina unsigned int uid = ++uid_counter;
4e5e846de22407f825fe3b4040d79606818a2419Jakub Hrozek const char *hostdomain;
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina pid_t pid;
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina bool process_forked;
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina i_assert(service->status_fd[0] != -1);
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina if (service->to_throttle != NULL) {
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina /* throttling service, don't create new processes */
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina return NULL;
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina }
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina if (service->list->destroying) {
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina /* these services are being destroyed, no point in creating
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina new processes now */
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina return NULL;
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina }
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina /* look this up before fork()ing so that it gets cached for all the
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina future lookups. */
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina hostdomain = my_hostdomain();
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina if (service->type == SERVICE_TYPE_ANVIL &&
4e5e846de22407f825fe3b4040d79606818a2419Jakub Hrozek service_anvil_global->pid != 0) {
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina pid = service_anvil_global->pid;
uid = service_anvil_global->uid;
process_forked = FALSE;
} else {
pid = fork();
process_forked = TRUE;
service->list->fork_counter++;
}
if (pid < 0) {
int fork_errno = errno;
rlim_t limit;
const char *limit_str = "";
if (fork_errno == EAGAIN &&
restrict_get_process_limit(&limit) == 0) {
limit_str = t_strdup_printf(" (ulimit -u %llu reached?)",
(unsigned long long)limit);
}
errno = fork_errno;
service_error(service, "fork() failed: %m%s", limit_str);
return NULL;
}
if (pid == 0) {
/* child */
service_process_setup_environment(service, uid, hostdomain);
service_reopen_inet_listeners(service);
service_dup_fds(service);
drop_privileges(service);
process_exec(service->executable);
}
i_assert(hash_table_lookup(service_pids, POINTER_CAST(pid)) == NULL);
process = i_new(struct service_process, 1);
process->service = service;
process->refcount = 1;
process->pid = pid;
process->uid = uid;
if (process_forked) {
process->to_status =
timeout_add(SERVICE_FIRST_STATUS_TIMEOUT_SECS * 1000,
service_process_status_timeout, process);
}
process->available_count = service->client_limit;
service->process_count++;
service->process_avail++;
DLLIST_PREPEND(&service->processes, process);
service_list_ref(service->list);
hash_table_insert(service_pids, POINTER_CAST(process->pid), process);
if (service->type == SERVICE_TYPE_ANVIL && process_forked)
service_anvil_process_created(process);
return process;
}
void service_process_destroy(struct service_process *process)
{
struct service *service = process->service;
struct service_list *service_list = service->list;
DLLIST_REMOVE(&service->processes, process);
hash_table_remove(service_pids, POINTER_CAST(process->pid));
if (process->available_count > 0)
service->process_avail--;
service->process_count--;
i_assert(service->process_avail <= service->process_count);
timeout_remove(&process->to_status);
timeout_remove(&process->to_idle);
if (service->list->log_byes != NULL)
service_process_notify_add(service->list->log_byes, process);
process->destroyed = TRUE;
service_process_unref(process);
if (service->process_count < service->process_limit &&
service->type == SERVICE_TYPE_LOGIN)
service_login_notify(service, FALSE);
service_list_unref(service_list);
}
void service_process_ref(struct service_process *process)
{
i_assert(process->refcount > 0);
process->refcount++;
}
void service_process_unref(struct service_process *process)
{
i_assert(process->refcount > 0);
if (--process->refcount > 0)
return;
i_assert(process->destroyed);
i_free(process);
}
static const char *
get_exit_status_message(struct service *service, enum fatal_exit_status status)
{
string_t *str;
switch (status) {
case FATAL_LOGOPEN:
return "Can't open log file";
case FATAL_LOGWRITE:
return "Can't write to log file";
case FATAL_LOGERROR:
return "Internal logging error";
case FATAL_OUTOFMEM:
str = t_str_new(128);
str_append(str, "Out of memory");
if (service->vsz_limit != 0) {
str_printfa(str, " (service %s { vsz_limit=%"PRIuUOFF_T" MB }, "
"you may need to increase it)",
service->set->name,
service->vsz_limit/1024/1024);
}
if (getenv("CORE_OUTOFMEM") == NULL)
str_append(str, " - set CORE_OUTOFMEM=1 environment to get core dump");
return str_c(str);
case FATAL_EXEC:
return "exec() failed";
case FATAL_DEFAULT:
return "Fatal failure";
}
return NULL;
}
static void
log_coredump(struct service *service, string_t *str, int status)
{
#ifdef WCOREDUMP
int signum = WTERMSIG(status);
if (WCOREDUMP(status) != 0) {
str_append(str, " (core dumped)");
return;
}
if (signum != SIGABRT && signum != SIGSEGV && signum != SIGBUS)
return;
/* let's try to figure out why we didn't get a core dump */
if (core_dumps_disabled) {
str_printfa(str, " (core dumps disabled)");
return;
}
#ifndef HAVE_PR_SET_DUMPABLE
if (!service->set->drop_priv_before_exec && service->uid != 0) {
str_printfa(str, " (core not dumped - set service %s "
"{ drop_priv_before_exec=yes })",
service->set->name);
return;
}
if (*service->set->privileged_group != '\0' && service->uid != 0) {
str_printfa(str, " (core not dumped - service %s "
"{ privileged_group } prevented it)",
service->set->name);
return;
}
#else
if (!service->set->login_dump_core &&
service->type == SERVICE_TYPE_LOGIN) {
str_printfa(str, " (core not dumped - add -D parameter to "
"service %s { executable }", service->set->name);
return;
}
#endif
if (service->set->chroot[0] != '\0') {
str_printfa(str, " (core not dumped - try to clear "
"service %s { chroot = } )", service->set->name);
return;
}
str_append(str, " (core not dumped)");
#endif
}
static void
service_process_get_status_error(string_t *str, struct service_process *process,
int status, bool *default_fatal_r)
{
struct service *service = process->service;
const char *msg;
*default_fatal_r = FALSE;
str_printfa(str, "service(%s): child %s ", service->set->name,
dec2str(process->pid));
if (WIFSIGNALED(status)) {
str_printfa(str, "killed with signal %d", WTERMSIG(status));
log_coredump(service, str, status);
return;
}
if (!WIFEXITED(status)) {
str_printfa(str, "died with status %d", status);
return;
}
status = WEXITSTATUS(status);
if (status == 0) {
str_truncate(str, 0);
return;
}
str_printfa(str, "returned error %d", status);
msg = get_exit_status_message(service, status);
if (msg != NULL)
str_printfa(str, " (%s)", msg);
if (status == FATAL_DEFAULT)
*default_fatal_r = TRUE;
}
static void service_process_log(struct service_process *process,
bool default_fatal, const char *str)
{
const char *data;
if (process->service->log_fd[1] == -1) {
i_error("%s", str);
return;
}
/* log it via the log process in charge of handling
this process's logging */
data = t_strdup_printf("%d %s %s %s\n",
process->service->log_process_internal_fd,
dec2str(process->pid),
default_fatal ? "DEFAULT-FATAL" : "FATAL", str);
if (write(process->service->list->master_log_fd[1],
data, strlen(data)) < 0) {
i_error("write(log process) failed: %m");
i_error("%s", str);
}
}
void service_process_log_status_error(struct service_process *process,
int status)
{
if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
/* fast path */
return;
}
T_BEGIN {
string_t *str = t_str_new(256);
bool default_fatal;
service_process_get_status_error(str, process, status,
&default_fatal);
if (str_len(str) > 0)
service_process_log(process, default_fatal, str_c(str));
} T_END;
}