indexer-client.c revision a8c5a86d183db25a57bf193c06b41e092ec2e151
0371406d952fe51367c7be91703e5634b7d9d225Timo Sirainen/* Copyright (c) 2011-2014 Dovecot authors, see the included COPYING file */
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen#include "lib.h"
0371406d952fe51367c7be91703e5634b7d9d225Timo Sirainen#include "llist.h"
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen#include "istream.h"
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen#include "ostream.h"
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen#include "strescape.h"
0371406d952fe51367c7be91703e5634b7d9d225Timo Sirainen#include "master-service.h"
0371406d952fe51367c7be91703e5634b7d9d225Timo Sirainen#include "indexer-queue.h"
0371406d952fe51367c7be91703e5634b7d9d225Timo Sirainen#include "indexer-client.h"
0371406d952fe51367c7be91703e5634b7d9d225Timo Sirainen
0371406d952fe51367c7be91703e5634b7d9d225Timo Sirainen#include <stdlib.h>
0371406d952fe51367c7be91703e5634b7d9d225Timo Sirainen#include <unistd.h>
0371406d952fe51367c7be91703e5634b7d9d225Timo Sirainen
0371406d952fe51367c7be91703e5634b7d9d225Timo Sirainen#define MAX_INBUF_SIZE (1024*64)
0371406d952fe51367c7be91703e5634b7d9d225Timo Sirainen
0371406d952fe51367c7be91703e5634b7d9d225Timo Sirainen#define INDEXER_CLIENT_PROTOCOL_MAJOR_VERSION 1
0371406d952fe51367c7be91703e5634b7d9d225Timo Sirainen#define INDEXER_CLIENT_PROTOCOL_MINOR_VERSION 0
0371406d952fe51367c7be91703e5634b7d9d225Timo Sirainen
0371406d952fe51367c7be91703e5634b7d9d225Timo Sirainenstruct indexer_client {
0371406d952fe51367c7be91703e5634b7d9d225Timo Sirainen struct indexer_client *prev, *next;
0371406d952fe51367c7be91703e5634b7d9d225Timo Sirainen
0371406d952fe51367c7be91703e5634b7d9d225Timo Sirainen int refcount;
0371406d952fe51367c7be91703e5634b7d9d225Timo Sirainen struct indexer_queue *queue;
0371406d952fe51367c7be91703e5634b7d9d225Timo Sirainen
0371406d952fe51367c7be91703e5634b7d9d225Timo Sirainen int fd;
0371406d952fe51367c7be91703e5634b7d9d225Timo Sirainen struct istream *input;
0371406d952fe51367c7be91703e5634b7d9d225Timo Sirainen struct ostream *output;
0371406d952fe51367c7be91703e5634b7d9d225Timo Sirainen struct io *io;
0371406d952fe51367c7be91703e5634b7d9d225Timo Sirainen
0371406d952fe51367c7be91703e5634b7d9d225Timo Sirainen unsigned int version_received:1;
0371406d952fe51367c7be91703e5634b7d9d225Timo Sirainen unsigned int handshaked:1;
0371406d952fe51367c7be91703e5634b7d9d225Timo Sirainen unsigned int destroyed:1;
0371406d952fe51367c7be91703e5634b7d9d225Timo Sirainen};
0371406d952fe51367c7be91703e5634b7d9d225Timo Sirainen
0371406d952fe51367c7be91703e5634b7d9d225Timo Sirainenstruct indexer_client_request {
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen struct indexer_client *client;
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen unsigned int tag;
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen};
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen
0371406d952fe51367c7be91703e5634b7d9d225Timo Sirainenstruct indexer_client *clients = NULL;
0371406d952fe51367c7be91703e5634b7d9d225Timo Sirainenstatic unsigned int clients_count = 0;
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen
0371406d952fe51367c7be91703e5634b7d9d225Timo Sirainenstatic void indexer_client_destroy(struct indexer_client *client);
0371406d952fe51367c7be91703e5634b7d9d225Timo Sirainenstatic void indexer_client_ref(struct indexer_client *client);
0371406d952fe51367c7be91703e5634b7d9d225Timo Sirainenstatic void indexer_client_unref(struct indexer_client *client);
0371406d952fe51367c7be91703e5634b7d9d225Timo Sirainen
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainenstatic const char *const*
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenindexer_client_next_line(struct indexer_client *client)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen{
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen const char *line;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen char **args;
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen unsigned int i;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen line = i_stream_next_line(client->input);
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen if (line == NULL)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen return NULL;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
a0b89f3b1df99b3a32f44623f13ad1893118825bTimo Sirainen args = p_strsplit(pool_datastack_create(), line, "\t");
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen for (i = 0; args[i] != NULL; i++)
a0b89f3b1df99b3a32f44623f13ad1893118825bTimo Sirainen args[i] = str_tabunescape(args[i]);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen return (void *)args;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen}
a0b89f3b1df99b3a32f44623f13ad1893118825bTimo Sirainen
a488bbb7e5d7cc537e7af21d1f651762a2b8bf56Timo Sirainenstatic int
a0b89f3b1df99b3a32f44623f13ad1893118825bTimo Sirainenindexer_client_request_queue(struct indexer_client *client, bool append,
a0b89f3b1df99b3a32f44623f13ad1893118825bTimo Sirainen const char *const *args, const char **error_r)
a0b89f3b1df99b3a32f44623f13ad1893118825bTimo Sirainen{
24ce0c343cefe54af841871fa39dbc3464028b06Timo Sirainen struct indexer_client_request *ctx = NULL;
24ce0c343cefe54af841871fa39dbc3464028b06Timo Sirainen unsigned int tag, max_recent_msgs;
24ce0c343cefe54af841871fa39dbc3464028b06Timo Sirainen
24ce0c343cefe54af841871fa39dbc3464028b06Timo Sirainen /* <tag> <user> <mailbox> [<max_recent_msgs>] */
24ce0c343cefe54af841871fa39dbc3464028b06Timo Sirainen if (str_array_length(args) < 3) {
a0b89f3b1df99b3a32f44623f13ad1893118825bTimo Sirainen *error_r = "Wrong parameter count";
a0b89f3b1df99b3a32f44623f13ad1893118825bTimo Sirainen return -1;
a0b89f3b1df99b3a32f44623f13ad1893118825bTimo Sirainen }
a488bbb7e5d7cc537e7af21d1f651762a2b8bf56Timo Sirainen if (str_to_uint(args[0], &tag) < 0) {
a488bbb7e5d7cc537e7af21d1f651762a2b8bf56Timo Sirainen *error_r = "Invalid tag";
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen return -1;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen }
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (args[3] == NULL)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen max_recent_msgs = 0;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen else if (str_to_uint(args[3], &max_recent_msgs) < 0) {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen *error_r = "Invalid max_recent_msgs";
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen return -1;
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen }
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen if (tag != 0) {
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen ctx = i_new(struct indexer_client_request, 1);
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen ctx->client = client;
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen ctx->tag = tag;
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen indexer_client_ref(client);
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen }
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen indexer_queue_append(client->queue, append, args[1], args[2],
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen max_recent_msgs, ctx);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen o_stream_nsend_str(client->output, t_strdup_printf("%u\tOK\n", tag));
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen return 0;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen}
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenstatic int
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenindexer_client_request_optimize(struct indexer_client *client,
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen const char *const *args, const char **error_r)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen{
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen struct indexer_client_request *ctx = NULL;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen unsigned int tag;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen /* <tag> <user> <mailbox> */
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (str_array_length(args) != 3) {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen *error_r = "Wrong parameter count";
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen return -1;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen }
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (str_to_uint(args[0], &tag) < 0) {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen *error_r = "Invalid tag";
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen return -1;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen }
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (tag != 0) {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen ctx = i_new(struct indexer_client_request, 1);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen ctx->client = client;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen ctx->tag = tag;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen indexer_client_ref(client);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen }
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen indexer_queue_append_optimize(client->queue, args[1], args[2], ctx);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen o_stream_nsend_str(client->output, t_strdup_printf("%u\tOK\n", tag));
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen return 0;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen}
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenstatic int
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenindexer_client_request(struct indexer_client *client,
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen const char *const *args, const char **error_r)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen{
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen const char *cmd = args[0];
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen args++;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen if (strcmp(cmd, "APPEND") == 0)
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen return indexer_client_request_queue(client, TRUE, args, error_r);
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen else if (strcmp(cmd, "PREPEND") == 0)
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen return indexer_client_request_queue(client, FALSE, args, error_r);
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen else if (strcmp(cmd, "OPTIMIZE") == 0)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen return indexer_client_request_optimize(client, args, error_r);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen else {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen *error_r = t_strconcat("Unknown command: ", cmd, NULL);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen return -1;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen }
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen}
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenstatic void indexer_client_input(struct indexer_client *client)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen{
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen const char *line, *const *args, *error;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen switch (i_stream_read(client->input)) {
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen case -2:
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen i_error("BUG: Client connection sent too much data");
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen indexer_client_destroy(client);
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen return;
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen case -1:
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen indexer_client_destroy(client);
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen return;
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen }
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen if (!client->version_received) {
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen if ((line = i_stream_next_line(client->input)) == NULL)
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen return;
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen if (!version_string_verify(line, "indexer",
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen INDEXER_CLIENT_PROTOCOL_MAJOR_VERSION)) {
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen i_error("Client not compatible with this server "
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen "(mixed old and new binaries?)");
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen indexer_client_destroy(client);
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen return;
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen }
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen client->version_received = TRUE;
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen }
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen while ((args = indexer_client_next_line(client)) != NULL) {
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen if (args[0] != NULL) {
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen if (indexer_client_request(client, args, &error) < 0) {
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen i_error("Client input error: %s", error);
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen indexer_client_destroy(client);
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen break;
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen }
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen }
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen }
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen}
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainenvoid indexer_client_status_callback(int percentage, void *context)
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen{
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen struct indexer_client_request *ctx = context;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen T_BEGIN {
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen o_stream_nsend_str(ctx->client->output,
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen t_strdup_printf("%u\t%d\n", ctx->tag, percentage));
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen } T_END;
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen if (percentage < 0 || percentage == 100) {
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen indexer_client_unref(ctx->client);
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen i_free(ctx);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen }
}
struct indexer_client *
indexer_client_create(int fd, struct indexer_queue *queue)
{
struct indexer_client *client;
client = i_new(struct indexer_client, 1);
client->refcount = 1;
client->queue = queue;
client->fd = fd;
client->input = i_stream_create_fd(fd, MAX_INBUF_SIZE, FALSE);
client->output = o_stream_create_fd(fd, (size_t)-1, FALSE);
o_stream_set_no_error_handling(client->output, TRUE);
client->io = io_add(fd, IO_READ, indexer_client_input, client);
DLLIST_PREPEND(&clients, client);
clients_count++;
indexer_refresh_proctitle();
return client;
}
static void indexer_client_destroy(struct indexer_client *client)
{
if (client->destroyed)
return;
client->destroyed = TRUE;
DLLIST_REMOVE(&clients, client);
io_remove(&client->io);
i_stream_close(client->input);
o_stream_close(client->output);
if (close(client->fd) < 0)
i_error("close(client) failed: %m");
client->fd = -1;
indexer_client_unref(client);
clients_count--;
master_service_client_connection_destroyed(master_service);
indexer_refresh_proctitle();
}
static void indexer_client_ref(struct indexer_client *client)
{
i_assert(client->refcount > 0);
client->refcount++;
}
static void indexer_client_unref(struct indexer_client *client)
{
i_assert(client->refcount > 0);
if (--client->refcount > 0)
return;
i_stream_destroy(&client->input);
o_stream_destroy(&client->output);
i_free(client);
}
unsigned int indexer_clients_get_count(void)
{
return clients_count;
}
void indexer_clients_destroy_all(void)
{
while (clients != NULL)
indexer_client_destroy(clients);
}