service-process.c revision 230ef558135f16a66b86cbe3762524eaa9ae9d81
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce/* Copyright (c) 2005-2009 Dovecot authors, see the included COPYING file */
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce unsigned int i, count, n = 0, socket_listener_count, ssl_socket_count;
7128fadade544efcd86b113a5090b00d20993671Jakub Hrozek /* stdin/stdout is already redirected to /dev/null. Other master fds
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce should have been opened with fd_close_on_exec() so we don't have to
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce worry about them.
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce because the destination fd might be another one's source fd we have
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce to be careful not to overwrite anything. dup() the fd when needed */
7128fadade544efcd86b113a5090b00d20993671Jakub Hrozek listeners = array_get(&service->listeners, &count);
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce services_log_dup2(&dups, service->list, MASTER_LISTEN_FD_FIRST,
7128fadade544efcd86b113a5090b00d20993671Jakub Hrozek dup2_append(&dups, service_anvil_global->log_fdpass_fd[0],
7128fadade544efcd86b113a5090b00d20993671Jakub Hrozek /* nonblocking anvil fd must be the first one. anvil treats it
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce as the master's fd */
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce dup2_append(&dups, service_anvil_global->nonblocking_fd[0],
7128fadade544efcd86b113a5090b00d20993671Jakub Hrozek dup2_append(&dups, service_anvil_global->blocking_fd[0],
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce /* first add non-ssl listeners */
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce for (i = 0; i < count; i++) {
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce /* then ssl-listeners */
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce for (i = 0; i < count; i++) {
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce dup2_append(&dups, service_anvil_global->blocking_fd[1],
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce dup2_append(&dups, service->status_fd[1], MASTER_STATUS_FD);
f35f4e4c8bd5b834504c0554552d78db3624706aFabiano Fidêncio /* set log file to stderr. dup2() here immediately so that
f35f4e4c8bd5b834504c0554552d78db3624706aFabiano Fidêncio we can set up logging to it without causing any log messages
f35f4e4c8bd5b834504c0554552d78db3624706aFabiano Fidêncio to be lost. */
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce /* make sure we don't leak syslog fd. try to do it as late as possible,
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce but also before dup2()s in case syslog fd is one of them. */
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce i_fatal("service(%s): dup2s failed", service->set->name);
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce env_put(t_strdup_printf("SOCKET_COUNT=%d", socket_listener_count));
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce env_put(t_strdup_printf("SSL_SOCKET_COUNT=%d", ssl_socket_count));
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce unsigned int len;
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce restrict_process_size(service->vsz_limit/1024, -1U);
7128fadade544efcd86b113a5090b00d20993671Jakub Hrozek rset.chroot_dir = *service->set->chroot == '\0' ? NULL :
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce /* drop trailing / if it exists */
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce rset.chroot_dir = t_strndup(rset.chroot_dir, len-1);
f35f4e4c8bd5b834504c0554552d78db3624706aFabiano Fidêncio disallow_root = service->type == SERVICE_TYPE_LOGIN;
f35f4e4c8bd5b834504c0554552d78db3624706aFabiano Fidêncio restrict_access(&rset, NULL, disallow_root);
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorceservice_process_setup_environment(struct service *service, unsigned int uid)
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce const struct master_service_settings *set = service->list->service_set;
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce const char *const *p;
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce /* remove all environment, and put back what we need */
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce for (p = service->list->child_process_env; *p != NULL; p++)
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce /* give the log's configuration directly, so it won't depend
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce on config process */
7128fadade544efcd86b113a5090b00d20993671Jakub Hrozek env_put(t_strconcat("LOG_PATH=", set->log_path, NULL));
7128fadade544efcd86b113a5090b00d20993671Jakub Hrozek env_put(t_strconcat("INFO_LOG_PATH=", set->info_log_path, NULL));
7128fadade544efcd86b113a5090b00d20993671Jakub Hrozek env_put(t_strconcat("DEBUG_LOG_PATH=", set->debug_log_path, NULL));
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce env_put(t_strconcat("LOG_TIMESTAMP=", set->log_timestamp, NULL));
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce env_put(t_strconcat("SYSLOG_FACILITY=", set->syslog_facility, NULL));
8bb2fcbce7c3fcfd986f1bc835fbcc43ac7cd9d1Jakub Hrozek services_get_config_socket_path(service->list), NULL));
a8361f37af31a8a9767056bd27c418c947293f56Fabiano Fidêncio env_put(t_strdup_printf(MASTER_CLIENT_LIMIT_ENV"=%u",
f35f4e4c8bd5b834504c0554552d78db3624706aFabiano Fidêncio env_put(t_strdup_printf(MASTER_SERVICE_COUNT_ENV"=%u",
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce env_put(t_strdup_printf(MASTER_UID_ENV"=%u", uid));
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce env_put(MASTER_DOVECOT_VERSION_ENV"="PACKAGE_VERSION);
625bb2ddf15e8f305a53afa44e87f2146fa930afSimo Sorce if (*ssl_manual_key_password != '\0' && service->have_inet_listeners) {
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce /* manually given SSL password. give it only to services
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce that have inet listeners. */
8bb2fcbce7c3fcfd986f1bc835fbcc43ac7cd9d1Jakub Hrozek env_put(t_strconcat(MASTER_SSL_KEY_PASSWORD_ENV"=",
7128fadade544efcd86b113a5090b00d20993671Jakub Hrozekstatic void service_process_status_timeout(struct service_process *process)
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce "Initial status notification not received in %d "
8bb2fcbce7c3fcfd986f1bc835fbcc43ac7cd9d1Jakub Hrozek "seconds, killing the process",
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce if (kill(process->pid, SIGKILL) < 0 && errno != ESRCH) {
7128fadade544efcd86b113a5090b00d20993671Jakub Hrozek service_error(process->service, "kill(%s, SIGKILL) failed: %m",
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorcestruct service_process *service_process_create(struct service *service)
7128fadade544efcd86b113a5090b00d20993671Jakub Hrozek static unsigned int uid_counter = 0;
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce /* throttling service, don't create new processes */
f35f4e4c8bd5b834504c0554552d78db3624706aFabiano Fidêncio process = i_new(struct service_process, 1);
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce timeout_add(SERVICE_FIRST_STATUS_TIMEOUT_SECS * 1000,
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce process->available_count = service->client_limit;
7128fadade544efcd86b113a5090b00d20993671Jakub Hrozek hash_table_insert(service_pids, &process->pid, process);
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce if (service->type == SERVICE_TYPE_ANVIL && process_forked)
7128fadade544efcd86b113a5090b00d20993671Jakub Hrozekvoid service_process_destroy(struct service_process *process)
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce struct service_list *service_list = service->list;
7128fadade544efcd86b113a5090b00d20993671Jakub Hrozek hash_table_remove(service_pids, &process->pid);
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce i_assert(service->process_avail <= service->process_count);
8bb2fcbce7c3fcfd986f1bc835fbcc43ac7cd9d1Jakub Hrozek service_process_notify_add(service->list->log_byes, process);
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce if (service->process_count < service->process_limit &&
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorcevoid service_process_ref(struct service_process *process)
f35f4e4c8bd5b834504c0554552d78db3624706aFabiano Fidêncioint service_process_unref(struct service_process *process)
d806427f200dc1ffd44d37724eb40125af5cc8c2Fabiano Fidênciostatic const char *
d806427f200dc1ffd44d37724eb40125af5cc8c2Fabiano Fidêncioget_exit_status_message(struct service *service, enum fatal_exit_status status)
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce return "Can't open log file";
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce return "Can't write to log file";
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce return "Internal logging error";
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce return "Out of memory";
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce return t_strdup_printf("Out of memory (vsz_limit=%u MB, "
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorce "you may need to increase it)",
7128fadade544efcd86b113a5090b00d20993671Jakub Hrozek return "exec() failed";
d806427f200dc1ffd44d37724eb40125af5cc8c2Fabiano Fidêncio return "Fatal failure";
a9d46b86993ee8d87fddf0ba50665c0b1b78ebb7Simo Sorcelog_coredump(struct service *service, string_t *str, int status)
efc65e78fa4e01e6cecc8690a9899af61213be62Fabiano Fidêncio if (signum != SIGABRT && signum != SIGSEGV && signum != SIGBUS)
efc65e78fa4e01e6cecc8690a9899af61213be62Fabiano Fidêncio /* let's try to figure out why we didn't get a core dump */
efc65e78fa4e01e6cecc8690a9899af61213be62Fabiano Fidêncio str_printfa(str, " (core dumps disabled)");
efc65e78fa4e01e6cecc8690a9899af61213be62Fabiano Fidêncio if (!service->set->drop_priv_before_exec) {
efc65e78fa4e01e6cecc8690a9899af61213be62Fabiano Fidêncio str_append(str, " (core not dumped - set drop_priv_before_exec=yes)");
efc65e78fa4e01e6cecc8690a9899af61213be62Fabiano Fidêncio if (*service->set->privileged_group != '\0') {
efc65e78fa4e01e6cecc8690a9899af61213be62Fabiano Fidêncio str_append(str, " (core not dumped - privileged_group prevented it)");
65a38b8c9cabde6c46cc0e9868f54cb9bb10afbfFabiano Fidêncio str_append(str, " (core not dumped - add -D parameter to service executable");
65a38b8c9cabde6c46cc0e9868f54cb9bb10afbfFabiano Fidêncioservice_process_get_status_error(string_t *str, struct service_process *process,
65a38b8c9cabde6c46cc0e9868f54cb9bb10afbfFabiano Fidêncio struct service *service = process->service;
65a38b8c9cabde6c46cc0e9868f54cb9bb10afbfFabiano Fidêncio str_printfa(str, "service(%s): child %s ", service->set->name,
65a38b8c9cabde6c46cc0e9868f54cb9bb10afbfFabiano Fidêncio str_printfa(str, "killed with signal %d", WTERMSIG(status));
65a38b8c9cabde6c46cc0e9868f54cb9bb10afbfFabiano Fidêncio str_printfa(str, "died with status %d", status);
7171a7584dda534dde5409f3e7f4657e845ece15Fabiano Fidêncio str_printfa(str, "returned error %d", status);
7171a7584dda534dde5409f3e7f4657e845ece15Fabiano Fidêncio msg = get_exit_status_message(service, status);
7171a7584dda534dde5409f3e7f4657e845ece15Fabiano Fidênciostatic void service_process_log(struct service_process *process,
7171a7584dda534dde5409f3e7f4657e845ece15Fabiano Fidêncio if (!default_fatal || process->service->log_fd[1] == -1) {
f35f4e4c8bd5b834504c0554552d78db3624706aFabiano Fidêncio /* log it via the log process in charge of handling
f35f4e4c8bd5b834504c0554552d78db3624706aFabiano Fidêncio this process's logging */
8bb2fcbce7c3fcfd986f1bc835fbcc43ac7cd9d1Jakub Hrozek data = t_strdup_printf("%d %s DEFAULT-FATAL %s\n",
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce if (write(process->service->list->master_log_fd[1],
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorcevoid service_process_log_status_error(struct service_process *process,
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
1dd679584241a0f9b29072c7eed1c5c5e4a577e4Simo Sorce /* fast path */
7128fadade544efcd86b113a5090b00d20993671Jakub Hrozek service_process_get_status_error(str, process, status,