summaryrefslogtreecommitdiffstats
path: root/src/master/master-client.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/master/master-client.c')
-rw-r--r--src/master/master-client.c179
1 files changed, 179 insertions, 0 deletions
diff --git a/src/master/master-client.c b/src/master/master-client.c
new file mode 100644
index 0000000..82389ec
--- /dev/null
+++ b/src/master/master-client.c
@@ -0,0 +1,179 @@
+/* Copyright (c) 2016-2018 Dovecot authors, see the included COPYING file */
+
+#include "common.h"
+#include "array.h"
+#include "str.h"
+#include "strescape.h"
+#include "ostream.h"
+#include "connection.h"
+#include "service.h"
+#include "service-process.h"
+#include "service-monitor.h"
+#include "master-client.h"
+
+struct master_client {
+ struct connection conn;
+};
+
+static void
+master_client_service_status_output(string_t *str,
+ const struct service *service)
+{
+ str_append_tabescaped(str, service->set->name);
+ str_printfa(str, "\t%u\t%u\t%u\t%u\t%u\t%ld\t%u\t%ld\t%c\t%c\t%c\t%"PRIu64"\n",
+ service->process_count, service->process_avail,
+ service->process_limit, service->client_limit,
+ (service->to_throttle == NULL ?
+ 0 : ((service->throttle_msecs + 999) / 1000)),
+ (long)service->exit_failure_last,
+ service->exit_failures_in_sec,
+ (long)service->last_drop_warning,
+ service->listen_pending ? 'y' : 'n',
+ service->listening ? 'y' : 'n',
+ service->doveadm_stop ? 'y' : 'n',
+ service->process_count_total);
+}
+
+static int
+master_client_service_status(struct master_client *client)
+{
+ struct service *service;
+ string_t *str = t_str_new(128);
+
+ array_foreach_elem(&services->services, service) {
+ str_truncate(str, 0);
+ master_client_service_status_output(str, service);
+ o_stream_nsend(client->conn.output, str_data(str), str_len(str));
+ }
+ o_stream_nsend_str(client->conn.output, "\n");
+ return 1;
+}
+
+static void
+master_client_process_output(string_t *str,
+ const struct service_process *process)
+{
+ str_append_tabescaped(str, process->service->set->name);
+ str_printfa(str, "\t%ld\t%u\t%u\t%ld\t%ld\t%ld\n",
+ (long)process->pid, process->available_count,
+ process->total_count, (long)process->idle_start,
+ (long)process->last_status_update,
+ (long)process->last_kill_sent);
+}
+
+static int
+master_client_process_status(struct master_client *client,
+ const char *const *args)
+{
+ struct service *service;
+ struct service_process *p;
+ string_t *str = t_str_new(128);
+
+ array_foreach_elem(&services->services, service) {
+ if (args[0] != NULL && !str_array_find(args, service->set->name))
+ continue;
+ for (p = service->processes; p != NULL; p = p->next) {
+ str_truncate(str, 0);
+ master_client_process_output(str, p);
+ o_stream_nsend(client->conn.output,
+ str_data(str), str_len(str));
+ }
+ }
+ o_stream_nsend_str(client->conn.output, "\n");
+ return 1;
+}
+
+static int
+master_client_stop(struct master_client *client, const char *const *args)
+{
+ struct service *service;
+ const char *reply = "+\n";
+
+ for (unsigned int i = 0; args[i] != NULL; i++) {
+ service = service_lookup(services, args[i]);
+ if (service == NULL)
+ reply = t_strdup_printf("-Unknown service: %s\n", args[i]);
+ else {
+ service_monitor_stop_close(service);
+ service->doveadm_stop = TRUE;
+ }
+ }
+ o_stream_nsend_str(client->conn.output, reply);
+ return 1;
+}
+
+static int
+master_client_input_args(struct connection *conn, const char *const *args)
+{
+ struct master_client *client = (struct master_client *)conn;
+ const char *cmd = args[0];
+
+ if (cmd == NULL) {
+ i_error("%s: Empty command", conn->name);
+ return 0;
+ }
+ args++;
+
+ if (strcmp(cmd, "SERVICE-STATUS") == 0)
+ return master_client_service_status(client);
+ if (strcmp(cmd, "PROCESS-STATUS") == 0)
+ return master_client_process_status(client, args);
+ if (strcmp(cmd, "STOP") == 0)
+ return master_client_stop(client, args);
+ i_error("%s: Unknown command: %s", conn->name, cmd);
+ return -1;
+}
+
+static void master_client_destroy(struct connection *conn)
+{
+ struct master_client *client = (struct master_client *)conn;
+
+ connection_deinit(conn);
+ i_free(client);
+}
+
+static const struct connection_settings master_conn_set = {
+ .service_name_in = "master-client",
+ .service_name_out = "master-server",
+ .major_version = 1,
+ .minor_version = 0,
+
+ .input_max_size = 1024,
+ .output_max_size = 1024,
+ .client = FALSE
+};
+
+static const struct connection_vfuncs master_conn_vfuncs = {
+ .destroy = master_client_destroy,
+ .input_args = master_client_input_args
+};
+
+static struct connection_list *master_connections;
+
+void master_client_connected(struct service_list *service_list)
+{
+ struct master_client *client;
+ int fd;
+
+ fd = net_accept(service_list->master_fd, NULL, NULL);
+ if (fd < 0) {
+ if (fd == -2)
+ i_error("net_accept() failed: %m");
+ return;
+ }
+ fd_close_on_exec(fd, TRUE);
+ client = i_new(struct master_client, 1);
+ connection_init_server(master_connections, &client->conn,
+ "master-client", fd, fd);
+}
+
+void master_clients_init(void)
+{
+ master_connections = connection_list_init(&master_conn_set,
+ &master_conn_vfuncs);
+}
+
+void master_clients_deinit(void)
+{
+ connection_list_deinit(&master_connections);
+}