From: Timo Sirainen Date: Sun, 26 Jun 2016 20:04:00 +0000 (+0300) Subject: master: Allow stopping specific services via master socket. X-Git-Tag: 2.3.0.rc1~3380 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=164f8e81a078c7b8f679fdd04892ffc9ba14bfa0;p=thirdparty%2Fdovecot%2Fcore.git master: Allow stopping specific services via master socket. --- diff --git a/src/master/Makefile.am b/src/master/Makefile.am index 9d3ad7844f..ef7955ff31 100644 --- a/src/master/Makefile.am +++ b/src/master/Makefile.am @@ -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 \ diff --git a/src/master/main.c b/src/master/main.c index 533d4d8056..ab67dc4dd3 100644 --- a/src/master/main.c +++ b/src/master/main.c @@ -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 index 0000000000..32cfbc3eb4 --- /dev/null +++ b/src/master/master-client.c @@ -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 index 0000000000..7e62edee8a --- /dev/null +++ b/src/master/master-client.h @@ -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 diff --git a/src/master/service-listen.c b/src/master/service-listen.c index ca5525312f..1d3e399a7e 100644 --- a/src/master/service-listen.c +++ b/src/master/service-listen.c @@ -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) diff --git a/src/master/service-monitor.c b/src/master/service-monitor.c index a90c4ebe8c..19655c2c39 100644 --- a/src/master/service-monitor.c +++ b/src/master/service-monitor.c @@ -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); diff --git a/src/master/service.h b/src/master/service.h index 004a03f4b9..1df29b002b 100644 --- a/src/master/service.h +++ b/src/master/service.h @@ -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;