service-process.c revision 114162093a3eb36c23a4ce4d4f2a43541dc18cc2
bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2005-2010 Dovecot authors, see the included COPYING file */
4a6f9ed8e5412508dcba1eabb58a3680ad5e9b68Timo Sirainen
4a6f9ed8e5412508dcba1eabb58a3680ad5e9b68Timo Sirainen#include "common.h"
4a6f9ed8e5412508dcba1eabb58a3680ad5e9b68Timo Sirainen#include "array.h"
4a6f9ed8e5412508dcba1eabb58a3680ad5e9b68Timo Sirainen#include "aqueue.h"
4a6f9ed8e5412508dcba1eabb58a3680ad5e9b68Timo Sirainen#include "ioloop.h"
4a6f9ed8e5412508dcba1eabb58a3680ad5e9b68Timo Sirainen#include "istream.h"
4a6f9ed8e5412508dcba1eabb58a3680ad5e9b68Timo Sirainen#include "ostream.h"
839c67ba93b58e394daa435f7b99587eec97cefbTimo Sirainen#include "write-full.h"
839c67ba93b58e394daa435f7b99587eec97cefbTimo Sirainen#include "base64.h"
839c67ba93b58e394daa435f7b99587eec97cefbTimo Sirainen#include "hash.h"
839c67ba93b58e394daa435f7b99587eec97cefbTimo Sirainen#include "str.h"
839c67ba93b58e394daa435f7b99587eec97cefbTimo Sirainen#include "llist.h"
839c67ba93b58e394daa435f7b99587eec97cefbTimo Sirainen#include "hostpid.h"
839c67ba93b58e394daa435f7b99587eec97cefbTimo Sirainen#include "env-util.h"
4a6f9ed8e5412508dcba1eabb58a3680ad5e9b68Timo Sirainen#include "fd-close-on-exec.h"
4a6f9ed8e5412508dcba1eabb58a3680ad5e9b68Timo Sirainen#include "restrict-access.h"
839c67ba93b58e394daa435f7b99587eec97cefbTimo Sirainen#include "restrict-process-size.h"
4a6f9ed8e5412508dcba1eabb58a3680ad5e9b68Timo Sirainen#include "eacces-error.h"
4a6f9ed8e5412508dcba1eabb58a3680ad5e9b68Timo Sirainen#include "master-service.h"
839c67ba93b58e394daa435f7b99587eec97cefbTimo Sirainen#include "master-service-settings.h"
839c67ba93b58e394daa435f7b99587eec97cefbTimo Sirainen#include "dup2-array.h"
839c67ba93b58e394daa435f7b99587eec97cefbTimo Sirainen#include "service.h"
839c67ba93b58e394daa435f7b99587eec97cefbTimo Sirainen#include "service-anvil.h"
839c67ba93b58e394daa435f7b99587eec97cefbTimo Sirainen#include "service-log.h"
839c67ba93b58e394daa435f7b99587eec97cefbTimo Sirainen#include "service-process-notify.h"
839c67ba93b58e394daa435f7b99587eec97cefbTimo Sirainen#include "service-process.h"
839c67ba93b58e394daa435f7b99587eec97cefbTimo Sirainen
4a6f9ed8e5412508dcba1eabb58a3680ad5e9b68Timo Sirainen#include <grp.h>
839c67ba93b58e394daa435f7b99587eec97cefbTimo Sirainen#include <pwd.h>
4a6f9ed8e5412508dcba1eabb58a3680ad5e9b68Timo Sirainen#include <stdlib.h>
4a6f9ed8e5412508dcba1eabb58a3680ad5e9b68Timo Sirainen#include <unistd.h>
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen#include <fcntl.h>
4a6f9ed8e5412508dcba1eabb58a3680ad5e9b68Timo Sirainen#include <syslog.h>
a3ea111cfdbfd4f32baeb0bd7f1d72568c60a023Timo Sirainen#include <signal.h>
4a6f9ed8e5412508dcba1eabb58a3680ad5e9b68Timo Sirainen#include <sys/wait.h>
4a6f9ed8e5412508dcba1eabb58a3680ad5e9b68Timo Sirainen
4a6f9ed8e5412508dcba1eabb58a3680ad5e9b68Timo Sirainenstatic void
4a6f9ed8e5412508dcba1eabb58a3680ad5e9b68Timo Sirainenservice_dup_fds(struct service *service)
a3ea111cfdbfd4f32baeb0bd7f1d72568c60a023Timo Sirainen{
4a6f9ed8e5412508dcba1eabb58a3680ad5e9b68Timo Sirainen struct service_listener *const *listeners;
4a6f9ed8e5412508dcba1eabb58a3680ad5e9b68Timo Sirainen ARRAY_TYPE(dup2) dups;
4a6f9ed8e5412508dcba1eabb58a3680ad5e9b68Timo Sirainen unsigned int i, count, n = 0, socket_listener_count, ssl_socket_count;
a3ea111cfdbfd4f32baeb0bd7f1d72568c60a023Timo Sirainen
4a6f9ed8e5412508dcba1eabb58a3680ad5e9b68Timo Sirainen /* stdin/stdout is already redirected to /dev/null. Other master fds
a87ee34b90aa7bb236751803c292c4deb6cfb058Timo Sirainen should have been opened with fd_close_on_exec() so we don't have to
a87ee34b90aa7bb236751803c292c4deb6cfb058Timo Sirainen worry about them.
a87ee34b90aa7bb236751803c292c4deb6cfb058Timo Sirainen
a87ee34b90aa7bb236751803c292c4deb6cfb058Timo Sirainen because the destination fd might be another one's source fd we have
a87ee34b90aa7bb236751803c292c4deb6cfb058Timo Sirainen to be careful not to overwrite anything. dup() the fd when needed */
4a6f9ed8e5412508dcba1eabb58a3680ad5e9b68Timo Sirainen
4a6f9ed8e5412508dcba1eabb58a3680ad5e9b68Timo Sirainen socket_listener_count = 0;
4a6f9ed8e5412508dcba1eabb58a3680ad5e9b68Timo Sirainen listeners = array_get(&service->listeners, &count);
4a6f9ed8e5412508dcba1eabb58a3680ad5e9b68Timo Sirainen t_array_init(&dups, count + 10);
4d3e576109a91270c04821f8d727cd1afec8c801Timo Sirainen
4a6f9ed8e5412508dcba1eabb58a3680ad5e9b68Timo Sirainen switch (service->type) {
4a6f9ed8e5412508dcba1eabb58a3680ad5e9b68Timo Sirainen case SERVICE_TYPE_LOG:
4d3e576109a91270c04821f8d727cd1afec8c801Timo Sirainen i_assert(n == 0);
4a6f9ed8e5412508dcba1eabb58a3680ad5e9b68Timo Sirainen services_log_dup2(&dups, service->list, MASTER_LISTEN_FD_FIRST,
4a6f9ed8e5412508dcba1eabb58a3680ad5e9b68Timo Sirainen &socket_listener_count);
4a6f9ed8e5412508dcba1eabb58a3680ad5e9b68Timo Sirainen n += socket_listener_count;
4d3e576109a91270c04821f8d727cd1afec8c801Timo Sirainen break;
4a6f9ed8e5412508dcba1eabb58a3680ad5e9b68Timo Sirainen case SERVICE_TYPE_ANVIL:
4a6f9ed8e5412508dcba1eabb58a3680ad5e9b68Timo Sirainen dup2_append(&dups, service_anvil_global->log_fdpass_fd[0],
4a6f9ed8e5412508dcba1eabb58a3680ad5e9b68Timo Sirainen MASTER_ANVIL_LOG_FDPASS_FD);
4a6f9ed8e5412508dcba1eabb58a3680ad5e9b68Timo Sirainen /* nonblocking anvil fd must be the first one. anvil treats it
047ebb958b682bc058097eddc34df574c3f3d6d4Timo Sirainen as the master's fd */
06843a896589e98fb259e6d7558326655126a407Timo Sirainen dup2_append(&dups, service_anvil_global->nonblocking_fd[0],
047ebb958b682bc058097eddc34df574c3f3d6d4Timo Sirainen MASTER_LISTEN_FD_FIRST + n++);
047ebb958b682bc058097eddc34df574c3f3d6d4Timo Sirainen dup2_append(&dups, service_anvil_global->blocking_fd[0],
047ebb958b682bc058097eddc34df574c3f3d6d4Timo Sirainen MASTER_LISTEN_FD_FIRST + n++);
06843a896589e98fb259e6d7558326655126a407Timo Sirainen socket_listener_count += 2;
06843a896589e98fb259e6d7558326655126a407Timo Sirainen break;
4a6f9ed8e5412508dcba1eabb58a3680ad5e9b68Timo Sirainen default:
4a6f9ed8e5412508dcba1eabb58a3680ad5e9b68Timo Sirainen break;
4a6f9ed8e5412508dcba1eabb58a3680ad5e9b68Timo Sirainen }
4d3e576109a91270c04821f8d727cd1afec8c801Timo Sirainen
4a6f9ed8e5412508dcba1eabb58a3680ad5e9b68Timo Sirainen /* first add non-ssl listeners */
146e7e3fabdd843d50680cdb6e4f7829890f5fcfTimo Sirainen for (i = 0; i < count; i++) {
6b35ba747072480bfc86ed7cc69df1d418e2ae20Timo Sirainen if (listeners[i]->fd != -1 &&
6b35ba747072480bfc86ed7cc69df1d418e2ae20Timo Sirainen (listeners[i]->type != SERVICE_LISTENER_INET ||
6b35ba747072480bfc86ed7cc69df1d418e2ae20Timo Sirainen !listeners[i]->set.inetset.set->ssl)) {
6b35ba747072480bfc86ed7cc69df1d418e2ae20Timo Sirainen dup2_append(&dups, listeners[i]->fd,
6b35ba747072480bfc86ed7cc69df1d418e2ae20Timo Sirainen MASTER_LISTEN_FD_FIRST + n);
6b35ba747072480bfc86ed7cc69df1d418e2ae20Timo Sirainen n++; socket_listener_count++;
6b35ba747072480bfc86ed7cc69df1d418e2ae20Timo Sirainen }
6b35ba747072480bfc86ed7cc69df1d418e2ae20Timo Sirainen }
6b35ba747072480bfc86ed7cc69df1d418e2ae20Timo Sirainen /* then ssl-listeners */
6b35ba747072480bfc86ed7cc69df1d418e2ae20Timo Sirainen ssl_socket_count = 0;
6b35ba747072480bfc86ed7cc69df1d418e2ae20Timo Sirainen for (i = 0; i < count; i++) {
6b35ba747072480bfc86ed7cc69df1d418e2ae20Timo Sirainen if (listeners[i]->fd != -1 &&
6b35ba747072480bfc86ed7cc69df1d418e2ae20Timo Sirainen listeners[i]->type == SERVICE_LISTENER_INET &&
6b35ba747072480bfc86ed7cc69df1d418e2ae20Timo Sirainen listeners[i]->set.inetset.set->ssl) {
6b35ba747072480bfc86ed7cc69df1d418e2ae20Timo Sirainen dup2_append(&dups, listeners[i]->fd,
6b35ba747072480bfc86ed7cc69df1d418e2ae20Timo Sirainen MASTER_LISTEN_FD_FIRST + n);
6b35ba747072480bfc86ed7cc69df1d418e2ae20Timo Sirainen n++; socket_listener_count++;
6b35ba747072480bfc86ed7cc69df1d418e2ae20Timo Sirainen ssl_socket_count++;
6b35ba747072480bfc86ed7cc69df1d418e2ae20Timo Sirainen }
6b35ba747072480bfc86ed7cc69df1d418e2ae20Timo Sirainen }
6b35ba747072480bfc86ed7cc69df1d418e2ae20Timo Sirainen
6b35ba747072480bfc86ed7cc69df1d418e2ae20Timo Sirainen if (service->login_notify_fd != -1) {
6b35ba747072480bfc86ed7cc69df1d418e2ae20Timo Sirainen dup2_append(&dups, service->login_notify_fd,
6b35ba747072480bfc86ed7cc69df1d418e2ae20Timo Sirainen MASTER_LOGIN_NOTIFY_FD);
51aceed49d7edcf1ce385d6d97f0acb7067a6608Aki Tuomi }
146e7e3fabdd843d50680cdb6e4f7829890f5fcfTimo Sirainen switch (service->type) {
51aceed49d7edcf1ce385d6d97f0acb7067a6608Aki Tuomi case SERVICE_TYPE_LOG:
51aceed49d7edcf1ce385d6d97f0acb7067a6608Aki Tuomi case SERVICE_TYPE_ANVIL:
51aceed49d7edcf1ce385d6d97f0acb7067a6608Aki Tuomi case SERVICE_TYPE_CONFIG:
51aceed49d7edcf1ce385d6d97f0acb7067a6608Aki Tuomi dup2_append(&dups, null_fd, MASTER_ANVIL_FD);
51aceed49d7edcf1ce385d6d97f0acb7067a6608Aki Tuomi break;
51aceed49d7edcf1ce385d6d97f0acb7067a6608Aki Tuomi case SERVICE_TYPE_UNKNOWN:
146e7e3fabdd843d50680cdb6e4f7829890f5fcfTimo Sirainen case SERVICE_TYPE_LOGIN:
146e7e3fabdd843d50680cdb6e4f7829890f5fcfTimo Sirainen dup2_append(&dups, service_anvil_global->blocking_fd[1],
146e7e3fabdd843d50680cdb6e4f7829890f5fcfTimo Sirainen MASTER_ANVIL_FD);
146e7e3fabdd843d50680cdb6e4f7829890f5fcfTimo Sirainen break;
146e7e3fabdd843d50680cdb6e4f7829890f5fcfTimo Sirainen }
146e7e3fabdd843d50680cdb6e4f7829890f5fcfTimo Sirainen dup2_append(&dups, service->status_fd[1], MASTER_STATUS_FD);
146e7e3fabdd843d50680cdb6e4f7829890f5fcfTimo Sirainen
146e7e3fabdd843d50680cdb6e4f7829890f5fcfTimo Sirainen if (service->type != SERVICE_TYPE_LOG) {
047ebb958b682bc058097eddc34df574c3f3d6d4Timo Sirainen /* set log file to stderr. dup2() here immediately so that
047ebb958b682bc058097eddc34df574c3f3d6d4Timo Sirainen we can set up logging to it without causing any log messages
047ebb958b682bc058097eddc34df574c3f3d6d4Timo Sirainen to be lost. */
047ebb958b682bc058097eddc34df574c3f3d6d4Timo Sirainen i_assert(service->log_fd[1] != -1);
146e7e3fabdd843d50680cdb6e4f7829890f5fcfTimo Sirainen
146e7e3fabdd843d50680cdb6e4f7829890f5fcfTimo Sirainen env_put("LOG_SERVICE=1");
146e7e3fabdd843d50680cdb6e4f7829890f5fcfTimo Sirainen if (dup2(service->log_fd[1], STDERR_FILENO) < 0)
146e7e3fabdd843d50680cdb6e4f7829890f5fcfTimo Sirainen i_fatal("dup2(log fd) failed: %m");
146e7e3fabdd843d50680cdb6e4f7829890f5fcfTimo Sirainen i_set_failure_internal();
51aceed49d7edcf1ce385d6d97f0acb7067a6608Aki Tuomi } else {
146e7e3fabdd843d50680cdb6e4f7829890f5fcfTimo Sirainen dup2_append(&dups, null_fd, STDERR_FILENO);
146e7e3fabdd843d50680cdb6e4f7829890f5fcfTimo Sirainen }
146e7e3fabdd843d50680cdb6e4f7829890f5fcfTimo Sirainen
146e7e3fabdd843d50680cdb6e4f7829890f5fcfTimo Sirainen /* make sure we don't leak syslog fd. try to do it as late as possible,
146e7e3fabdd843d50680cdb6e4f7829890f5fcfTimo Sirainen but also before dup2()s in case syslog fd is one of them. */
51aceed49d7edcf1ce385d6d97f0acb7067a6608Aki Tuomi closelog();
51aceed49d7edcf1ce385d6d97f0acb7067a6608Aki Tuomi
51aceed49d7edcf1ce385d6d97f0acb7067a6608Aki Tuomi if (dup2_array(&dups) < 0)
51aceed49d7edcf1ce385d6d97f0acb7067a6608Aki Tuomi i_fatal("service(%s): dup2s failed", service->set->name);
51aceed49d7edcf1ce385d6d97f0acb7067a6608Aki Tuomi
146e7e3fabdd843d50680cdb6e4f7829890f5fcfTimo Sirainen env_put(t_strdup_printf("SOCKET_COUNT=%d", socket_listener_count));
146e7e3fabdd843d50680cdb6e4f7829890f5fcfTimo Sirainen env_put(t_strdup_printf("SSL_SOCKET_COUNT=%d", ssl_socket_count));
146e7e3fabdd843d50680cdb6e4f7829890f5fcfTimo Sirainen}
146e7e3fabdd843d50680cdb6e4f7829890f5fcfTimo Sirainen
146e7e3fabdd843d50680cdb6e4f7829890f5fcfTimo Sirainenstatic void
146e7e3fabdd843d50680cdb6e4f7829890f5fcfTimo Sirainendrop_privileges(struct service *service)
047ebb958b682bc058097eddc34df574c3f3d6d4Timo Sirainen{
146e7e3fabdd843d50680cdb6e4f7829890f5fcfTimo Sirainen struct restrict_access_settings rset;
146e7e3fabdd843d50680cdb6e4f7829890f5fcfTimo Sirainen bool disallow_root;
d03a871a77f8ec36f48f5fea98d810e51b186fdbTimo Sirainen unsigned int len;
146e7e3fabdd843d50680cdb6e4f7829890f5fcfTimo Sirainen
146e7e3fabdd843d50680cdb6e4f7829890f5fcfTimo Sirainen if (service->vsz_limit != 0)
146e7e3fabdd843d50680cdb6e4f7829890f5fcfTimo Sirainen restrict_process_size(service->vsz_limit/1024, -1U);
146e7e3fabdd843d50680cdb6e4f7829890f5fcfTimo Sirainen
146e7e3fabdd843d50680cdb6e4f7829890f5fcfTimo Sirainen restrict_access_init(&rset);
047ebb958b682bc058097eddc34df574c3f3d6d4Timo Sirainen rset.uid = service->uid;
047ebb958b682bc058097eddc34df574c3f3d6d4Timo Sirainen rset.gid = service->gid;
047ebb958b682bc058097eddc34df574c3f3d6d4Timo Sirainen rset.privileged_gid = service->privileged_gid;
047ebb958b682bc058097eddc34df574c3f3d6d4Timo Sirainen rset.chroot_dir = *service->set->chroot == '\0' ? NULL :
047ebb958b682bc058097eddc34df574c3f3d6d4Timo Sirainen service->set->chroot;
047ebb958b682bc058097eddc34df574c3f3d6d4Timo Sirainen if (rset.chroot_dir != NULL) {
047ebb958b682bc058097eddc34df574c3f3d6d4Timo Sirainen /* drop trailing / if it exists */
047ebb958b682bc058097eddc34df574c3f3d6d4Timo Sirainen len = strlen(rset.chroot_dir);
047ebb958b682bc058097eddc34df574c3f3d6d4Timo Sirainen if (rset.chroot_dir[len-1] == '/')
047ebb958b682bc058097eddc34df574c3f3d6d4Timo Sirainen rset.chroot_dir = t_strndup(rset.chroot_dir, len-1);
047ebb958b682bc058097eddc34df574c3f3d6d4Timo Sirainen }
047ebb958b682bc058097eddc34df574c3f3d6d4Timo Sirainen rset.extra_groups = service->extra_gids;
047ebb958b682bc058097eddc34df574c3f3d6d4Timo Sirainen
047ebb958b682bc058097eddc34df574c3f3d6d4Timo Sirainen if (service->set->drop_priv_before_exec) {
047ebb958b682bc058097eddc34df574c3f3d6d4Timo Sirainen disallow_root = service->type == SERVICE_TYPE_LOGIN;
047ebb958b682bc058097eddc34df574c3f3d6d4Timo Sirainen restrict_access(&rset, NULL, disallow_root);
047ebb958b682bc058097eddc34df574c3f3d6d4Timo Sirainen } else {
047ebb958b682bc058097eddc34df574c3f3d6d4Timo Sirainen restrict_access_set_env(&rset);
51aceed49d7edcf1ce385d6d97f0acb7067a6608Aki Tuomi }
51aceed49d7edcf1ce385d6d97f0acb7067a6608Aki Tuomi}
51aceed49d7edcf1ce385d6d97f0acb7067a6608Aki Tuomi
047ebb958b682bc058097eddc34df574c3f3d6d4Timo Sirainenstatic void
047ebb958b682bc058097eddc34df574c3f3d6d4Timo Sirainenservice_process_setup_environment(struct service *service, unsigned int uid)
047ebb958b682bc058097eddc34df574c3f3d6d4Timo Sirainen{
047ebb958b682bc058097eddc34df574c3f3d6d4Timo Sirainen const struct master_service_settings *set = service->list->service_set;
047ebb958b682bc058097eddc34df574c3f3d6d4Timo Sirainen const char *const *p;
047ebb958b682bc058097eddc34df574c3f3d6d4Timo Sirainen
047ebb958b682bc058097eddc34df574c3f3d6d4Timo Sirainen /* remove all environment, and put back what we need */
047ebb958b682bc058097eddc34df574c3f3d6d4Timo Sirainen env_clean();
047ebb958b682bc058097eddc34df574c3f3d6d4Timo Sirainen for (p = service->list->child_process_env; *p != NULL; p++)
047ebb958b682bc058097eddc34df574c3f3d6d4Timo Sirainen env_put(*p);
047ebb958b682bc058097eddc34df574c3f3d6d4Timo Sirainen
047ebb958b682bc058097eddc34df574c3f3d6d4Timo Sirainen switch (service->type) {
047ebb958b682bc058097eddc34df574c3f3d6d4Timo Sirainen case SERVICE_TYPE_CONFIG:
047ebb958b682bc058097eddc34df574c3f3d6d4Timo Sirainen env_put(t_strconcat(MASTER_CONFIG_FILE_ENV"=",
047ebb958b682bc058097eddc34df574c3f3d6d4Timo Sirainen service->config_file_path, NULL));
047ebb958b682bc058097eddc34df574c3f3d6d4Timo Sirainen break;
047ebb958b682bc058097eddc34df574c3f3d6d4Timo Sirainen case SERVICE_TYPE_LOG:
047ebb958b682bc058097eddc34df574c3f3d6d4Timo Sirainen /* give the log's configuration directly, so it won't depend
047ebb958b682bc058097eddc34df574c3f3d6d4Timo Sirainen on config process */
047ebb958b682bc058097eddc34df574c3f3d6d4Timo Sirainen env_put("DOVECONF_ENV=1");
047ebb958b682bc058097eddc34df574c3f3d6d4Timo Sirainen env_put(t_strconcat("LOG_PATH=", set->log_path, NULL));
047ebb958b682bc058097eddc34df574c3f3d6d4Timo Sirainen env_put(t_strconcat("INFO_LOG_PATH=", set->info_log_path, NULL));
03b33ccb012636e453189ceef3865dee7812392eTimo Sirainen env_put(t_strconcat("DEBUG_LOG_PATH=", set->debug_log_path, NULL));
03b33ccb012636e453189ceef3865dee7812392eTimo Sirainen env_put(t_strconcat("LOG_TIMESTAMP=", set->log_timestamp, NULL));
03b33ccb012636e453189ceef3865dee7812392eTimo Sirainen env_put(t_strconcat("SYSLOG_FACILITY=", set->syslog_facility, NULL));
03b33ccb012636e453189ceef3865dee7812392eTimo Sirainen break;
03b33ccb012636e453189ceef3865dee7812392eTimo Sirainen default:
03b33ccb012636e453189ceef3865dee7812392eTimo Sirainen env_put(t_strconcat(MASTER_CONFIG_FILE_ENV"=",
60212a51eb0e34a90fd3416a8a6c0047814ee968Timo Sirainen services_get_config_socket_path(service->list), NULL));
60212a51eb0e34a90fd3416a8a6c0047814ee968Timo Sirainen break;
60212a51eb0e34a90fd3416a8a6c0047814ee968Timo Sirainen }
60212a51eb0e34a90fd3416a8a6c0047814ee968Timo Sirainen
03b33ccb012636e453189ceef3865dee7812392eTimo Sirainen env_put(t_strdup_printf(MASTER_CLIENT_LIMIT_ENV"=%u",
03b33ccb012636e453189ceef3865dee7812392eTimo Sirainen service->client_limit));
03b33ccb012636e453189ceef3865dee7812392eTimo Sirainen if (service->set->service_count != 0) {
03b33ccb012636e453189ceef3865dee7812392eTimo Sirainen env_put(t_strdup_printf(MASTER_SERVICE_COUNT_ENV"=%u",
03b33ccb012636e453189ceef3865dee7812392eTimo Sirainen service->set->service_count));
03b33ccb012636e453189ceef3865dee7812392eTimo Sirainen }
03b33ccb012636e453189ceef3865dee7812392eTimo Sirainen env_put(t_strdup_printf(MASTER_UID_ENV"=%u", uid));
03b33ccb012636e453189ceef3865dee7812392eTimo Sirainen
03b33ccb012636e453189ceef3865dee7812392eTimo Sirainen if (!service->set->master_set->version_ignore)
03b33ccb012636e453189ceef3865dee7812392eTimo Sirainen env_put(MASTER_DOVECOT_VERSION_ENV"="PACKAGE_VERSION);
51aceed49d7edcf1ce385d6d97f0acb7067a6608Aki Tuomi
51aceed49d7edcf1ce385d6d97f0acb7067a6608Aki Tuomi if (*ssl_manual_key_password != '\0' && service->have_inet_listeners) {
51aceed49d7edcf1ce385d6d97f0acb7067a6608Aki Tuomi /* manually given SSL password. give it only to services
03b33ccb012636e453189ceef3865dee7812392eTimo Sirainen that have inet listeners. */
03b33ccb012636e453189ceef3865dee7812392eTimo Sirainen env_put(t_strconcat(MASTER_SSL_KEY_PASSWORD_ENV"=",
03b33ccb012636e453189ceef3865dee7812392eTimo Sirainen ssl_manual_key_password, NULL));
03b33ccb012636e453189ceef3865dee7812392eTimo Sirainen }
03b33ccb012636e453189ceef3865dee7812392eTimo Sirainen}
03b33ccb012636e453189ceef3865dee7812392eTimo Sirainen
03b33ccb012636e453189ceef3865dee7812392eTimo Sirainenstatic void service_process_status_timeout(struct service_process *process)
03b33ccb012636e453189ceef3865dee7812392eTimo Sirainen{
03b33ccb012636e453189ceef3865dee7812392eTimo Sirainen service_error(process->service,
03b33ccb012636e453189ceef3865dee7812392eTimo Sirainen "Initial status notification not received in %d "
03b33ccb012636e453189ceef3865dee7812392eTimo Sirainen "seconds, killing the process",
03b33ccb012636e453189ceef3865dee7812392eTimo Sirainen SERVICE_FIRST_STATUS_TIMEOUT_SECS);
03b33ccb012636e453189ceef3865dee7812392eTimo Sirainen if (kill(process->pid, SIGKILL) < 0 && errno != ESRCH) {
03b33ccb012636e453189ceef3865dee7812392eTimo Sirainen service_error(process->service, "kill(%s, SIGKILL) failed: %m",
03b33ccb012636e453189ceef3865dee7812392eTimo Sirainen dec2str(process->pid));
03b33ccb012636e453189ceef3865dee7812392eTimo Sirainen }
03b33ccb012636e453189ceef3865dee7812392eTimo Sirainen timeout_remove(&process->to_status);
03b33ccb012636e453189ceef3865dee7812392eTimo Sirainen}
03b33ccb012636e453189ceef3865dee7812392eTimo Sirainen
03b33ccb012636e453189ceef3865dee7812392eTimo Sirainenstruct service_process *service_process_create(struct service *service)
03b33ccb012636e453189ceef3865dee7812392eTimo Sirainen{
03b33ccb012636e453189ceef3865dee7812392eTimo Sirainen static unsigned int uid_counter = 0;
8d85926bc5484cf5f8de2ab3218298fe01d62696Timo Sirainen struct service_process *process;
6ea49f0ffe92ac0d709ac9282e136a94df0053eeTimo Sirainen unsigned int uid = ++uid_counter;
6ea49f0ffe92ac0d709ac9282e136a94df0053eeTimo Sirainen pid_t pid;
6ea49f0ffe92ac0d709ac9282e136a94df0053eeTimo Sirainen bool process_forked;
6ea49f0ffe92ac0d709ac9282e136a94df0053eeTimo Sirainen
6ea49f0ffe92ac0d709ac9282e136a94df0053eeTimo Sirainen i_assert(service->status_fd[0] != -1);
6ea49f0ffe92ac0d709ac9282e136a94df0053eeTimo Sirainen
6ea49f0ffe92ac0d709ac9282e136a94df0053eeTimo Sirainen if (service->to_throttle != NULL) {
6ea49f0ffe92ac0d709ac9282e136a94df0053eeTimo Sirainen /* throttling service, don't create new processes */
7de17a595a44e61714d3fc7e55491449d38a8e62Timo Sirainen return NULL;
7de17a595a44e61714d3fc7e55491449d38a8e62Timo Sirainen }
7de17a595a44e61714d3fc7e55491449d38a8e62Timo Sirainen
7de17a595a44e61714d3fc7e55491449d38a8e62Timo Sirainen if (service->type == SERVICE_TYPE_ANVIL &&
7de17a595a44e61714d3fc7e55491449d38a8e62Timo Sirainen service_anvil_global->pid != 0) {
7de17a595a44e61714d3fc7e55491449d38a8e62Timo Sirainen pid = service_anvil_global->pid;
7de17a595a44e61714d3fc7e55491449d38a8e62Timo Sirainen uid = service_anvil_global->uid;
7de17a595a44e61714d3fc7e55491449d38a8e62Timo Sirainen process_forked = FALSE;
7de17a595a44e61714d3fc7e55491449d38a8e62Timo Sirainen } else {
7de17a595a44e61714d3fc7e55491449d38a8e62Timo Sirainen pid = fork();
7de17a595a44e61714d3fc7e55491449d38a8e62Timo Sirainen process_forked = TRUE;
7de17a595a44e61714d3fc7e55491449d38a8e62Timo Sirainen }
7de17a595a44e61714d3fc7e55491449d38a8e62Timo Sirainen
7de17a595a44e61714d3fc7e55491449d38a8e62Timo Sirainen if (pid < 0) {
7de17a595a44e61714d3fc7e55491449d38a8e62Timo Sirainen service_error(service, "fork() failed: %m");
7de17a595a44e61714d3fc7e55491449d38a8e62Timo Sirainen return NULL;
7de17a595a44e61714d3fc7e55491449d38a8e62Timo Sirainen }
7de17a595a44e61714d3fc7e55491449d38a8e62Timo Sirainen if (pid == 0) {
7de17a595a44e61714d3fc7e55491449d38a8e62Timo Sirainen /* child */
7de17a595a44e61714d3fc7e55491449d38a8e62Timo Sirainen service_process_setup_environment(service, uid);
7de17a595a44e61714d3fc7e55491449d38a8e62Timo Sirainen service_dup_fds(service);
7de17a595a44e61714d3fc7e55491449d38a8e62Timo Sirainen drop_privileges(service);
7de17a595a44e61714d3fc7e55491449d38a8e62Timo Sirainen process_exec(service->executable, NULL);
7de17a595a44e61714d3fc7e55491449d38a8e62Timo Sirainen }
7de17a595a44e61714d3fc7e55491449d38a8e62Timo Sirainen
7de17a595a44e61714d3fc7e55491449d38a8e62Timo Sirainen process = i_new(struct service_process, 1);
7de17a595a44e61714d3fc7e55491449d38a8e62Timo Sirainen process->service = service;
7de17a595a44e61714d3fc7e55491449d38a8e62Timo Sirainen process->refcount = 1;
7de17a595a44e61714d3fc7e55491449d38a8e62Timo Sirainen process->pid = pid;
7de17a595a44e61714d3fc7e55491449d38a8e62Timo Sirainen process->uid = uid;
7de17a595a44e61714d3fc7e55491449d38a8e62Timo Sirainen if (process_forked) {
7de17a595a44e61714d3fc7e55491449d38a8e62Timo Sirainen process->to_status =
7de17a595a44e61714d3fc7e55491449d38a8e62Timo Sirainen timeout_add(SERVICE_FIRST_STATUS_TIMEOUT_SECS * 1000,
7de17a595a44e61714d3fc7e55491449d38a8e62Timo Sirainen service_process_status_timeout, process);
7de17a595a44e61714d3fc7e55491449d38a8e62Timo Sirainen }
7de17a595a44e61714d3fc7e55491449d38a8e62Timo Sirainen
7de17a595a44e61714d3fc7e55491449d38a8e62Timo Sirainen process->available_count = service->client_limit;
7de17a595a44e61714d3fc7e55491449d38a8e62Timo Sirainen service->process_count++;
7de17a595a44e61714d3fc7e55491449d38a8e62Timo Sirainen service->process_avail++;
7de17a595a44e61714d3fc7e55491449d38a8e62Timo Sirainen DLLIST_PREPEND(&service->processes, process);
7de17a595a44e61714d3fc7e55491449d38a8e62Timo Sirainen
7de17a595a44e61714d3fc7e55491449d38a8e62Timo Sirainen service_list_ref(service->list);
7de17a595a44e61714d3fc7e55491449d38a8e62Timo Sirainen hash_table_insert(service_pids, &process->pid, process);
7de17a595a44e61714d3fc7e55491449d38a8e62Timo Sirainen
8d85926bc5484cf5f8de2ab3218298fe01d62696Timo Sirainen if (service->type == SERVICE_TYPE_ANVIL && process_forked)
8d85926bc5484cf5f8de2ab3218298fe01d62696Timo Sirainen service_anvil_process_created(process);
8d85926bc5484cf5f8de2ab3218298fe01d62696Timo Sirainen return process;
8d85926bc5484cf5f8de2ab3218298fe01d62696Timo Sirainen}
8d85926bc5484cf5f8de2ab3218298fe01d62696Timo Sirainen
8d85926bc5484cf5f8de2ab3218298fe01d62696Timo Sirainenvoid service_process_destroy(struct service_process *process)
8d85926bc5484cf5f8de2ab3218298fe01d62696Timo Sirainen{
8d85926bc5484cf5f8de2ab3218298fe01d62696Timo Sirainen struct service *service = process->service;
8d85926bc5484cf5f8de2ab3218298fe01d62696Timo Sirainen struct service_list *service_list = service->list;
8d85926bc5484cf5f8de2ab3218298fe01d62696Timo Sirainen
8d85926bc5484cf5f8de2ab3218298fe01d62696Timo Sirainen DLLIST_REMOVE(&service->processes, process);
8d85926bc5484cf5f8de2ab3218298fe01d62696Timo Sirainen hash_table_remove(service_pids, &process->pid);
8d85926bc5484cf5f8de2ab3218298fe01d62696Timo Sirainen
4ec55911f4a7244ac2829570416f57623d5d1d5aTimo Sirainen if (process->available_count > 0)
8d85926bc5484cf5f8de2ab3218298fe01d62696Timo Sirainen service->process_avail--;
service->process_count--;
i_assert(service->process_avail <= service->process_count);
if (process->to_status != NULL)
timeout_remove(&process->to_status);
if (process->to_idle != NULL)
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++;
}
int service_process_unref(struct service_process *process)
{
i_assert(process->refcount > 0);
if (--process->refcount > 0)
return TRUE;
i_assert(process->destroyed);
i_free(process);
return FALSE;
}
static const char *
get_exit_status_message(struct service *service, enum fatal_exit_status status)
{
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:
if (service->vsz_limit == 0)
return "Out of memory";
return t_strdup_printf("Out of memory (vsz_limit=%u MB, "
"you may need to increase it)",
(unsigned int)(service->vsz_limit/1024/1024));
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)) {
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_append(str, " (core not dumped - set drop_priv_before_exec=yes)");
return;
}
if (*service->set->privileged_group != '\0' && service->uid != 0) {
str_append(str, " (core not dumped - privileged_group prevented it)");
return;
}
#else
if (!service->set->login_dump_core &&
service->type == SERVICE_TYPE_LOGIN) {
str_append(str, " (core not dumped - add -D parameter to service executable");
return;
}
#endif
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 (!default_fatal || 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 DEFAULT-FATAL %s\n",
process->service->log_process_internal_fd,
dec2str(process->pid), 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;
}