dovecot-2.0: Added support for a simplified IPC infrastructure.

dovecot at dovecot.org dovecot at dovecot.org
Fri May 20 18:48:19 EEST 2011


details:   http://hg.dovecot.org/dovecot-2.0/rev/bf5c8ee58e5e
changeset: 12818:bf5c8ee58e5e
user:      Timo Sirainen <tss at iki.fi>
date:      Fri May 20 18:45:29 2011 +0300
description:
Added support for a simplified IPC infrastructure.
The idea is that you have one "ipc" proxy process, where all server
processes connect to. IPC clients can then connect to the proxy and ask it
to forward commands to either a specific server or all servers. The proxy
does this, and forwards back any replies from the server.

diffstat:

 src/Makefile.am             |    1 +
 src/ipc/Makefile.am         |   25 ++++
 src/ipc/client.c            |  150 +++++++++++++++++++++++++++
 src/ipc/client.h            |    9 +
 src/ipc/ipc-connection.c    |  242 ++++++++++++++++++++++++++++++++++++++++++++
 src/ipc/ipc-connection.h    |   45 ++++++++
 src/ipc/ipc-group.c         |  160 +++++++++++++++++++++++++++++
 src/ipc/ipc-group.h         |   43 +++++++
 src/ipc/ipc-settings.c      |   48 ++++++++
 src/ipc/main.c              |   66 ++++++++++++
 src/lib-master/Makefile.am  |    4 +
 src/lib-master/ipc-client.c |  163 +++++++++++++++++++++++++++++
 src/lib-master/ipc-client.h |   20 +++
 src/lib-master/ipc-server.c |  197 +++++++++++++++++++++++++++++++++++
 src/lib-master/ipc-server.h |   20 +++
 15 files changed, 1193 insertions(+), 0 deletions(-)

diffs (truncated from 1272 to 300 lines):

diff -r c67ba5bf1ba9 -r bf5c8ee58e5e src/Makefile.am
--- a/src/Makefile.am	Fri May 20 14:21:51 2011 +0300
+++ b/src/Makefile.am	Fri May 20 18:45:29 2011 +0300
@@ -24,6 +24,7 @@
 	auth \
 	dict \
 	dns \
+	ipc \
 	master \
 	login-common \
 	imap-login \
