bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2011-2018 Dovecot authors, see the included COPYING file */
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen#include "lib.h"
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen#include "array.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-client.h"
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen#include <unistd.h>
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainenstruct ipc_client_cmd {
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen ipc_client_callback_t *callback;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen void *context;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen};
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainenstruct ipc_client {
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen char *path;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen ipc_client_callback_t *callback;
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;
4ee00532a265bdfb38539d811fcd12d51210ac35Timo Sirainen ARRAY(struct ipc_client_cmd) cmds;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen};
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainenstatic void ipc_client_disconnect(struct ipc_client *client);
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainenstatic void ipc_client_input_line(struct ipc_client *client, const char *line)
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen{
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen const struct ipc_client_cmd *cmds;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen unsigned int count;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen enum ipc_client_cmd_state state;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen cmds = array_get(&client->cmds, &count);
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen if (count == 0) {
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen i_error("IPC proxy sent unexpected input: %s", line);
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen return;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen }
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen switch (*line++) {
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen case ':':
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen state = IPC_CLIENT_CMD_STATE_REPLY;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen break;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen case '+':
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen state = IPC_CLIENT_CMD_STATE_OK;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen break;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen case '-':
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen state = IPC_CLIENT_CMD_STATE_ERROR;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen break;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen default:
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen i_error("IPC proxy sent invalid input: %s", line);
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen line = "Invalid input";
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen ipc_client_disconnect(client);
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen state = IPC_CLIENT_CMD_STATE_ERROR;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen break;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen }
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen cmds[0].callback(state, line, cmds[0].context);
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen if (state != IPC_CLIENT_CMD_STATE_REPLY)
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen array_delete(&client->cmds, 0, 1);
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen}
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainenstatic void ipc_client_input(struct ipc_client *client)
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen{
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen const char *line;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen if (i_stream_read(client->input) < 0) {
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen ipc_client_disconnect(client);
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen return;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen }
8a1e199862549ee3092db0de2f78c3a48c3ab1dbTimo Sirainen while ((line = i_stream_next_line(client->input)) != NULL) T_BEGIN {
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen ipc_client_input_line(client, line);
8a1e199862549ee3092db0de2f78c3a48c3ab1dbTimo Sirainen } T_END;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen}
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainenstatic int ipc_client_connect(struct ipc_client *client)
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen{
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen if (client->fd != -1)
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen return 0;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen client->fd = net_connect_unix(client->path);
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen if (client->fd == -1) {
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen i_error("connect(%s) failed: %m", client->path);
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen return -1;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen }
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen client->io = io_add(client->fd, IO_READ, ipc_client_input, client);
e93184a9055c2530366dfe617e07199603c399ddMartti Rannanjärvi client->input = i_stream_create_fd(client->fd, (size_t)-1);
e93184a9055c2530366dfe617e07199603c399ddMartti Rannanjärvi client->output = o_stream_create_fd(client->fd, (size_t)-1);
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen o_stream_set_no_error_handling(client->output, TRUE);
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen return 0;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen}
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainenstatic void ipc_client_disconnect(struct ipc_client *client)
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen{
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen const struct ipc_client_cmd *cmd;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen if (client->fd == -1)
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen return;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen array_foreach(&client->cmds, cmd) {
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen cmd->callback(IPC_CLIENT_CMD_STATE_ERROR,
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen "Disconnected", cmd->context);
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen }
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen array_clear(&client->cmds);
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen io_remove(&client->io);
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen i_stream_destroy(&client->input);
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen o_stream_destroy(&client->output);
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen if (close(client->fd) < 0)
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen i_error("close(%s) failed: %m", client->path);
3c9c62c4abac1d0fb26422160ff12441a0217edaTimo Sirainen client->fd = -1;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen}
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainenstruct ipc_client *
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainenipc_client_init(const char *ipc_socket_path)
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen{
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen struct ipc_client *client;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen client = i_new(struct ipc_client, 1);
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen client->path = i_strdup(ipc_socket_path);
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen client->fd = -1;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen i_array_init(&client->cmds, 8);
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen return client;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen}
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainenvoid ipc_client_deinit(struct ipc_client **_client)
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen{
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen struct ipc_client *client = *_client;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen *_client = NULL;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen ipc_client_disconnect(client);
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen array_free(&client->cmds);
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen i_free(client->path);
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen i_free(client);
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen}
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainenvoid ipc_client_cmd(struct ipc_client *client, const char *cmd,
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen ipc_client_callback_t *callback, void *context)
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen{
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen struct ipc_client_cmd *ipc_cmd;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen struct const_iovec iov[2];
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen if (ipc_client_connect(client) < 0) {
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen callback(IPC_CLIENT_CMD_STATE_ERROR,
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen "ipc connect failed", context);
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen return;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen }
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen iov[0].iov_base = cmd;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen iov[0].iov_len = strlen(cmd);
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen iov[1].iov_base = "\n";
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen iov[1].iov_len = 1;
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen o_stream_nsendv(client->output, iov, N_ELEMENTS(iov));
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen ipc_cmd = array_append_space(&client->cmds);
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen ipc_cmd->callback = callback;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen ipc_cmd->context = context;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen}