anvil-client.c revision 82d29ac2af30acd837abc32a71b526dc6846ebca
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen/* Copyright (c) 2009 Dovecot authors, see the included COPYING file */
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen#include "lib.h"
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen#include "ioloop.h"
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen#include "network.h"
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen#include "istream.h"
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen#include "ostream.h"
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen#include "array.h"
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen#include "aqueue.h"
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen#include "anvil-client.h"
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainenstruct anvil_query {
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen anvil_callback_t *callback;
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen void *context;
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen};
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainenstruct anvil_client {
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen char *path;
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen int fd;
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen struct istream *input;
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen struct ostream *output;
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen struct io *io;
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen ARRAY_DEFINE(queries_arr, struct anvil_query);
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen struct aqueue *queries;
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen bool (*reconnect_callback)(void);
82d29ac2af30acd837abc32a71b526dc6846ebcaTimo Sirainen enum anvil_client_flags flags;
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen};
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen#define ANVIL_HANDSHAKE "VERSION\tanvil\t1\t0\n"
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen#define ANVIL_INBUF_SIZE 1024
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainenstatic void anvil_client_disconnect(struct anvil_client *client);
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainenstruct anvil_client *
82d29ac2af30acd837abc32a71b526dc6846ebcaTimo Sirainenanvil_client_init(const char *path, bool (*reconnect_callback)(void),
82d29ac2af30acd837abc32a71b526dc6846ebcaTimo Sirainen enum anvil_client_flags flags)
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen{
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen struct anvil_client *client;
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen client = i_new(struct anvil_client, 1);
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen client->path = i_strdup(path);
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen client->reconnect_callback = reconnect_callback;
82d29ac2af30acd837abc32a71b526dc6846ebcaTimo Sirainen client->flags = flags;
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen client->fd = -1;
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen i_array_init(&client->queries_arr, 32);
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen client->queries = aqueue_init(&client->queries_arr.arr);
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen return client;
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen}
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainenvoid anvil_client_deinit(struct anvil_client **_client)
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen{
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen struct anvil_client *client = *_client;
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen *_client = NULL;
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen anvil_client_disconnect(client);
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen array_free(&client->queries_arr);
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen aqueue_deinit(&client->queries);
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen i_free(client->path);
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen i_free(client);
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen}
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainenstatic void anvil_reconnect(struct anvil_client *client)
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen{
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen anvil_client_disconnect(client);
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen if (client->reconnect_callback != NULL) {
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen if (!client->reconnect_callback()) {
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen /* no reconnection */
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen return;
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen }
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen }
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen (void)anvil_client_connect(client, FALSE);
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen}
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainenstatic void anvil_input(struct anvil_client *client)
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen{
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen const struct anvil_query *queries, *query;
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen const char *line;
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen unsigned int count;
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen queries = array_get(&client->queries_arr, &count);
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen while ((line = i_stream_read_next_line(client->input)) != NULL) {
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen if (aqueue_count(client->queries) == 0) {
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen i_error("anvil: Unexpected input: %s", line);
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen continue;
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen }
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen query = &queries[aqueue_idx(client->queries, 0)];
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen query->callback(line, query->context);
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen aqueue_delete_tail(client->queries);
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen }
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen if (client->input->stream_errno != 0) {
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen i_error("read(%s) failed: %m", client->path);
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen anvil_reconnect(client);
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen } else if (client->input->eof) {
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen i_error("read(%s) failed: EOF", client->path);
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen anvil_reconnect(client);
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen }
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen}
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainenint anvil_client_connect(struct anvil_client *client, bool retry)
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen{
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen int fd;
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen i_assert(client->fd == -1);
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen fd = retry ? net_connect_unix_with_retries(client->path, 5000) :
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen net_connect_unix(client->path);
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen if (fd == -1) {
82d29ac2af30acd837abc32a71b526dc6846ebcaTimo Sirainen if (errno != ENOENT ||
82d29ac2af30acd837abc32a71b526dc6846ebcaTimo Sirainen (client->flags & ANVIL_CLIENT_FLAG_HIDE_ENOENT) == 0) {
82d29ac2af30acd837abc32a71b526dc6846ebcaTimo Sirainen i_error("net_connect_unix(%s) failed: %m",
82d29ac2af30acd837abc32a71b526dc6846ebcaTimo Sirainen client->path);
82d29ac2af30acd837abc32a71b526dc6846ebcaTimo Sirainen }
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen return -1;
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen }
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen client->fd = fd;
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen client->input = i_stream_create_fd(fd, ANVIL_INBUF_SIZE, FALSE);
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen client->output = o_stream_create_fd(fd, (size_t)-1, FALSE);
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen client->io = io_add(fd, IO_READ, anvil_input, client);
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen o_stream_send_str(client->output, ANVIL_HANDSHAKE);
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen return 0;
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen}
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainenstatic void anvil_client_cancel_queries(struct anvil_client *client)
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen{
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen const struct anvil_query *queries, *query;
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen unsigned int count;
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen queries = array_get(&client->queries_arr, &count);
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen while (aqueue_count(client->queries) > 0) {
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen query = &queries[aqueue_idx(client->queries, 0)];
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen query->callback(NULL, query->context);
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen aqueue_delete_tail(client->queries);
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen }
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen}
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainenstatic void anvil_client_disconnect(struct anvil_client *client)
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen{
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen if (client->fd == -1)
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen return;
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen anvil_client_cancel_queries(client);
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen io_remove(&client->io);
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen i_stream_destroy(&client->input);
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen o_stream_destroy(&client->output);
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen net_disconnect(client->fd);
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen client->fd = -1;
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen}
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen
82d29ac2af30acd837abc32a71b526dc6846ebcaTimo Sirainenstatic int anvil_client_send(struct anvil_client *client, const char *cmd)
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen{
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen struct const_iovec iov[2];
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen if (client->fd == -1) {
82d29ac2af30acd837abc32a71b526dc6846ebcaTimo Sirainen if (anvil_client_connect(client, FALSE) < 0)
82d29ac2af30acd837abc32a71b526dc6846ebcaTimo Sirainen return -1;
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen }
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen
82d29ac2af30acd837abc32a71b526dc6846ebcaTimo Sirainen iov[0].iov_base = cmd;
82d29ac2af30acd837abc32a71b526dc6846ebcaTimo Sirainen iov[0].iov_len = strlen(cmd);
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen iov[1].iov_base = "\n";
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen iov[1].iov_len = 1;
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen o_stream_sendv(client->output, iov, 2);
82d29ac2af30acd837abc32a71b526dc6846ebcaTimo Sirainen return 0;
82d29ac2af30acd837abc32a71b526dc6846ebcaTimo Sirainen}
82d29ac2af30acd837abc32a71b526dc6846ebcaTimo Sirainen
82d29ac2af30acd837abc32a71b526dc6846ebcaTimo Sirainenvoid anvil_client_query(struct anvil_client *client, const char *query,
82d29ac2af30acd837abc32a71b526dc6846ebcaTimo Sirainen anvil_callback_t *callback, void *context)
82d29ac2af30acd837abc32a71b526dc6846ebcaTimo Sirainen{
82d29ac2af30acd837abc32a71b526dc6846ebcaTimo Sirainen struct anvil_query anvil_query;
82d29ac2af30acd837abc32a71b526dc6846ebcaTimo Sirainen
82d29ac2af30acd837abc32a71b526dc6846ebcaTimo Sirainen if (anvil_client_send(client, query) < 0) {
82d29ac2af30acd837abc32a71b526dc6846ebcaTimo Sirainen callback(NULL, context);
82d29ac2af30acd837abc32a71b526dc6846ebcaTimo Sirainen return;
82d29ac2af30acd837abc32a71b526dc6846ebcaTimo Sirainen }
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen anvil_query.callback = callback;
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen anvil_query.context = context;
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen aqueue_append(client->queries, &anvil_query);
83879fb63453dc3b96269d2217339e14dca753b4Timo Sirainen}
82d29ac2af30acd837abc32a71b526dc6846ebcaTimo Sirainen
82d29ac2af30acd837abc32a71b526dc6846ebcaTimo Sirainenvoid anvil_client_cmd(struct anvil_client *client, const char *cmd)
82d29ac2af30acd837abc32a71b526dc6846ebcaTimo Sirainen{
82d29ac2af30acd837abc32a71b526dc6846ebcaTimo Sirainen (void)anvil_client_send(client, cmd);
82d29ac2af30acd837abc32a71b526dc6846ebcaTimo Sirainen}