anvil-connection.c revision 5dd73391f9dfb2396cf2060402017f6080c93dc7
7cb128dc4cae2a03a742f63ba7afee23c78e3af0Phil Carmody/* Copyright (C) 2009 Dovecot authors, see the included COPYING file */
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen#include "common.h"
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen#include "llist.h"
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen#include "istream.h"
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen#include "ostream.h"
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen#include "master-service.h"
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen#include "master-interface.h"
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen#include "connect-limit.h"
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen#include "anvil-connection.h"
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen#include <stdlib.h>
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen#include <unistd.h>
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen#define MAX_INBUF_SIZE 1024
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen
8a2401de6de54250ba271bfc53524a847805e8f4Timo Sirainen#define ANVIL_CLIENT_PROTOCOL_MAJOR_VERSION 1
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen#define ANVIL_CLIENT_PROTOCOL_MINOR_VERSION 0
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainenstruct anvil_connection {
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen struct anvil_connection *prev, *next;
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen int fd;
46b0fad7bc31f63f6f969b487bef2178beb125faTimo Sirainen struct istream *input;
8a2401de6de54250ba271bfc53524a847805e8f4Timo Sirainen struct ostream *output;
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen struct io *io;
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen unsigned char *fifo_inbuf;
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen size_t fifo_inbuf_size;
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen unsigned int version_received:1;
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen unsigned int handshaked:1;
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen unsigned int master:1;
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen};
b6c48e71a9413ac90a0414dfeb7d1248c144eeb2Timo Sirainen
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainenstruct anvil_connection *anvil_connections = NULL;
8a2401de6de54250ba271bfc53524a847805e8f4Timo Sirainen
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainenstatic const char *
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainenanvil_connection_fifo_read_line(struct anvil_connection *conn)
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen{
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen ssize_t ret;
b6c48e71a9413ac90a0414dfeb7d1248c144eeb2Timo Sirainen
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen ret = read(conn->fd, conn->fifo_inbuf, conn->fifo_inbuf_size);
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen if (ret > 0) {
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen if (conn->fifo_inbuf[ret-1] != '\n') {
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen i_error("BUG: Client packet didn't end with LF");
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen return NULL;
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen }
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen conn->fifo_inbuf[ret-1] = '\0';
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen return (const char *)conn->fifo_inbuf;
6516e7c2cfb84bbdaff7d748df0a0f1f6f39f75dTimo Sirainen }
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen if (ret == 0) {
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen /* disconnected */
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen } else {
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen if (errno == EAGAIN)
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen return NULL;
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen i_error("read() failed: %m");
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen }
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen anvil_connection_destroy(conn);
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen return NULL;
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen}
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainenstatic const char *const *
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainenanvil_connection_next_line(struct anvil_connection *conn)
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen{
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen const char *line;
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen if (conn->input != NULL)
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen line = i_stream_next_line(conn->input);
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen else
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen line = anvil_connection_fifo_read_line(conn);
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen return line == NULL ? NULL : t_strsplit(line, "\t");
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen}
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainenstatic int
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainenanvil_connection_request(struct anvil_connection *conn,
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen const char *const *args, const char **error_r)
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen{
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen const char *cmd = args[0];
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen unsigned int count;
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen pid_t pid;
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen args++;
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen if (strcmp(cmd, "CONNECT") == 0) {
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen if (args[0] == NULL || args[1] == NULL) {
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen *error_r = "CONNECT: Not enough parameters";
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen return -1;
8a2401de6de54250ba271bfc53524a847805e8f4Timo Sirainen }
8a2401de6de54250ba271bfc53524a847805e8f4Timo Sirainen pid = strtol(args[0], NULL, 10);
d4c3d55021bcbf2b062f4782b1cde9115d35aefcTimo Sirainen connect_limit_connect(connect_limit, pid, args[1]);
d4c3d55021bcbf2b062f4782b1cde9115d35aefcTimo Sirainen return 0;
d4c3d55021bcbf2b062f4782b1cde9115d35aefcTimo Sirainen } else if (strcmp(cmd, "DISCONNECT") == 0) {
d4c3d55021bcbf2b062f4782b1cde9115d35aefcTimo Sirainen if (args[0] == NULL || args[1] == NULL) {
d4c3d55021bcbf2b062f4782b1cde9115d35aefcTimo Sirainen *error_r = "DISCONNECT: Not enough parameters";
d4c3d55021bcbf2b062f4782b1cde9115d35aefcTimo Sirainen return -1;
d4c3d55021bcbf2b062f4782b1cde9115d35aefcTimo Sirainen }
d4c3d55021bcbf2b062f4782b1cde9115d35aefcTimo Sirainen pid = strtol(args[0], NULL, 10);
d4c3d55021bcbf2b062f4782b1cde9115d35aefcTimo Sirainen connect_limit_disconnect(connect_limit, pid, args[1]);
b6c48e71a9413ac90a0414dfeb7d1248c144eeb2Timo Sirainen return 0;
b6c48e71a9413ac90a0414dfeb7d1248c144eeb2Timo Sirainen } else if (strcmp(cmd, "KILL") == 0) {
b6c48e71a9413ac90a0414dfeb7d1248c144eeb2Timo Sirainen if (args[0] == NULL) {
b6c48e71a9413ac90a0414dfeb7d1248c144eeb2Timo Sirainen *error_r = "KILL: Not enough parameters";
b6c48e71a9413ac90a0414dfeb7d1248c144eeb2Timo Sirainen return -1;
b6c48e71a9413ac90a0414dfeb7d1248c144eeb2Timo Sirainen }
b6c48e71a9413ac90a0414dfeb7d1248c144eeb2Timo Sirainen if (!conn->master) {
b6c48e71a9413ac90a0414dfeb7d1248c144eeb2Timo Sirainen *error_r = "KILL sent by a non-master connection";
d4c3d55021bcbf2b062f4782b1cde9115d35aefcTimo Sirainen return -1;
d4c3d55021bcbf2b062f4782b1cde9115d35aefcTimo Sirainen }
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen pid = strtol(args[0], NULL, 10);
d4c3d55021bcbf2b062f4782b1cde9115d35aefcTimo Sirainen connect_limit_disconnect_pid(connect_limit, pid);
d4c3d55021bcbf2b062f4782b1cde9115d35aefcTimo Sirainen return 0;
d4c3d55021bcbf2b062f4782b1cde9115d35aefcTimo Sirainen } else if (strcmp(cmd, "LOOKUP") == 0) {
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen if (args[0] == NULL) {
d4c3d55021bcbf2b062f4782b1cde9115d35aefcTimo Sirainen *error_r = "LOOKUP: Not enough parameters";
8a2401de6de54250ba271bfc53524a847805e8f4Timo Sirainen return -1;
d4c3d55021bcbf2b062f4782b1cde9115d35aefcTimo Sirainen }
8a2401de6de54250ba271bfc53524a847805e8f4Timo Sirainen if (conn->output == NULL) {
8a2401de6de54250ba271bfc53524a847805e8f4Timo Sirainen *error_r = "LOOKUP on a FIFO, can't send reply";
8a2401de6de54250ba271bfc53524a847805e8f4Timo Sirainen return -1;
8a2401de6de54250ba271bfc53524a847805e8f4Timo Sirainen }
d4c3d55021bcbf2b062f4782b1cde9115d35aefcTimo Sirainen count = connect_limit_lookup(connect_limit, args[0]);
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen (void)o_stream_send_str(conn->output,
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen t_strdup_printf("%u\n", count));
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen return 0;
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen } else {
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen *error_r = t_strconcat("Unknown command: ", cmd, NULL);
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen return -1;
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen }
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen}
b6c48e71a9413ac90a0414dfeb7d1248c144eeb2Timo Sirainen
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainenstatic void anvil_connection_input(void *context)
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen{
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen struct anvil_connection *conn = context;
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen const char *const *args, *error;
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen
1183340bf4fda4040268aa4ba7a816b567659c08Timo Sirainen if (conn->input != NULL) {
1183340bf4fda4040268aa4ba7a816b567659c08Timo Sirainen switch (i_stream_read(conn->input)) {
1183340bf4fda4040268aa4ba7a816b567659c08Timo Sirainen case -2:
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen i_error("BUG: Anvil client connection sent too "
1183340bf4fda4040268aa4ba7a816b567659c08Timo Sirainen "much data");
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen anvil_connection_destroy(conn);
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen return;
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen case -1:
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen anvil_connection_destroy(conn);
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen return;
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen }
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen }
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen
46b0fad7bc31f63f6f969b487bef2178beb125faTimo Sirainen if (!conn->version_received) {
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen if ((args = anvil_connection_next_line(conn)) == NULL)
8a2401de6de54250ba271bfc53524a847805e8f4Timo Sirainen return;
46b0fad7bc31f63f6f969b487bef2178beb125faTimo Sirainen
46b0fad7bc31f63f6f969b487bef2178beb125faTimo Sirainen if (str_array_length(args) < 3 ||
46b0fad7bc31f63f6f969b487bef2178beb125faTimo Sirainen strcmp(args[0], "VERSION") != 0 ||
46b0fad7bc31f63f6f969b487bef2178beb125faTimo Sirainen atoi(args[1]) != ANVIL_CLIENT_PROTOCOL_MAJOR_VERSION) {
8a2401de6de54250ba271bfc53524a847805e8f4Timo Sirainen i_error("Anvil client not compatible with this server "
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen "(mixed old and new binaries?)");
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen anvil_connection_destroy(conn);
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen return;
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen }
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen conn->version_received = TRUE;
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen }
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen while ((args = anvil_connection_next_line(conn)) != NULL) {
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen if (args[0] != NULL) {
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen if (anvil_connection_request(conn, args, &error) < 0)
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen i_error("Anvil client input error: %s", error);
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen }
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen }
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen}
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainenstruct anvil_connection *
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainenanvil_connection_create(int fd, bool master, bool fifo)
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen{
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen struct anvil_connection *conn;
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen conn = i_new(struct anvil_connection, 1);
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen conn->fd = fd;
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen if (!fifo) {
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen conn->input = i_stream_create_fd(fd, MAX_INBUF_SIZE, FALSE);
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen conn->output = o_stream_create_fd(fd, (size_t)-1, FALSE);
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen } else {
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen conn->fifo_inbuf_size = MAX_INBUF_SIZE;
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen conn->fifo_inbuf = i_malloc(conn->fifo_inbuf_size);
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen }
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen conn->io = io_add(fd, IO_READ, anvil_connection_input, conn);
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen conn->master = master;
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen DLLIST_PREPEND(&anvil_connections, conn);
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen return conn;
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen}
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainenvoid anvil_connection_destroy(struct anvil_connection *conn)
46b0fad7bc31f63f6f969b487bef2178beb125faTimo Sirainen{
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen DLLIST_REMOVE(&anvil_connections, conn);
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen io_remove(&conn->io);
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen if (conn->input != NULL)
b6c48e71a9413ac90a0414dfeb7d1248c144eeb2Timo Sirainen i_stream_destroy(&conn->input);
b6c48e71a9413ac90a0414dfeb7d1248c144eeb2Timo Sirainen if (conn->output != NULL)
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen o_stream_destroy(&conn->output);
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen if (close(conn->fd) < 0)
8a2401de6de54250ba271bfc53524a847805e8f4Timo Sirainen i_error("close(anvil conn) failed: %m");
46b0fad7bc31f63f6f969b487bef2178beb125faTimo Sirainen i_free(conn->fifo_inbuf);
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen i_free(conn);
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen master_service_client_connection_destroyed(master_service);
8a2401de6de54250ba271bfc53524a847805e8f4Timo Sirainen}
46b0fad7bc31f63f6f969b487bef2178beb125faTimo Sirainen
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainenvoid anvil_connections_destroy_all(void)
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen{
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen while (anvil_connections != NULL)
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen anvil_connection_destroy(anvil_connections);
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen}
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen