/* Copyright (c) 2009-2018 Dovecot authors, see the included COPYING file */ #include "common.h" #include "ioloop.h" #include "fdpass.h" #include "service.h" #include "service-process.h" #include "service-process-notify.h" #include "service-anvil.h" #include #define ANVIL_HANDSHAKE "VERSION\tanvil\t1\t0\n" struct service_anvil_global *service_anvil_global; static void service_list_anvil_discard_input_stop(struct service_anvil_global *anvil) { if (anvil->io_blocking != NULL) { io_remove(&anvil->io_blocking); io_remove(&anvil->io_nonblocking); } } static void anvil_input_fd_discard(struct service_anvil_global *anvil, int fd) { char buf[1024]; ssize_t ret; ret = read(fd, buf, sizeof(buf)); if (ret <= 0) { i_error("read(anvil fd) failed: %m"); service_list_anvil_discard_input_stop(anvil); } } static void anvil_input_blocking_discard(struct service_anvil_global *anvil) { anvil_input_fd_discard(anvil, anvil->blocking_fd[0]); } static void anvil_input_nonblocking_discard(struct service_anvil_global *anvil) { anvil_input_fd_discard(anvil, anvil->nonblocking_fd[0]); } static void service_list_anvil_discard_input(struct service_anvil_global *anvil) { if (anvil->io_blocking != NULL) return; anvil->io_blocking = io_add(anvil->blocking_fd[0], IO_READ, anvil_input_blocking_discard, anvil); anvil->io_nonblocking = io_add(anvil->nonblocking_fd[0], IO_READ, anvil_input_nonblocking_discard, anvil); } static int anvil_send_handshake(int fd, const char **error_r) { ssize_t ret; ret = write(fd, ANVIL_HANDSHAKE, strlen(ANVIL_HANDSHAKE)); if (ret < 0) { *error_r = t_strdup_printf("write(anvil) failed: %m"); return -1; } if (ret == 0) { *error_r = t_strdup_printf("write(anvil) returned EOF"); return -1; } /* this is a pipe, it either wrote all of it or nothing */ i_assert((size_t)ret == strlen(ANVIL_HANDSHAKE)); return 0; } static int service_process_write_anvil_kill(int fd, struct service_process *process) { const char *data; data = t_strdup_printf("KILL\t%s\n", dec2str(process->pid)); if (write(fd, data, strlen(data)) < 0) { if (errno != EAGAIN) i_error("write(anvil process) failed: %m"); return -1; } return 0; } void service_anvil_monitor_start(struct service_list *service_list) { struct service *service; if (service_anvil_global->process_count == 0) service_list_anvil_discard_input(service_anvil_global); else { service = service_lookup_type(service_list, SERVICE_TYPE_ANVIL); (void)service_process_create(service); } } void service_anvil_process_created(struct service_process *process) { struct service_anvil_global *anvil = service_anvil_global; const char *error; service_anvil_global->pid = process->pid; service_anvil_global->uid = process->uid; service_anvil_global->process_count++; service_list_anvil_discard_input_stop(anvil); if (anvil_send_handshake(anvil->blocking_fd[1], &error) < 0 || anvil_send_handshake(anvil->nonblocking_fd[1], &error) < 0) service_error(process->service, "%s", error); } void service_anvil_process_destroyed(struct service_process *process) { i_assert(service_anvil_global->process_count > 0); if (--service_anvil_global->process_count == 0) service_list_anvil_discard_input(service_anvil_global); if (service_anvil_global->pid == process->pid) service_anvil_global->pid = 0; service_anvil_global->restarted = TRUE; } void service_anvil_send_log_fd(void) { ssize_t ret; char b = 0; if (service_anvil_global->process_count == 0) return; ret = fd_send(service_anvil_global->log_fdpass_fd[1], services->anvil->log_fd[1], &b, 1); if (ret < 0) i_error("fd_send(anvil log fd) failed: %m"); else if (ret == 0) i_error("fd_send(anvil log fd) failed: disconnected"); } void service_anvil_global_init(void) { struct service_anvil_global *anvil; anvil = i_new(struct service_anvil_global, 1); if (pipe(anvil->status_fd) < 0) i_fatal("pipe() failed: %m"); if (pipe(anvil->blocking_fd) < 0) i_fatal("pipe() failed: %m"); if (pipe(anvil->nonblocking_fd) < 0) i_fatal("pipe() failed: %m"); if (socketpair(AF_UNIX, SOCK_STREAM, 0, anvil->log_fdpass_fd) < 0) i_fatal("socketpair() failed: %m"); fd_set_nonblock(anvil->status_fd[0], TRUE); fd_set_nonblock(anvil->status_fd[1], TRUE); fd_set_nonblock(anvil->nonblocking_fd[1], TRUE); fd_close_on_exec(anvil->status_fd[0], TRUE); fd_close_on_exec(anvil->status_fd[1], TRUE); fd_close_on_exec(anvil->blocking_fd[0], TRUE); fd_close_on_exec(anvil->blocking_fd[1], TRUE); fd_close_on_exec(anvil->nonblocking_fd[0], TRUE); fd_close_on_exec(anvil->nonblocking_fd[1], TRUE); fd_close_on_exec(anvil->log_fdpass_fd[0], TRUE); fd_close_on_exec(anvil->log_fdpass_fd[1], TRUE); anvil->kills = service_process_notify_init(anvil->nonblocking_fd[1], service_process_write_anvil_kill); service_anvil_global = anvil; } void service_anvil_global_deinit(void) { struct service_anvil_global *anvil = service_anvil_global; service_list_anvil_discard_input_stop(anvil); service_process_notify_deinit(&anvil->kills); if (close(anvil->log_fdpass_fd[0]) < 0) i_error("close(anvil) failed: %m"); if (close(anvil->log_fdpass_fd[1]) < 0) i_error("close(anvil) failed: %m"); if (close(anvil->blocking_fd[0]) < 0) i_error("close(anvil) failed: %m"); if (close(anvil->blocking_fd[1]) < 0) i_error("close(anvil) failed: %m"); if (close(anvil->nonblocking_fd[0]) < 0) i_error("close(anvil) failed: %m"); if (close(anvil->nonblocking_fd[1]) < 0) i_error("close(anvil) failed: %m"); if (close(anvil->status_fd[0]) < 0) i_error("close(anvil) failed: %m"); if (close(anvil->status_fd[1]) < 0) i_error("close(anvil) failed: %m"); i_free(anvil); service_anvil_global = NULL; }