bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2011-2018 Dovecot authors, see the included COPYING file */
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen#include "lib.h"
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen#include "ioloop.h"
bdd36cfdba3ff66d25570a9ff568d69e1eb543cfTimo Sirainen#include "net.h"
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen#include "istream.h"
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen#include "ostream.h"
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen#include "hostpid.h"
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen#include "master-service.h"
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen#include "ipc-server.h"
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen#include <unistd.h>
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen#define IPC_SERVER_RECONNECT_MSECS (60*1000)
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen#define IPC_SERVER_PROTOCOL_MAJOR_VERSION 1
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen#define IPC_SERVER_PROTOCOL_MINOR_VERSION 0
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen#define IPC_SERVER_HANDSHAKE "VERSION\tipc-server\t1\t0\nHANDSHAKE\t%s\t%s\n"
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainenstruct ipc_cmd {
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen struct ipc_server *server;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen unsigned int tag;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen};
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainenstruct ipc_server {
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen char *name, *path;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen ipc_command_callback_t *callback;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen int ipc_cmd_refcount;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen int fd;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen struct io *io;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen struct timeout *to;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen struct istream *input;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen struct ostream *output;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen bool version_received:1;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen};
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainenstatic void ipc_server_disconnect(struct ipc_server *server);
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainenstatic void ipc_server_input_line(struct ipc_server *server, char *line)
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen{
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen struct ipc_cmd *cmd;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen unsigned int tag = 0;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen char *p;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen /* tag cmd */
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen p = strchr(line, '\t');
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen if (p != NULL) {
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen *p++ = '\0';
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen if (str_to_uint(line, &tag) < 0)
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen p = NULL;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen }
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen if (p == NULL || *p == '\0') {
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen i_error("IPC proxy sent invalid input: %s", line);
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen return;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen }
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen cmd = i_new(struct ipc_cmd, 1);
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen cmd->server = server;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen cmd->tag = tag;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen server->ipc_cmd_refcount++;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen T_BEGIN {
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen server->callback(cmd, p);
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen } T_END;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen}
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainenstatic void ipc_server_input(struct ipc_server *server)
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen{
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen char *line;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen if (i_stream_read(server->input) < 0) {
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen ipc_server_disconnect(server);
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen return;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen }
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen if (!server->version_received) {
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen if ((line = i_stream_next_line(server->input)) == NULL)
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen return;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen if (!version_string_verify(line, "ipc-proxy",
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen IPC_SERVER_PROTOCOL_MAJOR_VERSION)) {
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen i_error("IPC proxy not compatible with this server "
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen "(mixed old and new binaries?)");
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen ipc_server_disconnect(server);
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen return;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen }
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen server->version_received = TRUE;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen }
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen while ((line = i_stream_next_line(server->input)) != NULL)
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen ipc_server_input_line(server, line);
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen}
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainenstatic void ipc_server_connect(struct ipc_server *server)
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen{
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen i_assert(server->fd == -1);
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
0d1b8b6bec79746c5d89d57dd8c1688946bd9237Josef 'Jeff' Sipek timeout_remove(&server->to);
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen server->fd = net_connect_unix(server->path);
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen if (server->fd == -1) {
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen i_error("connect(%s) failed: %m", server->path);
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen server->to = timeout_add(IPC_SERVER_RECONNECT_MSECS,
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen ipc_server_connect, server);
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen return;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen }
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen server->io = io_add(server->fd, IO_READ, ipc_server_input, server);
e93184a9055c2530366dfe617e07199603c399ddMartti Rannanjärvi server->input = i_stream_create_fd(server->fd, (size_t)-1);
e93184a9055c2530366dfe617e07199603c399ddMartti Rannanjärvi server->output = o_stream_create_fd(server->fd, (size_t)-1);
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen o_stream_set_no_error_handling(server->output, TRUE);
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen o_stream_nsend_str(server->output,
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen t_strdup_printf(IPC_SERVER_HANDSHAKE, server->name, my_pid));
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen o_stream_cork(server->output);
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen}
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainenstatic void ipc_server_disconnect(struct ipc_server *server)
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen{
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen if (server->fd == -1)
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen return;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen io_remove(&server->io);
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen i_stream_destroy(&server->input);
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen o_stream_destroy(&server->output);
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen if (close(server->fd) < 0)
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen i_error("close(%s) failed: %m", server->path);
d5d2eb556d8f4794cbab46ee3e5e05b9351508acTimo Sirainen server->fd = -1;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen}
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainenstruct ipc_server *
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainenipc_server_init(const char *ipc_socket_path, const char *name,
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen ipc_command_callback_t *callback)
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen{
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen struct ipc_server *server;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen server = i_new(struct ipc_server, 1);
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen server->name = i_strdup(name);
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen server->path = i_strdup(ipc_socket_path);
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen server->callback = callback;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen server->fd = -1;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen ipc_server_connect(server);
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen return server;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen}
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainenvoid ipc_server_deinit(struct ipc_server **_server)
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen{
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen struct ipc_server *server = *_server;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen *_server = NULL;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen i_assert(server->ipc_cmd_refcount == 0);
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen ipc_server_disconnect(server);
0d1b8b6bec79746c5d89d57dd8c1688946bd9237Josef 'Jeff' Sipek timeout_remove(&server->to);
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen i_free(server->name);
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen i_free(server->path);
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen i_free(server);
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen}
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainenvoid ipc_cmd_send(struct ipc_cmd *cmd, const char *data)
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen{
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen o_stream_nsend_str(cmd->server->output,
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen t_strdup_printf("%u\t:%s\n", cmd->tag, data));
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen}
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainenstatic void ipc_cmd_finish(struct ipc_cmd *cmd, const char *line)
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen{
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen o_stream_nsend_str(cmd->server->output,
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen t_strdup_printf("%u\t%s\n", cmd->tag, line));
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen o_stream_uncork(cmd->server->output);
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen i_assert(cmd->server->ipc_cmd_refcount > 0);
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen cmd->server->ipc_cmd_refcount--;
d4361f37b24057f615db6fc55aca0dc2b5d7444eTimo Sirainen i_free(cmd);
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen}
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainenvoid ipc_cmd_success(struct ipc_cmd **_cmd)
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen{
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen ipc_cmd_success_reply(_cmd, "");
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen}
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainenvoid ipc_cmd_success_reply(struct ipc_cmd **_cmd, const char *data)
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen{
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen struct ipc_cmd *cmd = *_cmd;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen *_cmd = NULL;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen ipc_cmd_finish(cmd, t_strconcat("+", data, NULL));
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen}
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainenvoid ipc_cmd_fail(struct ipc_cmd **_cmd, const char *errormsg)
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen{
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen struct ipc_cmd *cmd = *_cmd;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen i_assert(errormsg != NULL);
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen *_cmd = NULL;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen ipc_cmd_finish(cmd, t_strconcat("-", errormsg, NULL));
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen}