service-process.c revision 6ec925f52d04ec8700e47cb005bd7ddc65ac5614
1505N/A/* Copyright (c) 2005-2009 Dovecot authors, see the included COPYING file */
1505N/A
1505N/A#include "common.h"
1505N/A#include "array.h"
1505N/A#include "aqueue.h"
1505N/A#include "ioloop.h"
1505N/A#include "istream.h"
1505N/A#include "ostream.h"
1505N/A#include "write-full.h"
1505N/A#include "base64.h"
1505N/A#include "hash.h"
1505N/A#include "str.h"
1505N/A#include "hostpid.h"
1505N/A#include "env-util.h"
1505N/A#include "fd-close-on-exec.h"
1505N/A#include "restrict-access.h"
1505N/A#include "restrict-process-size.h"
1505N/A#include "master-service-settings.h"
1505N/A#include "dup2-array.h"
1505N/A#include "service.h"
1505N/A#include "service-log.h"
1505N/A#include "service-auth-server.h"
3339N/A#include "service-auth-source.h"
1505N/A#include "service-process-notify.h"
3339N/A#include "service-process.h"
1505N/A
1505N/A#include <stdlib.h>
1715N/A#include <unistd.h>
1505N/A#include <fcntl.h>
1505N/A#include <syslog.h>
1505N/A#include <signal.h>
2631N/A#include <sys/wait.h>
1505N/A
1505N/Astatic void
1505N/Aservice_dup_fds(struct service *service, int auth_fd, int std_fd,
1715N/A bool give_anvil_fd)
1505N/A{
1715N/A struct service_listener *const *listeners;
1505N/A ARRAY_TYPE(dup2) dups;
1505N/A unsigned int i, count, n = 0, socket_listener_count, ssl_socket_count;
1505N/A
1895N/A /* stdin/stdout is already redirected to /dev/null. Other master fds
1715N/A should have been opened with fd_close_on_exec() so we don't have to
1715N/A worry about them.
1715N/A
1715N/A because the destination fd might be another one's source fd we have
1715N/A to be careful not to overwrite anything. dup() the fd when needed */
1715N/A
1715N/A socket_listener_count = 0;
2483N/A listeners = array_get(&service->listeners, &count);
3070N/A t_array_init(&dups, count + 10);
2631N/A
1505N/A switch (service->type) {
1505N/A case SERVICE_TYPE_LOG:
2795N/A i_assert(n == 0);
2795N/A services_log_dup2(&dups, service->list, MASTER_LISTEN_FD_FIRST,
2795N/A &socket_listener_count);
2795N/A n += socket_listener_count;
2795N/A break;
2795N/A case SERVICE_TYPE_ANVIL:
2795N/A /* nonblocking anvil fd must be the first one. anvil treats it
2798N/A as the master's fd */
2798N/A dup2_append(&dups, service->list->nonblocking_anvil_fd[0],
2798N/A MASTER_LISTEN_FD_FIRST + n++);
2798N/A dup2_append(&dups, service->list->blocking_anvil_fd[0],
2798N/A MASTER_LISTEN_FD_FIRST + n++);
2798N/A socket_listener_count += 2;
2798N/A break;
2798N/A default:
2798N/A break;
3070N/A }
3070N/A
3070N/A /* first add non-ssl listeners */
2798N/A for (i = 0; i < count; i++) {
2798N/A if (listeners[i]->fd != -1 &&
1895N/A (listeners[i]->type != SERVICE_LISTENER_INET ||
1895N/A !listeners[i]->set.inetset.set->ssl)) {
2483N/A dup2_append(&dups, listeners[i]->fd,
3070N/A MASTER_LISTEN_FD_FIRST + n);
1895N/A n++; socket_listener_count++;
1505N/A }
1505N/A }
1715N/A /* then ssl-listeners */
1715N/A ssl_socket_count = 0;
2798N/A for (i = 0; i < count; i++) {
2798N/A if (listeners[i]->fd != -1 &&
2795N/A listeners[i]->type == SERVICE_LISTENER_INET &&
2795N/A listeners[i]->set.inetset.set->ssl) {
1505N/A dup2_append(&dups, listeners[i]->fd,
2798N/A MASTER_LISTEN_FD_FIRST + n);
2798N/A n++; socket_listener_count++;
2798N/A ssl_socket_count++;
2798N/A }
2798N/A }
2798N/A
2798N/A if (!give_anvil_fd)
3171N/A dup2_append(&dups, null_fd, MASTER_ANVIL_FD);
2798N/A else {
2798N/A dup2_append(&dups, service->list->blocking_anvil_fd[1],
2798N/A MASTER_ANVIL_FD);
2798N/A }
2798N/A dup2_append(&dups, service->status_fd[1], MASTER_STATUS_FD);
1505N/A
1895N/A switch (service->type) {
1505N/A case SERVICE_TYPE_AUTH_SOURCE:
1505N/A case SERVICE_TYPE_AUTH_SERVER:
3009N/A i_assert(auth_fd != -1);
1505N/A dup2_append(&dups, auth_fd, MASTER_AUTH_FD);
1505N/A env_put(t_strdup_printf("MASTER_AUTH_FD=%d", MASTER_AUTH_FD));
3339N/A break;
3171N/A default:
1505N/A i_assert(auth_fd == -1);
1505N/A dup2_append(&dups, null_fd, MASTER_AUTH_FD);
3339N/A break;
1505N/A }
1505N/A
3339N/A if (std_fd != -1) {
1505N/A dup2_append(&dups, std_fd, STDIN_FILENO);
1505N/A dup2_append(&dups, std_fd, STDOUT_FILENO);
2798N/A env_put("LOGGED_IN=1");
1505N/A }
1505N/A
1895N/A if (service->type != SERVICE_TYPE_LOG) {
1895N/A /* set log file to stderr. dup2() here immediately so that
1895N/A we can set up logging to it without causing any log messages
1505N/A to be lost. */
2798N/A i_assert(service->log_fd[1] != -1);
1505N/A
2910N/A env_put("LOG_SERVICE=1");
2798N/A if (dup2(service->log_fd[1], STDERR_FILENO) < 0)
1505N/A i_fatal("dup2(log fd) failed: %m");
2798N/A i_set_failure_internal();
2555N/A } else {
2798N/A dup2_append(&dups, null_fd, STDERR_FILENO);
1505N/A }
1505N/A
1505N/A /* make sure we don't leak syslog fd. try to do it as late as possible,
2672N/A but also before dup2()s in case syslog fd is one of them. */
1505N/A closelog();
1505N/A
1505N/A if (dup2_array(&dups) < 0)
1505N/A service_error(service, "dup2s failed");
1505N/A
1505N/A env_put(t_strdup_printf("SOCKET_COUNT=%d", socket_listener_count));
1505N/A env_put(t_strdup_printf("SSL_SOCKET_COUNT=%d", ssl_socket_count));
1505N/A}
3070N/A
3070N/Astatic int validate_uid_gid(struct master_settings *set, uid_t uid, gid_t gid,
3070N/A const char *user)
3070N/A{
3070N/A if (uid == 0) {
3070N/A i_error("Logins with UID 0 not permitted (user %s)", user);
3070N/A return FALSE;
3070N/A }
3070N/A
3070N/A if (uid < (uid_t)set->first_valid_uid ||
3070N/A (set->last_valid_uid != 0 && uid > (uid_t)set->last_valid_uid)) {
1505N/A i_error("Logins with UID %s (user %s) not permitted "
3096N/A "(see first_valid_uid in config file)",
3339N/A dec2str(uid), user);
3096N/A return FALSE;
3096N/A }
3096N/A
2631N/A if (gid < (gid_t)set->first_valid_gid ||
2631N/A (set->last_valid_gid != 0 && gid > (gid_t)set->last_valid_gid)) {
2631N/A i_error("Logins for users with primary group ID %s (user %s) "
2631N/A "not permitted (see first_valid_gid in config file).",
2910N/A dec2str(gid), user);
2925N/A return FALSE;
2925N/A }
2631N/A
2631N/A return TRUE;
2631N/A}
2631N/A
2631N/Astatic void auth_args_apply(const char *const *args,
2631N/A struct restrict_access_settings *rset,
2631N/A const char **home)
2632N/A{
2632N/A const char *key, *value;
2632N/A string_t *expanded_vars;
2910N/A
2925N/A expanded_vars = t_str_new(128);
2925N/A str_append(expanded_vars, "VARS_EXPANDED=");
2632N/A for (; *args != NULL; args++) {
2632N/A if (strncmp(*args, "uid=", 4) == 0)
2632N/A rset->uid = (uid_t)strtoul(*args + 4, NULL, 10);
2632N/A else if (strncmp(*args, "gid=", 4) == 0)
2632N/A rset->gid = (gid_t)strtoul(*args + 4, NULL, 10);
2632N/A else if (strncmp(*args, "home=", 5) == 0) {
2632N/A *home = *args + 5;
2632N/A env_put(t_strconcat("HOME=", *args + 5, NULL));
2631N/A } else if (strncmp(*args, "chroot=", 7) == 0)
2910N/A rset->chroot_dir = *args + 7;
2925N/A else if (strncmp(*args, "system_groups_user=", 19) == 0)
2925N/A rset->system_groups_user = *args + 19;
2631N/A else if (strncmp(*args, "mail_access_groups=", 19) == 0) {
2500N/A rset->extra_groups =
2500N/A rset->extra_groups == NULL ? *args + 19 :
2500N/A t_strconcat(*args + 19, ",",
2500N/A rset->extra_groups, NULL);
2798N/A } else {
2925N/A /* unknown, set as environment */
2925N/A value = strchr(*args, '=');
1505N/A if (value == NULL) {
1505N/A /* boolean */
1505N/A key = *args;
1505N/A value = "=1";
1505N/A } else {
1505N/A key = t_strdup_until(*args, value);
1505N/A if (strcmp(key, "mail") == 0) {
1505N/A /* FIXME: kind of ugly to have it
1505N/A here.. */
1505N/A key = "mail_location";
1505N/A }
3070N/A }
3070N/A str_append(expanded_vars, key);
1895N/A str_append_c(expanded_vars, ' ');
1505N/A env_put(t_strconcat(t_str_ucase(key), value, NULL));
2500N/A }
2500N/A }
2500N/A env_put(str_c(expanded_vars));
2798N/A}
2519N/A
2925N/Astatic void auth_success_write(void)
2925N/A{
2925N/A int fd;
2925N/A
2925N/A if (auth_success_written)
2519N/A return;
1505N/A
1505N/A fd = creat(AUTH_SUCCESS_PATH, 0666);
1505N/A if (fd == -1)
3158N/A i_error("creat(%s) failed: %m", AUTH_SUCCESS_PATH);
1895N/A else
1505N/A (void)close(fd);
1505N/A auth_success_written = TRUE;
1505N/A}
1505N/A
1895N/Astatic void drop_privileges(struct service *service,
1505N/A const char *const *auth_args)
3158N/A{
1505N/A struct master_settings *master_set = service->set->master_set;
3070N/A struct restrict_access_settings rset;
3070N/A const char *user, *home = NULL;
3070N/A bool disallow_root;
3070N/A
3070N/A if (auth_args != NULL && service->set->master_set->mail_debug)
3070N/A env_put("DEBUG=1");
3070N/A
3070N/A if (service->set->vsz_limit != 0)
3070N/A restrict_process_size(service->set->vsz_limit, -1U);
3070N/A
2672N/A restrict_access_init(&rset);
2672N/A rset.uid = service->uid;
2672N/A rset.gid = service->gid;
2672N/A rset.privileged_gid = service->privileged_gid;
2672N/A rset.chroot_dir = *service->set->chroot == '\0' ? NULL :
2672N/A service->set->chroot;
2672N/A rset.extra_groups = service->extra_gids;
2672N/A
2672N/A if (auth_args == NULL) {
2672N/A /* non-authenticating service. don't use *_valid_gid checks */
2672N/A } else {
2798N/A i_assert(auth_args[0] != NULL);
2672N/A
2925N/A rset.first_valid_gid = master_set->first_valid_gid;
2925N/A rset.last_valid_gid = master_set->last_valid_gid;
2925N/A
2925N/A user = auth_args[0];
2672N/A env_put(t_strconcat("USER=", user, NULL));
2672N/A
2672N/A auth_success_write();
2672N/A auth_args_apply(auth_args + 1, &rset, &home);
2672N/A
2672N/A if (!validate_uid_gid(master_set, rset.uid, rset.gid, user))
2672N/A exit(FATAL_DEFAULT);
2672N/A }
2798N/A
2672N/A if (home != NULL) {
2925N/A if (chdir(home) < 0 && errno != ENOENT)
2925N/A i_error("chdir(%s) failed: %m", home);
2925N/A }
2925N/A
2925N/A if (service->set->drop_priv_before_exec) {
2672N/A disallow_root = service->type == SERVICE_TYPE_AUTH_SERVER ||
2672N/A service->type == SERVICE_TYPE_AUTH_SOURCE;
2672N/A restrict_access(&rset, home, disallow_root);
2798N/A } else {
3009N/A restrict_access_set_env(&rset);
2482N/A }
2519N/A}
2482N/A
2482N/Astatic void
2482N/Aservice_process_setup_environment(struct service *service, unsigned int uid)
2519N/A{
2910N/A const struct master_service_settings *set;
2925N/A struct service_listener *const *listeners;
2519N/A const char *const *p;
2519N/A unsigned int limit, count;
2925N/A
2482N/A /* remove all environment, and put back what we need */
2482N/A env_clean();
2482N/A for (p = service->list->child_process_env; *p != NULL; p++)
2519N/A env_put(*p);
2482N/A
3009N/A switch (service->type) {
3009N/A case SERVICE_TYPE_CONFIG:
3009N/A env_put(t_strconcat(MASTER_CONFIG_FILE_ENV"=",
3009N/A service->config_file_path, NULL));
3009N/A break;
3234N/A case SERVICE_TYPE_LOG:
3009N/A /* give the log's configuration directly, so it won't depend
3009N/A on config process */
3009N/A set = master_service_settings_get(master_service);
3009N/A env_put("DOVECONF_ENV=1");
3009N/A env_put(t_strconcat("LOG_PATH=", set->log_path, NULL));
3009N/A env_put(t_strconcat("INFO_LOG_PATH=", set->info_log_path, NULL));
3009N/A env_put(t_strconcat("LOG_TIMESTAMP=", set->log_timestamp, NULL));
3009N/A env_put(t_strconcat("SYSLOG_FACILITY=", set->syslog_facility, NULL));
3009N/A break;
3009N/A default:
3009N/A listeners = array_get(&service->list->config->listeners,
3009N/A &count);
3009N/A i_assert(count > 0);
3009N/A env_put(t_strconcat(MASTER_CONFIG_FILE_ENV"=",
3009N/A listeners[0]->set.fileset.set->path, NULL));
3009N/A break;
3009N/A }
3009N/A
3009N/A limit = service->set->client_limit;
2798N/A if (limit == 0) {
2798N/A /* fallback to default limit */
2483N/A limit = service->set->master_set->default_client_limit;
2483N/A }
2483N/A
2483N/A env_put(t_strdup_printf(MASTER_CLIENT_LIMIT_ENV"=%u", limit));
2483N/A env_put(t_strdup_printf(MASTER_UID_ENV"=%u", uid));
2483N/A
2483N/A if (!service->set->master_set->version_ignore)
2483N/A env_put(MASTER_DOVECOT_VERSION_ENV"="PACKAGE_VERSION);
2483N/A}
2483N/A
1895N/Astatic void service_process_status_timeout(struct service_process *process)
2798N/A{
2795N/A service_error(process->service,
2795N/A "Initial status notification not received in %d "
2795N/A "seconds, killing the process",
2795N/A SERVICE_FIRST_STATUS_TIMEOUT_SECS);
2795N/A if (kill(process->pid, SIGKILL) < 0 && errno != ESRCH) {
2795N/A service_error(process->service, "kill(%s, SIGKILL) failed: %m",
2795N/A dec2str(process->pid));
2795N/A }
2795N/A timeout_remove(&process->to_status);
2795N/A}
2795N/A
2795N/Astatic void
2795N/Ahandle_request(const struct service_process_auth_request *request)
2795N/A{
2795N/A string_t *str;
2910N/A
2795N/A if (request == NULL)
2925N/A return;
2925N/A
2910N/A if (request->data_size > 0) {
2795N/A str = t_str_new(request->data_size*3);
2795N/A str_append(str, "CLIENT_INPUT=");
2795N/A base64_encode(request->data, request->data_size, str);
2795N/A env_put(str_c(str));
2795N/A }
2795N/A
2795N/A env_put(t_strconcat("LOCAL_IP=", net_ip2addr(&request->local_ip), NULL));
2795N/A env_put(t_strconcat("IP=", net_ip2addr(&request->remote_ip), NULL));
2795N/A}
2910N/A
2795N/Astruct service_process *
2925N/Aservice_process_create(struct service *service, const char *const *auth_args,
2925N/A const struct service_process_auth_request *request)
2910N/A{
2795N/A static unsigned int uid_counter = 0;
2795N/A struct service_process *process;
2795N/A unsigned int uid = ++uid_counter;
2795N/A int fd[2];
2795N/A pid_t pid;
2795N/A
2798N/A switch (service->type) {
2795N/A case SERVICE_TYPE_AUTH_SOURCE:
3070N/A case SERVICE_TYPE_AUTH_SERVER:
2795N/A if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd) < 0) {
2795N/A service_error(service, "socketpair() failed: %m");
2795N/A return NULL;
2795N/A }
2795N/A fd_close_on_exec(fd[0], TRUE);
2795N/A fd_close_on_exec(fd[1], TRUE);
2795N/A break;
2795N/A default:
3070N/A fd[0] = fd[1] = -1;
2795N/A break;
2795N/A }
2795N/A
2795N/A pid = fork();
2910N/A if (pid < 0) {
2795N/A service_error(service, "fork() failed: %m");
2795N/A if (fd[0] != -1) {
2925N/A (void)close(fd[0]);
2795N/A (void)close(fd[1]);
2795N/A }
2795N/A return NULL;
2795N/A }
2795N/A if (pid == 0) {
2795N/A /* child */
2795N/A if (fd[0] != -1)
2795N/A (void)close(fd[0]);
2795N/A service_process_setup_environment(service, uid);
2910N/A handle_request(request);
2795N/A service_dup_fds(service, fd[1], request == NULL ? -1 :
2925N/A request->fd, auth_args != NULL);
2925N/A drop_privileges(service, auth_args);
2910N/A process_exec(service->executable, NULL);
2795N/A }
2795N/A
2795N/A switch (service->type) {
2795N/A case SERVICE_TYPE_AUTH_SERVER:
2795N/A process = i_malloc(sizeof(struct service_process_auth_server));
2795N/A process->service = service;
2795N/A service_process_auth_server_init(process, fd[0]);
2795N/A (void)close(fd[1]);
2795N/A break;
2910N/A case SERVICE_TYPE_AUTH_SOURCE:
2795N/A process = i_malloc(sizeof(struct service_process_auth_source));
2795N/A process->service = service;
2925N/A service_process_auth_source_init(process, fd[0]);
2925N/A (void)close(fd[1]);
2795N/A break;
2795N/A default:
2795N/A process = i_new(struct service_process, 1);
2795N/A process->service = service;
2795N/A i_assert(fd[0] == -1);
2795N/A break;
2795N/A }
2795N/A
2795N/A process->refcount = 1;
2795N/A process->pid = pid;
2910N/A process->uid = uid;
2795N/A process->to_status =
2795N/A timeout_add(SERVICE_FIRST_STATUS_TIMEOUT_SECS * 1000,
2925N/A service_process_status_timeout, process);
2795N/A
2795N/A process->available_count = service->set->client_limit;
2795N/A if (process->available_count == 0) {
2795N/A /* fallback to default limit */
2795N/A process->available_count =
2795N/A service->set->master_set->default_client_limit;
2795N/A }
2795N/A
2795N/A service->process_count++;
2910N/A service->process_avail++;
2795N/A
2795N/A hash_table_insert(service->list->pids, &process->pid, process);
2795N/A return process;
2795N/A}
2795N/A
3070N/Avoid service_process_destroy(struct service_process *process)
3070N/A{
3070N/A struct service *service = process->service;
3070N/A
3070N/A hash_table_remove(service->list->pids, &process->pid);
3070N/A
3070N/A if (process->available_count > 0)
3070N/A service->process_avail--;
3070N/A service->process_count--;
3070N/A i_assert(service->process_avail <= service->process_count);
3070N/A
3070N/A if (process->to_status != NULL)
3070N/A timeout_remove(&process->to_status);
3070N/A
3070N/A switch (process->service->type) {
2798N/A case SERVICE_TYPE_AUTH_SERVER:
2798N/A service_process_auth_server_deinit(process);
2798N/A break;
2798N/A case SERVICE_TYPE_AUTH_SOURCE:
2798N/A service_process_auth_source_deinit(process);
2798N/A break;
3070N/A default:
2798N/A break;
2798N/A }
2798N/A
3070N/A if (service->list->log_byes != NULL)
2798N/A service_process_notify_add(service->list->log_byes, process);
2798N/A
2798N/A process->destroyed = TRUE;
2798N/A service_process_unref(process);
2798N/A}
3070N/A
3070N/Avoid service_process_ref(struct service_process *process)
2798N/A{
2798N/A i_assert(process->refcount > 0);
3070N/A
3070N/A process->refcount++;
3070N/A}
3070N/A
3070N/Aint service_process_unref(struct service_process *process)
3070N/A{
3070N/A i_assert(process->refcount > 0);
3070N/A
3070N/A if (--process->refcount > 0)
3070N/A return TRUE;
3070N/A
3070N/A i_assert(process->destroyed);
3070N/A
3070N/A i_free(process);
3070N/A return FALSE;
2798N/A}
2798N/A
2798N/Astatic const char *
3070N/Aget_exit_status_message(struct service *service, enum fatal_exit_status status)
2798N/A{
2798N/A switch (status) {
2798N/A case FATAL_LOGOPEN:
2798N/A return "Can't open log file";
2798N/A case FATAL_LOGWRITE:
3070N/A return "Can't write to log file";
3070N/A case FATAL_LOGERROR:
2798N/A return "Internal logging error";
2798N/A case FATAL_OUTOFMEM:
2798N/A if (service->set->vsz_limit == 0)
3070N/A return "Out of memory";
2798N/A return t_strdup_printf("Out of memory (vsz_limit=%u MB)",
2798N/A service->set->vsz_limit);
3070N/A case FATAL_EXEC:
2798N/A return "exec() failed";
2798N/A
2798N/A case FATAL_DEFAULT:
2798N/A return "Fatal failure";
2798N/A }
3070N/A
3070N/A return NULL;
2798N/A}
2798N/A
3070N/Astatic void log_coredump(struct service *service, string_t *str, int status)
3070N/A{
3070N/A#ifdef WCOREDUMP
2798N/A int signum = WTERMSIG(status);
2798N/A
3070N/A if (WCOREDUMP(status)) {
2798N/A str_append(str, " (core dumped)");
2798N/A return;
2798N/A }
2798N/A
2798N/A if (signum != SIGABRT && signum != SIGSEGV && signum != SIGBUS)
3070N/A return;
3070N/A
2798N/A /* let's try to figure out why we didn't get a core dump */
2798N/A if (core_dumps_disabled) {
2798N/A str_printfa(str, " (core dumps disabled)");
3070N/A return;
2798N/A }
2798N/A
3070N/A#ifdef HAVE_PR_SET_DUMPABLE
2798N/A if (!service->set->drop_priv_before_exec) {
2798N/A str_append(str, " (core not dumped - set drop_priv_before_exec=yes)");
2798N/A return;
2798N/A }
2798N/A if (*service->set->privileged_group != '\0') {
3070N/A str_append(str, " (core not dumped - privileged_group prevented it)");
3070N/A return;
2798N/A }
2798N/A#endif
3070N/A
3070N/A str_append(str, " (core not dumped)");
2798N/A#endif
2798N/A}
2798N/A
3070N/Astatic void
2798N/Aservice_process_get_status_error(string_t *str, struct service_process *process,
2798N/A int status, bool *default_fatal_r)
2798N/A{
2798N/A struct service *service = process->service;
2798N/A const char *msg;
3070N/A
3070N/A *default_fatal_r = FALSE;
2798N/A
2798N/A str_printfa(str, "service(%s): child %s ", service->set->name,
3070N/A dec2str(process->pid));
3070N/A if (WIFSIGNALED(status)) {
2798N/A str_printfa(str, "killed with signal %d", WTERMSIG(status));
2798N/A log_coredump(service, str, status);
2798N/A return;
3070N/A }
2798N/A if (!WIFEXITED(status)) {
2798N/A str_printfa(str, "died with status %d", status);
2798N/A return;
2798N/A }
2798N/A
3070N/A status = WEXITSTATUS(status);
3070N/A if (status == 0) {
2798N/A str_truncate(str, 0);
2798N/A return;
3070N/A }
3070N/A str_printfa(str, "returned error %d", status);
2798N/A
3070N/A msg = get_exit_status_message(service, status);
2798N/A if (msg != NULL)
2798N/A str_printfa(str, " (%s)", msg);
3070N/A
2798N/A if (status == FATAL_DEFAULT)
2798N/A *default_fatal_r = TRUE;
2798N/A}
2798N/A
2798N/Astatic void service_process_log(struct service_process *process,
3070N/A bool default_fatal, const char *str)
3070N/A{
2798N/A const char *data;
2798N/A
3070N/A if (!default_fatal || process->service->log_fd[1] == -1) {
3070N/A i_error("%s", str);
3070N/A return;
3070N/A }
2798N/A
2798N/A /* log it via the log process in charge of handling
3070N/A this process's logging */
2798N/A data = t_strdup_printf("%d %s DEFAULT-FATAL %s\n",
2798N/A process->service->log_process_internal_fd,
2798N/A dec2str(process->pid), str);
2798N/A if (write(process->service->list->master_log_fd[1],
2798N/A data, strlen(data)) < 0) {
3070N/A i_error("write(log process) failed: %m");
3070N/A i_error("%s", str);
2798N/A }
2798N/A}
3070N/A
3070N/Avoid service_process_log_status_error(struct service_process *process,
3070N/A int status)
3070N/A{
2798N/A if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
2798N/A /* fast path */
3070N/A return;
2798N/A }
2798N/A T_BEGIN {
2798N/A string_t *str = t_str_new(256);
2798N/A bool default_fatal;
2798N/A
3070N/A service_process_get_status_error(str, process, status,
3070N/A &default_fatal);
2798N/A if (str_len(str) > 0)
2798N/A service_process_log(process, default_fatal, str_c(str));
3459N/A } T_END;
3459N/A}
3459N/A