summaryrefslogtreecommitdiffstats
path: root/src/doveadm/doveadm-master.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/doveadm/doveadm-master.c')
-rw-r--r--src/doveadm/doveadm-master.c297
1 files changed, 297 insertions, 0 deletions
diff --git a/src/doveadm/doveadm-master.c b/src/doveadm/doveadm-master.c
new file mode 100644
index 0000000..2f161d7
--- /dev/null
+++ b/src/doveadm/doveadm-master.c
@@ -0,0 +1,297 @@
+/* Copyright (c) 2010-2018 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "str.h"
+#include "strescape.h"
+#include "istream.h"
+#include "write-full.h"
+#include "master-service.h"
+#include "sleep.h"
+#include "doveadm.h"
+#include "doveadm-print.h"
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+
+#define MASTER_PID_FILE_NAME "master.pid"
+
+static bool pid_file_read(const char *path, pid_t *pid_r)
+{
+ char buf[32];
+ int fd;
+ ssize_t ret;
+ bool found;
+
+ fd = open(path, O_RDONLY);
+ if (fd == -1) {
+ if (errno == ENOENT)
+ return FALSE;
+ i_fatal("open(%s) failed: %m", path);
+ }
+
+ ret = read(fd, buf, sizeof(buf)-1);
+ if (ret <= 0) {
+ if (ret == 0)
+ i_error("Empty PID file in %s", path);
+ else
+ i_fatal("read(%s) failed: %m", path);
+ found = FALSE;
+ } else {
+ if (buf[ret-1] == '\n')
+ ret--;
+ buf[ret] = '\0';
+ if (str_to_pid(buf, pid_r) < 0)
+ found = FALSE;
+ else {
+ found = !(*pid_r == getpid() ||
+ (kill(*pid_r, 0) < 0 && errno == ESRCH));
+ }
+ }
+ i_close_fd(&fd);
+ return found;
+}
+
+void doveadm_master_send_signal(int signo)
+{
+ const char *pidfile_path;
+ unsigned int i;
+ pid_t pid;
+
+ pidfile_path = t_strconcat(doveadm_settings->base_dir,
+ "/"MASTER_PID_FILE_NAME, NULL);
+
+ if (!pid_file_read(pidfile_path, &pid))
+ i_fatal("Dovecot is not running (read from %s)", pidfile_path);
+
+ if (kill(pid, signo) < 0)
+ i_fatal("kill(%s, %d) failed: %m", dec2str(pid), signo);
+
+ if (signo == SIGTERM) {
+ /* wait for a while for the process to die */
+ i_sleep_msecs(1);
+ for (i = 0; i < 30; i++) {
+ if (kill(pid, 0) < 0) {
+ if (errno != ESRCH)
+ i_error("kill() failed: %m");
+ break;
+ }
+ i_sleep_msecs(100);
+ }
+ }
+}
+
+static void cmd_stop(struct doveadm_cmd_context *cctx ATTR_UNUSED)
+{
+ doveadm_master_send_signal(SIGTERM);
+}
+
+static void cmd_reload(struct doveadm_cmd_context *cctx ATTR_UNUSED)
+{
+ doveadm_master_send_signal(SIGHUP);
+}
+
+static struct istream *master_service_send_cmd(const char *cmd)
+{
+ struct istream *input;
+ const char *path, *line;
+
+ path = t_strconcat(doveadm_settings->base_dir, "/master", NULL);
+ int fd = net_connect_unix(path);
+ if (fd == -1)
+ i_fatal("net_connect_unix(%s) failed: %m", path);
+ net_set_nonblock(fd, FALSE);
+
+ const char *str =
+ t_strdup_printf("VERSION\tmaster-client\t1\t0\n%s\n", cmd);
+ if (write_full(fd, str, strlen(str)) < 0)
+ i_fatal("write(%s) failed: %m", path);
+
+ input = i_stream_create_fd_autoclose(&fd, IO_BLOCK_SIZE);
+ alarm(5);
+ if ((line = i_stream_read_next_line(input)) == NULL)
+ i_fatal("read(%s) failed: %m", path);
+ if (!version_string_verify(line, "master-server", 1)) {
+ i_fatal_status(EX_PROTOCOL,
+ "%s is not a compatible master socket", path);
+ }
+ alarm(0);
+ return input;
+}
+
+static struct istream *
+master_service_send_cmd_with_args(const char *cmd, const char *const *args)
+{
+ string_t *str = t_str_new(128);
+
+ str_append(str, cmd);
+ if (args != NULL) {
+ for (unsigned int i = 0; args[i] != NULL; i++) {
+ str_append_c(str, '\t');
+ str_append_tabescaped(str, args[i]);
+ }
+ }
+ return master_service_send_cmd(str_c(str));
+}
+
+static void cmd_service_stop(struct doveadm_cmd_context *cctx)
+{
+ const char *line, *const *services;
+
+ if (!doveadm_cmd_param_array(cctx, "service", &services))
+ i_fatal("service parameter missing");
+
+ struct istream *input =
+ master_service_send_cmd_with_args("STOP", services);
+
+ alarm(5);
+ if ((line = i_stream_read_next_line(input)) == NULL) {
+ i_error("read(%s) failed: %s", i_stream_get_name(input),
+ i_stream_get_error(input));
+ doveadm_exit_code = EX_TEMPFAIL;
+ } else if (line[0] == '-') {
+ doveadm_exit_code = DOVEADM_EX_NOTFOUND;
+ i_error("%s", line+1);
+ } else if (line[0] != '+') {
+ i_error("Unexpected input from %s: %s",
+ i_stream_get_name(input), line);
+ doveadm_exit_code = EX_TEMPFAIL;
+ }
+ alarm(0);
+ i_stream_destroy(&input);
+}
+
+static void cmd_service_status(struct doveadm_cmd_context *cctx)
+{
+ const char *line, *const *services;
+ unsigned int fields_count;
+
+ if (!doveadm_cmd_param_array(cctx, "service", &services))
+ services = NULL;
+
+ struct istream *input = master_service_send_cmd("SERVICE-STATUS");
+
+ doveadm_print_init(DOVEADM_PRINT_TYPE_PAGER);
+ doveadm_print_header_simple("name");
+ doveadm_print_header_simple("process_count");
+ doveadm_print_header_simple("process_avail");
+ doveadm_print_header_simple("process_limit");
+ doveadm_print_header_simple("client_limit");
+ doveadm_print_header_simple("throttle_secs");
+ doveadm_print_header_simple("exit_failure_last");
+ doveadm_print_header_simple("exit_failures_in_sec");
+ doveadm_print_header_simple("last_drop_warning");
+ doveadm_print_header_simple("listen_pending");
+ doveadm_print_header_simple("listening");
+ doveadm_print_header_simple("doveadm_stop");
+ doveadm_print_header_simple("process_total");
+ fields_count = doveadm_print_get_headers_count();
+
+ alarm(5);
+ while ((line = i_stream_read_next_line(input)) != NULL) {
+ if (line[0] == '\0')
+ break;
+ T_BEGIN {
+ const char *const *args = t_strsplit_tabescaped(line);
+ if (args[0] != NULL &&
+ (services == NULL ||
+ str_array_find(services, args[0]))) {
+ unsigned int i;
+ for (i = 0; i < fields_count && args[i] != NULL; i++)
+ doveadm_print(args[i]);
+ for (; i < fields_count; i++)
+ doveadm_print("");
+ }
+ } T_END;
+ }
+ if (line == NULL) {
+ i_error("read(%s) failed: %s", i_stream_get_name(input),
+ i_stream_get_error(input));
+ doveadm_exit_code = EX_TEMPFAIL;
+ }
+ alarm(0);
+ i_stream_destroy(&input);
+}
+
+static void cmd_process_status(struct doveadm_cmd_context *cctx)
+{
+ const char *line, *const *services;
+
+ if (!doveadm_cmd_param_array(cctx, "service", &services))
+ services = NULL;
+
+ struct istream *input =
+ master_service_send_cmd_with_args("PROCESS-STATUS", services);
+
+ doveadm_print_init(DOVEADM_PRINT_TYPE_TABLE);
+ doveadm_print_header_simple("name");
+ doveadm_print_header_simple("pid");
+ doveadm_print_header_simple("available_count");
+ doveadm_print_header_simple("total_count");
+ doveadm_print_header_simple("idle_start");
+ doveadm_print_header_simple("last_status_update");
+ doveadm_print_header_simple("last_kill_sent");
+
+ alarm(5);
+ while ((line = i_stream_read_next_line(input)) != NULL) {
+ if (line[0] == '\0')
+ break;
+ T_BEGIN {
+ const char *const *args = t_strsplit_tabescaped(line);
+ if (str_array_length(args) >= 7) {
+ for (unsigned int i = 0; i < 7; i++)
+ doveadm_print(args[i]);
+ }
+ } T_END;
+ }
+ if (line == NULL) {
+ i_error("read(%s) failed: %s", i_stream_get_name(input),
+ i_stream_get_error(input));
+ doveadm_exit_code = EX_TEMPFAIL;
+ }
+ alarm(0);
+ i_stream_destroy(&input);
+}
+
+struct doveadm_cmd_ver2 doveadm_cmd_stop_ver2 = {
+ .cmd = cmd_stop,
+ .name = "stop",
+ .usage = "",
+DOVEADM_CMD_PARAMS_START
+DOVEADM_CMD_PARAMS_END
+};
+
+struct doveadm_cmd_ver2 doveadm_cmd_reload_ver2 = {
+ .cmd = cmd_reload,
+ .name = "reload",
+ .usage = "",
+DOVEADM_CMD_PARAMS_START
+DOVEADM_CMD_PARAMS_END
+};
+
+struct doveadm_cmd_ver2 doveadm_cmd_service_stop_ver2 = {
+ .cmd = cmd_service_stop,
+ .name = "service stop",
+ .usage = "<service> [<service> [...]]",
+DOVEADM_CMD_PARAMS_START
+DOVEADM_CMD_PARAM('\0', "service", CMD_PARAM_ARRAY, CMD_PARAM_FLAG_POSITIONAL)
+DOVEADM_CMD_PARAMS_END
+};
+
+struct doveadm_cmd_ver2 doveadm_cmd_service_status_ver2 = {
+ .cmd = cmd_service_status,
+ .name = "service status",
+ .usage = "[<service> [...]]",
+DOVEADM_CMD_PARAMS_START
+DOVEADM_CMD_PARAM('\0', "service", CMD_PARAM_ARRAY, CMD_PARAM_FLAG_POSITIONAL)
+DOVEADM_CMD_PARAMS_END
+};
+
+struct doveadm_cmd_ver2 doveadm_cmd_process_status_ver2 = {
+ .cmd = cmd_process_status,
+ .name = "process status",
+ .usage = "[<service> [...]]",
+DOVEADM_CMD_PARAMS_START
+DOVEADM_CMD_PARAM('\0', "service", CMD_PARAM_ARRAY, CMD_PARAM_FLAG_POSITIONAL)
+DOVEADM_CMD_PARAMS_END
+};