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