bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2011-2018 Dovecot authors, see the included COPYING file */
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen#include "lib.h"
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen#include "llist.h"
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen#include "ioloop.h"
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen#include "istream.h"
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen#include "ostream.h"
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen#include "master-service.h"
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen#include "ipc-group.h"
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen#include "ipc-connection.h"
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen#include "client.h"
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen#include <unistd.h>
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainenstruct client {
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen struct client *prev, *next;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen int fd;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen struct io *io;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen struct istream *input;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen struct ostream *output;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen};
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainenstatic struct client *clients;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainenstatic void client_input(struct client *client);
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainenstatic void
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainenclient_cmd_input(enum ipc_cmd_status status, const char *line, void *context)
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen{
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen struct client *client = context;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen char chr = '\0';
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen switch (status) {
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen case IPC_CMD_STATUS_REPLY:
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen chr = ':';
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen break;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen case IPC_CMD_STATUS_OK:
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen chr = '+';
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen break;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen case IPC_CMD_STATUS_ERROR:
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen chr = '-';
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen break;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen }
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen T_BEGIN {
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen o_stream_nsend_str(client->output,
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen t_strdup_printf("%c%s\n", chr, line));
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen } T_END;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
eef20a55e19a239244b14963d716cf0d070fe1bbTimo Sirainen if (status != IPC_CMD_STATUS_REPLY && client->io == NULL) {
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen client->io = io_add(client->fd, IO_READ, client_input, client);
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen client_input(client);
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen }
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen}
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainenstatic void client_input(struct client *client)
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen{
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen struct ipc_group *group;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen struct ipc_connection *conn;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen char *line, *id, *data;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen unsigned int id_num;
eef20a55e19a239244b14963d716cf0d070fe1bbTimo Sirainen bool ret;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen while ((line = i_stream_read_next_line(client->input)) != NULL) {
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen /* <ipc name> *|<id> <command> */
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen id = strchr(line, '\t');
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen if (id == NULL)
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen data = NULL;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen else {
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen *id++ = '\0';
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen data = strchr(id, '\t');
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen }
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen if (data == NULL || data[1] == '\0') {
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen o_stream_nsend_str(client->output, "-Invalid input\n");
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen continue;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen }
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen *data++ = '\0';
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen group = ipc_group_lookup_name(line);
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
eef20a55e19a239244b14963d716cf0d070fe1bbTimo Sirainen ret = FALSE;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen if (strcmp(id, "*") == 0) {
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen /* send to everyone */
eef20a55e19a239244b14963d716cf0d070fe1bbTimo Sirainen if (group == NULL) {
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen client_cmd_input(IPC_CMD_STATUS_OK, "", client);
eef20a55e19a239244b14963d716cf0d070fe1bbTimo Sirainen } else {
eef20a55e19a239244b14963d716cf0d070fe1bbTimo Sirainen ret = ipc_group_cmd(group, data,
eef20a55e19a239244b14963d716cf0d070fe1bbTimo Sirainen client_cmd_input, client);
eef20a55e19a239244b14963d716cf0d070fe1bbTimo Sirainen }
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen } else if (str_to_uint(id, &id_num) < 0) {
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen o_stream_nsend_str(client->output,
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen t_strdup_printf("-Invalid IPC connection id: %s\n", id));
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen continue;
eef20a55e19a239244b14963d716cf0d070fe1bbTimo Sirainen } else if (group == NULL) {
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen o_stream_nsend_str(client->output,
eef20a55e19a239244b14963d716cf0d070fe1bbTimo Sirainen t_strdup_printf("-Unknown IPC group: %s\n", line));
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen } else if ((conn = ipc_connection_lookup_id(group, id_num)) == NULL) {
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen o_stream_nsend_str(client->output,
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen t_strdup_printf("-Unknown IPC connection id: %u\n", id_num));
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen continue;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen } else {
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen ipc_connection_cmd(conn, data, client_cmd_input, client);
eef20a55e19a239244b14963d716cf0d070fe1bbTimo Sirainen ret = TRUE;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen }
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
eef20a55e19a239244b14963d716cf0d070fe1bbTimo Sirainen if (ret) {
eef20a55e19a239244b14963d716cf0d070fe1bbTimo Sirainen /* we'll handle commands one at a time. stop reading
eef20a55e19a239244b14963d716cf0d070fe1bbTimo Sirainen input until this command is finished. */
eef20a55e19a239244b14963d716cf0d070fe1bbTimo Sirainen io_remove(&client->io);
eef20a55e19a239244b14963d716cf0d070fe1bbTimo Sirainen break;
eef20a55e19a239244b14963d716cf0d070fe1bbTimo Sirainen }
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen }
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen if (client->input->eof || client->input->stream_errno != 0)
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen client_destroy(&client);
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen}
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainenstruct client *client_create(int fd)
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen{
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen struct client *client;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen client = i_new(struct client, 1);
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen client->fd = fd;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen client->io = io_add(fd, IO_READ, client_input, client);
e93184a9055c2530366dfe617e07199603c399ddMartti Rannanjärvi client->input = i_stream_create_fd(fd, (size_t)-1);
e93184a9055c2530366dfe617e07199603c399ddMartti Rannanjärvi client->output = o_stream_create_fd(fd, (size_t)-1);
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen o_stream_set_no_error_handling(client->output, TRUE);
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen DLLIST_PREPEND(&clients, client);
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen return client;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen}
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainenvoid client_destroy(struct client **_client)
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen{
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen struct client *client = *_client;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen *_client = NULL;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen DLLIST_REMOVE(&clients, client);
5f1d689131a75c39f064cbd4202373e7edf78f18Josef 'Jeff' Sipek 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(client) failed: %m");
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen i_free(client);
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen master_service_client_connection_destroyed(master_service);
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen}
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainenvoid clients_destroy_all(void)
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen{
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen while (clients != NULL) {
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen struct client *client = clients;
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen client_destroy(&client);
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen }
83942ac160cdfb922c3a2f29ddfae2a13ebf8b5dTimo Sirainen}