]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
master: Allow stopping specific services via master socket.
authorTimo Sirainen <timo.sirainen@dovecot.fi>
Sun, 26 Jun 2016 20:04:00 +0000 (23:04 +0300)
committerGitLab <gitlab@git.dovecot.net>
Wed, 29 Jun 2016 16:15:03 +0000 (19:15 +0300)
src/master/Makefile.am
src/master/main.c
src/master/master-client.c [new file with mode: 0644]
src/master/master-client.h [new file with mode: 0644]
src/master/service-listen.c
src/master/service-monitor.c
src/master/service.h

index 9d3ad7844f0e7831c91a567286116081b980116a..ef7955ff315c4de7b9d20111e0e49d2163c20964 100644 (file)
@@ -25,6 +25,7 @@ dovecot_SOURCES = \
        capabilities-posix.c \
        dup2-array.c \
        main.c \
+       master-client.c \
        master-settings.c \
        service-anvil.c \
        service-listen.c \
@@ -39,6 +40,7 @@ noinst_HEADERS = \
        capabilities.h \
        common.h \
        dup2-array.h \
+       master-client.h \
        master-settings.h \
        sd-daemon.h \
        service-anvil.h \
index 533d4d80566bfa73f26f32eb381e69441f6a30a5..ab67dc4dd35133d35f9b103072eacb2bdc449928 100644 (file)
@@ -18,6 +18,7 @@
 #include "master-service-settings.h"
 #include "askpass.h"
 #include "capabilities.h"
+#include "master-client.h"
 #include "service.h"
 #include "service-anvil.h"
 #include "service-listen.h"
@@ -526,6 +527,7 @@ static void main_init(const struct master_settings *set)
        create_pid_file(pidfile_path);
        create_config_symlink(set);
        instance_update(set);
+       master_clients_init();
 
        services_monitor_start(services);
 }
@@ -542,6 +544,7 @@ static void global_dead_pipe_close(void)
 
 static void main_deinit(void)
 {
+       master_clients_deinit();
        instance_update_now(instances);
        timeout_remove(&to_instance);
        master_instance_list_deinit(&instances);
diff --git a/src/master/master-client.c b/src/master/master-client.c
new file mode 100644 (file)
index 0000000..32cfbc3
--- /dev/null
@@ -0,0 +1,100 @@
+/* Copyright (c) 2016 Dovecot authors, see the included COPYING file */
+
+#include "common.h"
+#include "ostream.h"
+#include "connection.h"
+#include "service.h"
+#include "service-monitor.h"
+#include "master-client.h"
+
+struct master_client {
+       struct connection conn;
+};
+
+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);
+       }
+       o_stream_send_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, "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;
+       }
+       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);
+}
diff --git a/src/master/master-client.h b/src/master/master-client.h
new file mode 100644 (file)
index 0000000..7e62ede
--- /dev/null
@@ -0,0 +1,9 @@
+#ifndef MASTER_CLIENT_H
+#define MASTER_CLIENT_H
+
+void master_client_connected(struct service_list *service_list);
+
+void master_clients_init(void);
+void master_clients_deinit(void);
+
+#endif
index ca5525312f4414d4c183b97a183c9a682843fe01..1d3e399a7e7a5fcc1ed7b042592a6b1dfeee0f53 100644 (file)
@@ -4,7 +4,9 @@
 #include "array.h"
 #include "fd-set-nonblock.h"
 #include "fd-close-on-exec.h"
+#include "ioloop.h"
 #include "net.h"
+#include "master-client.h"
 #ifdef HAVE_SYSTEMD
 #include "sd-daemon.h"
 #endif
@@ -337,6 +339,31 @@ static int services_verify_systemd(struct service_list *service_list)
 }
 #endif
 
+static int services_listen_master(struct service_list *service_list)
+{
+       const char *path;
+       mode_t old_umask;
+
+       path = t_strdup_printf("%s/master", service_list->set->base_dir);
+       old_umask = umask(0600 ^ 0777);
+       service_list->master_fd = net_listen_unix(path, 16);
+       if (service_list->master_fd == -1 && errno == EADDRINUSE) {
+               /* already in use. all the other sockets were fine, so just
+                  delete this and retry. */
+               i_unlink_if_exists(path);
+               service_list->master_fd = net_listen_unix(path, 16);
+       }
+       umask(old_umask);
+
+       if (service_list->master_fd == -1)
+               return 0;
+
+       service_list->io_master =
+               io_add(service_list->master_fd, IO_READ,
+                      master_client_connected, service_list);
+       return 1;
+}
+
 int services_listen(struct service_list *service_list)
 {
        struct service *const *services;
@@ -347,6 +374,8 @@ int services_listen(struct service_list *service_list)
                if (ret2 < ret)
                        ret = ret2;
        }
+       if (ret > 0)
+               ret = services_listen_master(service_list);
 
 #ifdef HAVE_SYSTEMD
        if (ret > 0)
index a90c4ebe8ce4dbb0146c1243c712358b623dc932..19655c2c39b92544bac38f3fb920a850d4262280 100644 (file)
@@ -589,6 +589,11 @@ void services_monitor_stop(struct service_list *service_list, bool wait)
                services_monitor_wait(service_list);
        }
 
+       if (service_list->io_master != NULL)
+               io_remove(&service_list->io_master);
+       if (service_list->master_fd != -1)
+               i_close_fd(&service_list->master_fd);
+
        array_foreach(&service_list->services, services)
                service_monitor_stop(*services);
 
index 004a03f4b998faefde6fc40c8dbe4ee3745ba299..1df29b002b1e06d510b11bef449bae20aa687bd3 100644 (file)
@@ -137,6 +137,10 @@ struct service_list {
        struct service *log;
        struct service *anvil;
 
+       struct file_listener_settings master_listener_set;
+       struct io *io_master;
+       int master_fd;
+
        /* nonblocking log fds usd by master */
        int master_log_fd[2];
        struct service_process_notify *log_byes;