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