view src/director/auth-connection.c @ 22536:5f09f6aa089b

director: doveadm HOST-* commands now wait for ring sync before returning OK This should make it easier for tests and maybe for scripts in general, so they won't think the command failed when it just takes a while to finish.
author Timo Sirainen <timo.sirainen@dovecot.fi>
date Mon, 14 Aug 2017 10:29:47 +0300
parents 2e2563132d5f
children cb108f786fb4
line wrap: on
line source

/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */

#include "lib.h"
#include "ioloop.h"
#include "istream.h"
#include "ostream.h"
#include "net.h"
#include "llist.h"
#include "safe-memset.h"
#include "auth-client-interface.h"
#include "auth-connection.h"

#include <unistd.h>

struct auth_connection {
	struct auth_connection *prev, *next;

	char *path;
	int fd;
	struct io *io;
	struct istream *input;
	struct ostream *output;

	auth_input_callback *callback;
	void *context;
};

static struct auth_connection *auth_connections;

static void auth_connection_disconnected(struct auth_connection **conn);

static void auth_connection_input(struct auth_connection *conn)
{
	char *line;

	switch (i_stream_read(conn->input)) {
	case 0:
		return;
	case -1:
		/* disconnected */
		i_error("Auth server disconnected unexpectedly");
		auth_connection_disconnected(&conn);
		return;
	case -2:
		/* buffer full */
		i_error("BUG: Auth server sent us more than %d bytes",
			(int)AUTH_CLIENT_MAX_LINE_LENGTH);
		auth_connection_disconnected(&conn);
		return;
	}

	while ((line = i_stream_next_line(conn->input)) != NULL) {
		T_BEGIN {
			conn->callback(line, conn->context);
			safe_memset(line, 0, strlen(line));
		} T_END;
	}
}

struct auth_connection *auth_connection_init(const char *path)
{
	struct auth_connection *conn;
 
	conn = i_new(struct auth_connection, 1);
	conn->fd = -1;
	conn->path = i_strdup(path);
	DLLIST_PREPEND(&auth_connections, conn);
	return conn;
}

void auth_connection_set_callback(struct auth_connection *conn,
				  auth_input_callback *callback, void *context)
{
	conn->callback = callback;
	conn->context = context;
}

int auth_connection_connect(struct auth_connection *conn)
{
	i_assert(conn->fd == -1);

	conn->fd = net_connect_unix_with_retries(conn->path, 1000);
	if (conn->fd == -1) {
		i_error("connect(%s) failed: %m", conn->path);
		return -1;
	}

	conn->input = i_stream_create_fd(conn->fd, AUTH_CLIENT_MAX_LINE_LENGTH,
					 FALSE);
	conn->output = o_stream_create_fd(conn->fd, (size_t)-1, FALSE);
	o_stream_set_no_error_handling(conn->output, TRUE);
	conn->io = io_add(conn->fd, IO_READ, auth_connection_input, conn);
	return 0;
}

void auth_connection_deinit(struct auth_connection **_conn)
{
	struct auth_connection *conn = *_conn;

	*_conn = NULL;

	DLLIST_REMOVE(&auth_connections, conn);
	if (conn->fd != -1) {
		io_remove(&conn->io);
		i_stream_unref(&conn->input);
		o_stream_unref(&conn->output);

		if (close(conn->fd) < 0)
			i_error("close(auth connection) failed: %m");
	}
	i_free(conn->path);
	i_free(conn);
}

static void auth_connection_disconnected(struct auth_connection **_conn)
{
	struct auth_connection *conn = *_conn;

	*_conn = NULL;
	/* notify callback. it should deinit this connection */
	conn->callback(NULL, conn->context);
}

struct ostream *auth_connection_get_output(struct auth_connection *conn)
{
	i_assert(conn->output != NULL);
	return conn->output;
}

void auth_connections_deinit(void)
{
	while (auth_connections != NULL) {
		struct auth_connection *conn = auth_connections;

		auth_connection_disconnected(&conn);
	}
}