diff options
Diffstat (limited to 'src/lib-master/ipc-server.c')
-rw-r--r-- | src/lib-master/ipc-server.c | 202 |
1 files changed, 202 insertions, 0 deletions
diff --git a/src/lib-master/ipc-server.c b/src/lib-master/ipc-server.c new file mode 100644 index 0000000..8bd8c23 --- /dev/null +++ b/src/lib-master/ipc-server.c @@ -0,0 +1,202 @@ +/* Copyright (c) 2011-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "ioloop.h" +#include "net.h" +#include "istream.h" +#include "ostream.h" +#include "hostpid.h" +#include "master-service.h" +#include "ipc-server.h" + +#include <unistd.h> + +#define IPC_SERVER_RECONNECT_MSECS (10*1000) +#define IPC_SERVER_PROTOCOL_MAJOR_VERSION 1 +#define IPC_SERVER_PROTOCOL_MINOR_VERSION 0 +#define IPC_SERVER_HANDSHAKE "VERSION\tipc-server\t1\t0\nHANDSHAKE\t%s\t%s\n" + +struct ipc_cmd { + struct ipc_server *server; + unsigned int tag; +}; + +struct ipc_server { + char *name, *path; + ipc_command_callback_t *callback; + + int ipc_cmd_refcount; + + int fd; + struct io *io; + struct timeout *to; + struct istream *input; + struct ostream *output; + + bool version_received:1; +}; + +static void ipc_server_disconnect(struct ipc_server *server); +static void ipc_server_connect(struct ipc_server *server); + +static void ipc_server_input_line(struct ipc_server *server, char *line) +{ + struct ipc_cmd *cmd; + unsigned int tag = 0; + char *p; + + /* tag cmd */ + p = strchr(line, '\t'); + if (p != NULL) { + *p++ = '\0'; + if (str_to_uint(line, &tag) < 0) + p = NULL; + } + if (p == NULL || *p == '\0') { + i_error("IPC proxy sent invalid input: %s", line); + return; + } + + cmd = i_new(struct ipc_cmd, 1); + cmd->server = server; + cmd->tag = tag; + + server->ipc_cmd_refcount++; + T_BEGIN { + server->callback(cmd, p); + } T_END; +} + +static void ipc_server_input(struct ipc_server *server) +{ + char *line; + + if (i_stream_read(server->input) < 0) { + ipc_server_disconnect(server); + ipc_server_connect(server); + return; + } + + if (!server->version_received) { + if ((line = i_stream_next_line(server->input)) == NULL) + return; + + if (!version_string_verify(line, "ipc-proxy", + IPC_SERVER_PROTOCOL_MAJOR_VERSION)) { + i_error("IPC proxy not compatible with this server " + "(mixed old and new binaries?)"); + ipc_server_disconnect(server); + return; + } + server->version_received = TRUE; + } + + while ((line = i_stream_next_line(server->input)) != NULL) + ipc_server_input_line(server, line); +} + +static void ipc_server_connect(struct ipc_server *server) +{ + i_assert(server->fd == -1); + + timeout_remove(&server->to); + + server->fd = net_connect_unix(server->path); + if (server->fd == -1) { + i_error("connect(%s) failed: %m", server->path); + server->to = timeout_add(IPC_SERVER_RECONNECT_MSECS, + ipc_server_connect, server); + return; + } + + server->io = io_add(server->fd, IO_READ, ipc_server_input, server); + server->input = i_stream_create_fd(server->fd, SIZE_MAX); + server->output = o_stream_create_fd(server->fd, SIZE_MAX); + o_stream_set_no_error_handling(server->output, TRUE); + o_stream_nsend_str(server->output, + t_strdup_printf(IPC_SERVER_HANDSHAKE, server->name, my_pid)); + o_stream_cork(server->output); +} + +static void ipc_server_disconnect(struct ipc_server *server) +{ + if (server->fd == -1) + return; + + io_remove(&server->io); + i_stream_destroy(&server->input); + o_stream_destroy(&server->output); + if (close(server->fd) < 0) + i_error("close(%s) failed: %m", server->path); + server->fd = -1; +} + +struct ipc_server * +ipc_server_init(const char *ipc_socket_path, const char *name, + ipc_command_callback_t *callback) +{ + struct ipc_server *server; + + server = i_new(struct ipc_server, 1); + server->name = i_strdup(name); + server->path = i_strdup(ipc_socket_path); + server->callback = callback; + server->fd = -1; + ipc_server_connect(server); + return server; +} + +void ipc_server_deinit(struct ipc_server **_server) +{ + struct ipc_server *server = *_server; + + *_server = NULL; + + i_assert(server->ipc_cmd_refcount == 0); + + ipc_server_disconnect(server); + timeout_remove(&server->to); + i_free(server->name); + i_free(server->path); + i_free(server); +} + +void ipc_cmd_send(struct ipc_cmd *cmd, const char *data) +{ + o_stream_nsend_str(cmd->server->output, + t_strdup_printf("%u\t:%s\n", cmd->tag, data)); +} + +static void ipc_cmd_finish(struct ipc_cmd *cmd, const char *line) +{ + o_stream_nsend_str(cmd->server->output, + t_strdup_printf("%u\t%s\n", cmd->tag, line)); + o_stream_uncork(cmd->server->output); + + i_assert(cmd->server->ipc_cmd_refcount > 0); + cmd->server->ipc_cmd_refcount--; + i_free(cmd); +} + +void ipc_cmd_success(struct ipc_cmd **_cmd) +{ + ipc_cmd_success_reply(_cmd, ""); +} + +void ipc_cmd_success_reply(struct ipc_cmd **_cmd, const char *data) +{ + struct ipc_cmd *cmd = *_cmd; + + *_cmd = NULL; + ipc_cmd_finish(cmd, t_strconcat("+", data, NULL)); +} + +void ipc_cmd_fail(struct ipc_cmd **_cmd, const char *errormsg) +{ + struct ipc_cmd *cmd = *_cmd; + + i_assert(errormsg != NULL); + + *_cmd = NULL; + ipc_cmd_finish(cmd, t_strconcat("-", errormsg, NULL)); +} |