service-process.c revision 939f758ca239c53a60dae89e70ccdecd92072c5b
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek/* Copyright (c) 2005-2017 Dovecot authors, see the included COPYING file */
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozekstatic void service_reopen_inet_listeners(struct service *service)
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek unsigned int i, count;
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek listeners = array_get(&service->listeners, &count);
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek for (i = 0; i < count; i++) {
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek if (!listeners[i]->reuse_port || listeners[i]->fd == -1)
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek /* stdin/stdout is already redirected to /dev/null. Other master fds
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek should have been opened with fd_close_on_exec() so we don't have to
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek worry about them.
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek because the destination fd might be another one's source fd we have
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek to be careful not to overwrite anything. dup() the fd when needed */
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek listeners = array_get(&service->listeners, &count);
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek dup2_append(&dups, service_anvil_global->log_fdpass_fd[0],
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek /* nonblocking anvil fd must be the first one. anvil treats it
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek as the master's fd */
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek dup2_append(&dups, service_anvil_global->nonblocking_fd[0], fd++);
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek dup2_append(&dups, service_anvil_global->blocking_fd[0], fd++);
1bcb68cc3069a6bd539289e68a87a0815aa2a1beJakub Hrozek /* add listeners */
1bcb68cc3069a6bd539289e68a87a0815aa2a1beJakub Hrozek for (i = 0; i < count; i++) {
1bcb68cc3069a6bd539289e68a87a0815aa2a1beJakub Hrozek str_append_tabescaped(listener_settings, listeners[i]->name);
1bcb68cc3069a6bd539289e68a87a0815aa2a1beJakub Hrozek if (listeners[i]->type == SERVICE_LISTENER_INET) {
1bcb68cc3069a6bd539289e68a87a0815aa2a1beJakub Hrozek env_put(t_strdup_printf("SOCKET%d_SETTINGS=%s",
1bcb68cc3069a6bd539289e68a87a0815aa2a1beJakub Hrozek socket_listener_count, str_c(listener_settings)));
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek dup2_append(&dups, dev_null_fd, MASTER_ANVIL_FD);
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek dup2_append(&dups, service_anvil_global->blocking_fd[1],
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek dup2_append(&dups, service->status_fd[1], MASTER_STATUS_FD);
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek dup2_append(&dups, service->master_dead_pipe_fd[1],
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek dup2_append(&dups, global_master_dead_pipe_fd[1],
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek /* keep stderr as-is. this is especially important when
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek log_path=/dev/stderr, but might be helpful even in other
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek situations for logging startup errors */
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek /* set log file to stderr. dup2() here immediately so that
08c72b84d85d482f030a30cf74786695f097e91cJakub Hrozek we can set up logging to it without causing any log messages
08c72b84d85d482f030a30cf74786695f097e91cJakub Hrozek to be lost. */
08c72b84d85d482f030a30cf74786695f097e91cJakub Hrozek if (dup2(service->log_fd[1], STDERR_FILENO) < 0)
08c72b84d85d482f030a30cf74786695f097e91cJakub Hrozek /* make sure we don't leak syslog fd. try to do it as late as possible,
08c72b84d85d482f030a30cf74786695f097e91cJakub Hrozek but also before dup2()s in case syslog fd is one of them. */
08c72b84d85d482f030a30cf74786695f097e91cJakub Hrozek i_fatal("service(%s): dup2s failed", service->set->name);
08c72b84d85d482f030a30cf74786695f097e91cJakub Hrozek i_assert(fd == MASTER_LISTEN_FD_FIRST + (int)socket_listener_count);
08c72b84d85d482f030a30cf74786695f097e91cJakub Hrozek env_put(t_strdup_printf("SOCKET_COUNT=%d", socket_listener_count));
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek rset.chroot_dir = *service->set->chroot == '\0' ? NULL :
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek /* drop trailing / if it exists */
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek rset.chroot_dir = t_strndup(rset.chroot_dir, len-1);
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek disallow_root = service->type == SERVICE_TYPE_LOGIN;
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozekstatic void service_process_setup_config_environment(struct service *service)
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek const struct master_service_settings *set = service->list->service_set;
a3c8390d19593b1e5277d95bfb4ab206d4785150Nikolai Kondrashov /* give the log's configuration directly, so it won't depend
a3c8390d19593b1e5277d95bfb4ab206d4785150Nikolai Kondrashov on config process */
08c72b84d85d482f030a30cf74786695f097e91cJakub Hrozek env_put(t_strconcat("LOG_PATH=", set->log_path, NULL));
08c72b84d85d482f030a30cf74786695f097e91cJakub Hrozek env_put(t_strconcat("INFO_LOG_PATH=", set->info_log_path, NULL));
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek env_put(t_strconcat("DEBUG_LOG_PATH=", set->debug_log_path, NULL));
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek env_put(t_strconcat("LOG_TIMESTAMP=", set->log_timestamp, NULL));
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek env_put(t_strconcat("SYSLOG_FACILITY=", set->syslog_facility, NULL));
a3c8390d19593b1e5277d95bfb4ab206d4785150Nikolai Kondrashov env_put(t_strconcat(MASTER_CONFIG_FILE_ENV"=",
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek services_get_config_socket_path(service->list), NULL));
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozekservice_process_setup_environment(struct service *service, unsigned int uid,
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek service_process_setup_config_environment(service);
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek env_put(t_strdup_printf(MASTER_CLIENT_LIMIT_ENV"=%u",
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek env_put(t_strdup_printf(MASTER_PROCESS_LIMIT_ENV"=%u",
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek env_put(t_strdup_printf(MASTER_PROCESS_MIN_AVAIL_ENV"=%u",
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek env_put(t_strdup_printf(MASTER_SERVICE_IDLE_KILL_ENV"=%u",
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek env_put(t_strdup_printf(MASTER_SERVICE_COUNT_ENV"=%u",
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek env_put(t_strdup_printf(MASTER_UID_ENV"=%u", uid));
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek env_put(t_strdup_printf(MY_HOSTNAME_ENV"=%s", my_hostname));
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek env_put(t_strdup_printf(MY_HOSTDOMAIN_ENV"=%s", hostdomain));
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek env_put(MASTER_DOVECOT_VERSION_ENV"="PACKAGE_VERSION);
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek if (ssl_manual_key_password != NULL && service->have_inet_listeners) {
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek /* manually given SSL password. give it only to services
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek that have inet listeners. */
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek env_put(t_strconcat(MASTER_SSL_KEY_PASSWORD_ENV"=",
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozekstatic void service_process_status_timeout(struct service_process *process)
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek "Initial status notification not received in %d "
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek "seconds, killing the process",
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek if (kill(process->pid, SIGKILL) < 0 && errno != ESRCH) {
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek service_error(process->service, "kill(%s, SIGKILL) failed: %m",
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozekstruct service_process *service_process_create(struct service *service)
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek static unsigned int uid_counter = 0;
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek /* throttling service, don't create new processes */
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek /* these services are being destroyed, no point in creating
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek new processes now */
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek /* look this up before fork()ing so that it gets cached for all the
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek future lookups. */
a3c8390d19593b1e5277d95bfb4ab206d4785150Nikolai Kondrashov limit_str = t_strdup_printf(" (ulimit -u %llu reached?)",
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek (unsigned long long)limit);
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek service_error(service, "fork() failed: %m%s", limit_str);
d8057ec487e452038f0106042021fa612bbb8555Michal Zidek service_process_setup_environment(service, uid, hostdomain);
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek i_assert(hash_table_lookup(service_pids, POINTER_CAST(pid)) == NULL);
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek timeout_add(SERVICE_FIRST_STATUS_TIMEOUT_SECS * 1000,
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek process->available_count = service->client_limit;
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek hash_table_insert(service_pids, POINTER_CAST(process->pid), process);
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek if (service->type == SERVICE_TYPE_ANVIL && process_forked)
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozekvoid service_process_destroy(struct service_process *process)
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek struct service_list *service_list = service->list;
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek hash_table_remove(service_pids, POINTER_CAST(process->pid));
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek i_assert(service->process_avail <= service->process_count);
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek service_process_notify_add(service->list->log_byes, process);
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek if (service->process_count < service->process_limit &&
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozekvoid service_process_ref(struct service_process *process)
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozekvoid service_process_unref(struct service_process *process)
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozekstatic const char *
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozekget_exit_status_message(struct service *service, enum fatal_exit_status status)
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek return "Can't open log file";
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek return "Can't write to log file";
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek return "Internal logging error";
5f93f452e4a80d6b0243eaf3c583d0caf9981ca0Jakub Hrozek str_printfa(str, " (service %s { vsz_limit=%u MB }, "
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek "you may need to increase it)",
a3c8390d19593b1e5277d95bfb4ab206d4785150Nikolai Kondrashov str_append(str, " - set CORE_OUTOFMEM=1 environment to get core dump");
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek return "exec() failed";
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek return "Fatal failure";
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozeklog_coredump(struct service *service, string_t *str, int status)
5f93f452e4a80d6b0243eaf3c583d0caf9981ca0Jakub Hrozek if (signum != SIGABRT && signum != SIGSEGV && signum != SIGBUS)
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek /* let's try to figure out why we didn't get a core dump */
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek if (!service->set->drop_priv_before_exec && service->uid != 0) {
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek str_printfa(str, " (core not dumped - set service %s "
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek "{ drop_priv_before_exec=yes })",
5f93f452e4a80d6b0243eaf3c583d0caf9981ca0Jakub Hrozek if (*service->set->privileged_group != '\0' && service->uid != 0) {
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek str_printfa(str, " (core not dumped - service %s "
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek "{ privileged_group } prevented it)",
1bcb68cc3069a6bd539289e68a87a0815aa2a1beJakub Hrozek str_printfa(str, " (core not dumped - add -D parameter to "
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek "service %s { executable }", service->set->name);
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek str_printfa(str, " (core not dumped - try to clear "
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek "service %s { chroot = } )", service->set->name);
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozekservice_process_get_status_error(string_t *str, struct service_process *process,
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek const char *msg;
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek str_printfa(str, "service(%s): child %s ", service->set->name,
129310e872a0a70e721ba59363e518176ef406d6Jakub Hrozek str_printfa(str, "killed with signal %d", WTERMSIG(status));
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek str_printfa(str, "died with status %d", status);
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek msg = get_exit_status_message(service, status);
129310e872a0a70e721ba59363e518176ef406d6Jakub Hrozekstatic void service_process_log(struct service_process *process,
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek const char *data;
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek /* log it via the log process in charge of handling
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek this process's logging */
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek default_fatal ? "DEFAULT-FATAL" : "FATAL", str);
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek if (write(process->service->list->master_log_fd[1],
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozekvoid service_process_log_status_error(struct service_process *process,
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek /* fast path */
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek service_process_get_status_error(str, process, status,