summaryrefslogtreecommitdiffstats
path: root/src/master/service-anvil.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/master/service-anvil.c')
-rw-r--r--src/master/service-anvil.c202
1 files changed, 202 insertions, 0 deletions
diff --git a/src/master/service-anvil.c b/src/master/service-anvil.c
new file mode 100644
index 0000000..f5f8632
--- /dev/null
+++ b/src/master/service-anvil.c
@@ -0,0 +1,202 @@
+/* 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 <unistd.h>
+
+#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;
+}