diff -r c67ba5bf1ba9 -r bf5c8ee58e5e src/ipc/Makefile.am
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ipc/Makefile.am	Fri May 20 18:45:29 2011 +0300
@@ -0,0 +1,25 @@
+pkglibexecdir = $(libexecdir)/dovecot
+
+pkglibexec_PROGRAMS = ipc
+
+AM_CPPFLAGS = \
+	-I$(top_srcdir)/src/lib \
+	-I$(top_srcdir)/src/lib-settings \
+	-I$(top_srcdir)/src/lib-master
+
+ipc_LDADD = \
+	$(LIBDOVECOT) \
+	$(MODULE_LIBS)
+ipc_DEPENDENCIES = $(LIBDOVECOT_DEPS)
+
+ipc_SOURCES = \
+	client.c \
+	main.c \
+	ipc-connection.c \
+	ipc-group.c \
+	ipc-settings.c
+
+noinst_HEADERS = \
+	client.h \
+	ipc-connection.h \
+	ipc-group.h
diff -r c67ba5bf1ba9 -r bf5c8ee58e5e src/ipc/client.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ipc/client.c	Fri May 20 18:45:29 2011 +0300
@@ -0,0 +1,150 @@
+/* Copyright (c) 2011 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "llist.h"
+#include "ioloop.h"
+#include "istream.h"
+#include "ostream.h"
+#include "master-service.h"
+#include "ipc-group.h"
+#include "ipc-connection.h"
+#include "client.h"
+
+#include <unistd.h>
+
+struct client {
+	struct client *prev, *next;
+
+	int fd;
+	struct io *io;
+	struct istream *input;
+	struct ostream *output;
+};
+
+static struct client *clients;
+
+static void client_input(struct client *client);
+
+static void
+client_cmd_input(enum ipc_cmd_status status, const char *line, void *context)
+{
+	struct client *client = context;
+	char chr = '\0';
+
+	switch (status) {
+	case IPC_CMD_STATUS_REPLY:
+		chr = ':';
+		break;
+	case IPC_CMD_STATUS_OK:
+		chr = '+';
+		break;
+	case IPC_CMD_STATUS_ERROR:
+		chr = '-';
+		break;
+	}
+
+	T_BEGIN {
+		o_stream_send_str(client->output,
+				  t_strdup_printf("%c%s\n", chr, line));
+	} T_END;
+
+	if (status != IPC_CMD_STATUS_REPLY) {
+		i_assert(client->io == NULL);
+		client->io = io_add(client->fd, IO_READ, client_input, client);
+		client_input(client);
+	}
+}
+
+static void client_input(struct client *client)
+{
+	struct ipc_group *group;
+	struct ipc_connection *conn;
+	char *line, *id, *data;
+	unsigned int id_num;
+
+	while ((line = i_stream_read_next_line(client->input)) != NULL) {
+		/* <ipc name> *|<id> <command> */
+		id = strchr(line, '\t');
+		if (id == NULL)
+			data = NULL;
+		else {
+			*id++ = '\0';
+			data = strchr(id, '\t');
+		}
+		if (data == NULL || data[1] == '\0') {
+			o_stream_send_str(client->output, "-Invalid input\n");
+			continue;
+		}
+		*data++ = '\0';
+
+		group = ipc_group_lookup_name(line);
+		if (group == NULL) {
+			o_stream_send_str(client->output,
+				t_strdup_printf("-Unknown IPC group: %s\n", line));
+			continue;
+		}
+
+		if (strcmp(id, "*") == 0) {
+			/* send to everyone */
+			ipc_group_cmd(group, data, client_cmd_input, client);
+		} else if (str_to_uint(id, &id_num) < 0) {
+			o_stream_send_str(client->output,
+				t_strdup_printf("-Invalid IPC connection id: %s\n", id));
+			continue;
+		} else if ((conn = ipc_connection_lookup_id(group, id_num)) == NULL) {
+			o_stream_send_str(client->output,
+				t_strdup_printf("-Unknown IPC connection id: %u\n", id_num));
+			continue;
+		} else {
+			ipc_connection_cmd(conn, data, client_cmd_input, client);
+		}
+
+		/* we'll handle commands one at a time. stop reading input
+		   until this command is finished. */
+		io_remove(&client->io);
+		break;
+	}
+	if (client->input->eof || client->input->stream_errno != 0)
+		client_destroy(&client);
+}
+
+struct client *client_create(int fd)
+{
+	struct client *client;
+
+	client = i_new(struct client, 1);
+	client->fd = fd;
+	client->io = io_add(fd, IO_READ, client_input, client);
+	client->input = i_stream_create_fd(fd, (size_t)-1, FALSE);
+	client->output = o_stream_create_fd(fd, (size_t)-1, FALSE);
+
+	DLLIST_PREPEND(&clients, client);
+	return client;
+}
+
+void client_destroy(struct client **_client)
+{
+	struct client *client = *_client;
+
+	*_client = NULL;
+
+	DLLIST_REMOVE(&clients, client);
+	if (client->io != NULL)
+		io_remove(&client->io);
+	i_stream_destroy(&client->input);
+	o_stream_destroy(&client->output);
+	if (close(client->fd) < 0)
+		i_error("close(client) failed: %m");
+	i_free(client);
+
+	master_service_client_connection_destroyed(master_service);
+}
+
+void clients_destroy_all(void)
+{
+	while (clients != NULL) {
+		struct client *client = clients;
+
+		client_destroy(&client);
+	}
+}
diff -r c67ba5bf1ba9 -r bf5c8ee58e5e src/ipc/client.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ipc/client.h	Fri May 20 18:45:29 2011 +0300
@@ -0,0 +1,9 @@
+#ifndef CLIENT_H
+#define CLIENT_H
+
+struct client *client_create(int fd);
+void client_destroy(struct client **client);
+
+void clients_destroy_all(void);
+
+#endif
diff -r c67ba5bf1ba9 -r bf5c8ee58e5e src/ipc/ipc-connection.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ipc/ipc-connection.c	Fri May 20 18:45:29 2011 +0300
@@ -0,0 +1,242 @@
+/* Copyright (c) 2011 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "array.h"
+#include "ioloop.h"
+#include "istream.h"
+#include "ostream.h"
+#include "llist.h"
+#include "master-service.h"
+#include "ipc-group.h"
+#include "ipc-connection.h"
+
+#include <unistd.h>
+
+#define IPC_SERVER_PROTOCOL_MAJOR_VERSION 1
+#define IPC_SERVER_PROTOCOL_MINOR_VERSION 0
+
+#define IPC_SERVER_HANDSHAKE "VERSION\tipc-proxy\t1\t0\n"
+
+static unsigned int connection_id_counter;
+static void ipc_connection_cmd_free(struct ipc_connection_cmd **cmd);
+
+static void ipc_connection_cmd_free(struct ipc_connection_cmd **_cmd)
+{
+	struct ipc_connection_cmd *cmd = *_cmd;
+	struct ipc_connection_cmd **cmds;
+	unsigned int i, count;
+
+	*_cmd = NULL;
+
+	cmds = array_get_modifiable(&cmd->conn->cmds, &count);
+	for (i = 0; i < count; i++) {
+		if (cmds[i] == cmd) {
+			array_delete(&cmd->conn->cmds, i, 1);
+			break;
+		}
+	}
+	if (cmd->callback != NULL) {
+		cmd->callback(IPC_CMD_STATUS_ERROR,
+			      "Connection to server died", cmd->context);
+	}
+	i_free(cmd);
+}
+
+static struct ipc_connection_cmd *
+ipc_connection_cmd_find(struct ipc_connection *conn, unsigned int tag)
+{
+	struct ipc_connection_cmd *const *cmdp;
+
+	array_foreach(&conn->cmds, cmdp) {
+		if ((*cmdp)->tag == tag)
+			return *cmdp;
+	}
+	return NULL;
+}
+
+static int
+ipc_connection_input_line(struct ipc_connection *conn, char *line)
+{
+	struct ipc_connection_cmd *cmd;
+	unsigned int tag;
+	enum ipc_cmd_status status;
+	char *data;
+
+	/* <tag> [:+-]<data> */
+	data = strchr(line, '\t');
+	if (data == NULL)
+		return -1;
+
+	*data++ = '\0';
+	if (str_to_uint(line, &tag) < 0)
+		return -1;
+
+	switch (data[0]) {
+	case ':':
+		status = IPC_CMD_STATUS_REPLY;
+		break;
+	case '+':
+		status = IPC_CMD_STATUS_OK;
+		break;
+	case '-':
+		status = IPC_CMD_STATUS_ERROR;
+		break;
+	default:
+		return -1;
+	}
+	data++;
+
+	cmd = ipc_connection_cmd_find(conn, tag);


More information about the dovecot-cvs mailing list