# HG changeset patch # User Timo Sirainen # Date 1105289688 -7200 # Node ID eb46a5dee02de04cdd8c36a607a2458816f85201 # Parent 8d15fea729c2c42f63fec118595c35058acdb936 Changed the way multiple auth processes are handled. It no longer uses a pid appended to socket name but instead there's a balancer process which proxies the requests to worker processes. diff -r 8d15fea729c2 -r eb46a5dee02d src/auth/Makefile.am --- a/src/auth/Makefile.am Sun Jan 09 18:52:30 2005 +0200 +++ b/src/auth/Makefile.am Sun Jan 09 18:54:48 2005 +0200 @@ -38,7 +38,11 @@ auth-master-connection.c \ auth-module.c \ auth-request.c \ + auth-request-balancer-child.c \ + auth-request-balancer-worker.c \ auth-request-handler.c \ + auth-request-handler-balancer.c \ + auth-request-handler-default.c \ db-ldap.c \ db-sql.c \ db-passwd-file.c \ @@ -81,6 +85,7 @@ auth-master-connection.h \ auth-module.h \ auth-request.h \ + auth-request-balancer.h \ auth-request-handler.h \ db-ldap.h \ db-sql.h \ diff -r 8d15fea729c2 -r eb46a5dee02d src/auth/auth-client-connection.c --- a/src/auth/auth-client-connection.c Sun Jan 09 18:52:30 2005 +0200 +++ b/src/auth/auth-client-connection.c Sun Jan 09 18:54:48 2005 +0200 @@ -98,16 +98,17 @@ conn->refcount++; if (!AUTH_MASTER_IS_DUMMY(conn->master)) { conn->request_handler = - auth_request_handler_create(conn->auth, - conn->connect_uid, pid, auth_callback, conn, + auth_request_handler_create(conn->auth, FALSE, + auth_callback, conn, auth_master_request_callback, conn->master); } else { conn->request_handler = - auth_request_handler_create(conn->auth, - conn->connect_uid, pid, + auth_request_handler_create(conn->auth, FALSE, auth_callback, conn, NULL, NULL); } + auth_request_handler_set(conn->request_handler, conn->connect_uid, pid); + conn->pid = pid; return TRUE; } @@ -338,6 +339,8 @@ master->clients = next; } - timeout_remove(master->to_clients); - master->to_clients = NULL; + if (master->to_clients != NULL) { + timeout_remove(master->to_clients); + master->to_clients = NULL; + } } diff -r 8d15fea729c2 -r eb46a5dee02d src/auth/auth-master-connection.c --- a/src/auth/auth-master-connection.c Sun Jan 09 18:52:30 2005 +0200 +++ b/src/auth/auth-master-connection.c Sun Jan 09 18:54:48 2005 +0200 @@ -10,6 +10,7 @@ #include "network.h" #include "userdb.h" #include "auth-request-handler.h" +#include "auth-request-balancer.h" #include "auth-master-interface.h" #include "auth-client-connection.h" #include "auth-master-connection.h" @@ -22,7 +23,7 @@ struct auth_listener { struct auth_master_connection *master; - int client_listener; + enum listener_type type; int fd; char *path; struct io *io; @@ -37,6 +38,7 @@ static int master_output(void *context); static void auth_master_connection_close(struct auth_master_connection *conn); static int auth_master_connection_unref(struct auth_master_connection *conn); +static void auth_listener_destroy(struct auth_listener *l); void auth_master_request_callback(const char *reply, void *context) { @@ -233,7 +235,6 @@ void auth_master_connection_destroy(struct auth_master_connection *conn) { struct auth_listener **l; - size_t i, size; if (conn->destroyed) return; @@ -244,16 +245,9 @@ if (conn->fd != -1) auth_master_connection_close(conn); - l = buffer_get_modifyable_data(conn->listeners_buf, &size); - size /= sizeof(*l); - for (i = 0; i < size; i++) { - net_disconnect(l[i]->fd); - io_remove(l[i]->io); - if (l[i]->path != NULL) { - (void)unlink(l[i]->path); - i_free(l[i]->path); - } - i_free(l[i]); + while (conn->listeners_buf->used > 0) { + l = buffer_get_modifyable_data(conn->listeners_buf, NULL); + auth_listener_destroy(*l); } buffer_free(conn->listeners_buf); conn->listeners_buf = NULL; @@ -280,30 +274,64 @@ fd = net_accept(l->fd, NULL, NULL); if (fd < 0) { if (fd < -1) - i_fatal("accept() failed: %m"); + i_fatal("accept(type %d) failed: %m", l->type); } else { net_set_nonblock(fd, TRUE); - if (l->client_listener) + switch (l->type) { + case LISTENER_CLIENT: (void)auth_client_connection_create(l->master, fd); - else { + break; + case LISTENER_MASTER: /* we'll just replace the previous master.. */ auth_master_connection_set_fd(l->master, fd); - auth_master_connection_send_handshake(l->master); + auth_master_connection_send_handshake(l->master); + break; + case LISTENER_BALANCER: + /* worker process connected to us */ + auth_request_balancer_add_child(fd); + auth_listener_destroy(l); + break; } } } void auth_master_connection_add_listener(struct auth_master_connection *conn, - int fd, const char *path, int client) + int fd, const char *path, + enum listener_type type) { struct auth_listener *l; l = i_new(struct auth_listener, 1); l->master = conn; - l->client_listener = client; + l->type = type; l->fd = fd; l->path = i_strdup(path); l->io = io_add(fd, IO_READ, auth_accept, l); buffer_append(conn->listeners_buf, &l, sizeof(l)); } + +static void auth_listener_destroy(struct auth_listener *l) +{ + struct auth_listener **lp; + size_t i, size; + + lp = buffer_get_modifyable_data(l->master->listeners_buf, &size); + size /= sizeof(*lp); + + for (i = 0; i < size; i++) { + if (lp[i] == l) { + buffer_delete(l->master->listeners_buf, + i * sizeof(l), sizeof(l)); + break; + } + } + + net_disconnect(l->fd); + io_remove(l->io); + if (l->path != NULL) { + (void)unlink(l->path); + i_free(l->path); + } + i_free(l); +} diff -r 8d15fea729c2 -r eb46a5dee02d src/auth/auth-master-connection.h --- a/src/auth/auth-master-connection.h Sun Jan 09 18:52:30 2005 +0200 +++ b/src/auth/auth-master-connection.h Sun Jan 09 18:54:48 2005 +0200 @@ -1,6 +1,12 @@ #ifndef __AUTH_MASTER_CONNECTION_H #define __AUTH_MASTER_CONNECTION_H +enum listener_type { + LISTENER_MASTER, + LISTENER_CLIENT, + LISTENER_BALANCER +}; + struct auth_master_connection { struct auth *auth; @@ -30,6 +36,7 @@ void auth_master_request_callback(const char *reply, void *context); void auth_master_connection_add_listener(struct auth_master_connection *conn, - int fd, const char *path, int client); + int fd, const char *path, + enum listener_type type); #endif diff -r 8d15fea729c2 -r eb46a5dee02d src/auth/auth-request-balancer-child.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/auth/auth-request-balancer-child.c Sun Jan 09 18:54:48 2005 +0200 @@ -0,0 +1,225 @@ +/* Copyright (C) 2002-2005 Timo Sirainen */ + +#include "common.h" +#include "ioloop.h" +#include "network.h" +#include "buffer.h" +#include "hash.h" +#include "istream.h" +#include "ostream.h" +#include "auth-request-balancer.h" + +#include +#include + +struct auth_balancer_child { + unsigned int id; + int fd; + + struct io *io; + struct istream *input; + struct ostream *output; +}; + +static buffer_t *balancer_children; +static struct hash_table *balancer_handlers; +static unsigned int balancer_next_idx = 0; + +static void +auth_request_balancer_remove_child(struct auth_balancer_child *child); + +static void balancer_input(void *context) +{ + struct auth_balancer_child *child = context; + struct auth_request_handler *handler; + const char *line, *id_str; + unsigned int id; + + switch (i_stream_read(child->input)) { + case 0: + return; + case -1: + /* disconnected */ + auth_request_balancer_remove_child(child); + return; + case -2: + /* buffer full */ + i_error("BUG: Auth balancer child sent us more than %d bytes", + (int)AUTH_BALANCER_MAX_LINE_LENGTH); + auth_request_balancer_remove_child(child); + return; + } + + while ((line = i_stream_next_line(child->input)) != NULL) { + id_str = line; + line = strchr(line, '\t'); + if (line == NULL) + continue; + + t_push(); + id = (unsigned int)strtoul(t_strcut(id_str, '\t'), NULL, 10); + handler = hash_lookup(balancer_handlers, POINTER_CAST(id)); + t_pop(); + + if (handler != NULL) { + auth_request_handler_balancer_reply(handler, + line + 1); + } + } +} + +static int balancer_output(void *context) +{ + struct auth_balancer_child *child = context; + + if (o_stream_flush(child->output) < 0) { + auth_request_balancer_remove_child(child); + return 1; + } + + /* FIXME: throttle control.. */ + return 1; +} + +void auth_request_balancer_add_child(int fd) +{ + static unsigned int balancer_id_counter = 0; + struct auth_balancer_child *child; + + net_set_nonblock(fd, TRUE); + + child = i_new(struct auth_balancer_child, 1); + child->id = ++balancer_id_counter; + child->fd = fd; + child->input = + i_stream_create_file(fd, default_pool, + AUTH_BALANCER_MAX_LINE_LENGTH, FALSE); + child->output = + o_stream_create_file(fd, default_pool, (size_t)-1, FALSE); + o_stream_set_flush_callback(child->output, balancer_output, child); + child->io = io_add(fd, IO_READ, balancer_input, child); + + buffer_append(balancer_children, &child, sizeof(child)); +} + +static void +auth_request_balancer_remove_child(struct auth_balancer_child *child) +{ + struct auth_balancer_child **children; + size_t i, size; + + children = buffer_get_modifyable_data(balancer_children, &size); + size /= sizeof(*children); + + for (i = 0; i < size; i++) { + if (children[i] == child) { + buffer_delete(balancer_children, + i * sizeof(child), sizeof(child)); + break; + } + } + i_assert(i != size); + + if (child->io != NULL) + io_remove(child->io); + + i_stream_unref(child->input); + o_stream_unref(child->output); + + if (close(child->fd) < 0) + i_error("close(balancer) failed: %m"); + i_free(child); +} + +static void balancer_send(struct auth_balancer_child *child, const char *line) +{ + struct const_iovec iov[2]; + + iov[0].iov_base = line; + iov[0].iov_len = strlen(line); + iov[1].iov_base = "\n"; + iov[1].iov_len = 1; + + (void)o_stream_sendv(child->output, iov, 2); + /* FIXME: throttle control */ +} + +unsigned int auth_request_balancer_send(const char *line) +{ + struct auth_balancer_child **child, *min_child; + size_t size, used_size, min_size; + unsigned int i, start; + + child = buffer_get_modifyable_data(balancer_children, &size); + size /= sizeof(*child); + + start = i = balancer_next_idx % size; + balancer_next_idx++; + + min_size = (size_t)-1; + min_child = NULL; + do { + used_size = o_stream_get_buffer_used_size(child[i]->output); + if (used_size == 0) { + /* nothing in output buffer, use this */ + balancer_send(child[i], line); + return child[i]->id; + } + if (used_size < min_size) { + min_size = used_size; + min_child = child[i]; + } + } while (++i != start); + + /* min_child has the smallest amount of data in output buffer */ + balancer_send(min_child, line); + return min_child->id; +} + +void auth_request_balancer_send_to(unsigned int id, const char *line) +{ + struct auth_balancer_child **child; + size_t i, size; + + child = buffer_get_modifyable_data(balancer_children, &size); + size /= sizeof(*child); + + for (i = 0; i < size; i++) { + if (child[i]->id == id) { + balancer_send(child[i], line); + return; + } + } + + // FIXME: ? +} + +void auth_request_balancer_add_handler(struct auth_request_handler *handler, + unsigned int connect_uid) +{ + hash_insert(balancer_handlers, POINTER_CAST(connect_uid), handler); +} + +void auth_request_balancer_remove_handler(unsigned int connect_uid) +{ + hash_remove(balancer_handlers, POINTER_CAST(connect_uid)); +} + +void auth_request_balancer_child_init(void) +{ + balancer_children = buffer_create_dynamic(default_pool, 32); + balancer_handlers = + hash_create(default_pool, default_pool, 0, NULL, NULL); +} + +void auth_request_balancer_child_deinit(void) +{ + while (balancer_children->used > 0) { + struct auth_balancer_child **child; + + child = buffer_get_modifyable_data(balancer_children, NULL); + auth_request_balancer_remove_child(*child); + } + buffer_free(balancer_children); + hash_destroy(balancer_handlers); +} diff -r 8d15fea729c2 -r eb46a5dee02d src/auth/auth-request-balancer-worker.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/auth/auth-request-balancer-worker.c Sun Jan 09 18:54:48 2005 +0200 @@ -0,0 +1,213 @@ +/* Copyright (C) 2002-2005 Timo Sirainen */ + +#include "common.h" +#include "ioloop.h" +#include "network.h" +#include "istream.h" +#include "ostream.h" +#include "safe-memset.h" +#include "auth-request-handler.h" +#include "auth-request-balancer.h" + +#include +#include + +struct auth_balancer_worker { + int fd; + + struct auth_request_handler *request_handler; + + struct io *io; + struct istream *input; + struct ostream *output; +}; + +static unsigned int next_uint(const char **line) +{ + const char *p, *value = *line; + + p = strchr(*line, '\t'); + if (p == NULL) + *line += strlen(value); + else { + value = t_strdup_until(value, p); + *line = p + 1; + } + return (unsigned int)strtoul(value, NULL, 10); +} + +static char *balancer_socket_path; +static struct timeout *to_connect; + +static void +auth_client_handle_line(struct auth_balancer_worker *worker, const char *line) +{ + struct auth_request_handler *rh = worker->request_handler; + unsigned int connect_uid, client_pid, id; + + connect_uid = next_uint(&line); + client_pid = next_uint(&line); + + auth_request_handler_set(rh, connect_uid, client_pid); + + if (strncmp(line, "AUTH\t", 5) == 0) + (void)auth_request_handler_auth_begin(rh, line + 5); + else if (strncmp(line, "CONT\t", 5) == 0) + (void)auth_request_handler_auth_continue(rh, line + 5); + else if (strncmp(line, "REQUEST\t", 8) == 0) { + id = (unsigned int)strtoul(line + 8, NULL, 10); + (void)auth_request_handler_master_request(rh, id, id); + } +} + +static void balancer_worker_input(void *context) +{ + struct auth_balancer_worker *worker = context; + char *line; + + switch (i_stream_read(worker->input)) { + case 0: + return; + case -1: + /* disconnected */ + auth_request_balancer_worker_destroy(worker); + return; + case -2: + /* buffer full */ + i_error("BUG: Auth balancer server sent us more than %d bytes", + (int)AUTH_BALANCER_MAX_LINE_LENGTH); + auth_request_balancer_worker_destroy(worker); + return; + } + + while ((line = i_stream_next_line(worker->input)) != NULL) { + t_push(); + + auth_client_handle_line(worker, line); + safe_memset(line, 0, strlen(line)); + + t_pop(); + } +} + +static int balancer_worker_output(void *context) +{ + struct auth_balancer_worker *worker = context; + + if (o_stream_flush(worker->output) < 0) { + auth_request_balancer_worker_destroy(worker); + return 1; + } + + /* FIXME: throttle control.. */ + return 1; +} + +static void auth_callback(const char *reply, void *context) +{ + struct auth_balancer_worker *worker = context; + struct const_iovec iov[2]; + + if (reply == NULL) { + /* request handler was destroyed */ + return; + } + + iov[0].iov_base = reply; + iov[0].iov_len = strlen(reply); + iov[1].iov_base = "\n"; + iov[1].iov_len = 1; + + (void)o_stream_sendv(worker->output, iov, 2); +} + +void auth_request_balancer_add_worker(struct auth *auth, int fd) +{ + struct auth_balancer_worker *worker; + + net_set_nonblock(fd, TRUE); + + worker = i_new(struct auth_balancer_worker, 1); + worker->fd = fd; + worker->input = + i_stream_create_file(fd, default_pool, + AUTH_BALANCER_MAX_LINE_LENGTH, FALSE); + worker->output = + o_stream_create_file(fd, default_pool, (size_t)-1, FALSE); + o_stream_set_flush_callback(worker->output, balancer_worker_output, + worker); + worker->io = io_add(fd, IO_READ, balancer_worker_input, worker); + + worker->request_handler = + auth_request_handler_create(auth, TRUE, auth_callback, worker, + auth_callback, worker); + + i_assert(auth->balancer_worker == NULL); + auth->balancer_worker = worker; +} + +void auth_request_balancer_worker_destroy(struct auth_balancer_worker *worker) +{ + io_loop_stop(ioloop); + auth_request_handler_unref(worker->request_handler); + + if (worker->io != NULL) + io_remove(worker->io); + + i_stream_unref(worker->input); + o_stream_unref(worker->output); + + if (close(worker->fd) < 0) + i_error("close(balancer) failed: %m"); + i_free(worker); +} + +static int auth_request_balancer_connect(struct auth *auth) +{ + int fd; + + fd = net_connect_unix(balancer_socket_path); + if (fd < 0) { + if (errno != EAGAIN) { + i_fatal("net_connect_unix(%s) failed: %m", + balancer_socket_path); + } + /* busy */ + return FALSE; + } + + auth_request_balancer_add_worker(auth, fd); + return TRUE; +} + +static void balancer_connect_timeout(void *context) +{ + struct auth *auth = context; + + if (auth_request_balancer_connect(auth)) { + timeout_remove(to_connect); + to_connect = NULL; + } +} + +void auth_request_balancer_worker_init(struct auth *auth) +{ + const char *name; + + name = getenv("AUTH_NAME"); + if (name == NULL) name = "auth"; + balancer_socket_path = i_strconcat(name, "-balancer", NULL); + + if (!auth_request_balancer_connect(auth)) { + /* couldn't connect to balancer yet, it's probably still + starting. try again later. */ + to_connect = timeout_add(1000, balancer_connect_timeout, auth); + } +} + +void auth_request_balancer_worker_deinit(void) +{ + if (to_connect != NULL) + timeout_remove(to_connect); + i_free(balancer_socket_path); +} diff -r 8d15fea729c2 -r eb46a5dee02d src/auth/auth-request-balancer.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/auth/auth-request-balancer.h Sun Jan 09 18:54:48 2005 +0200 @@ -0,0 +1,31 @@ +#ifndef __AUTH_REQUEST_BALANCER_H +#define __AUTH_REQUEST_BALANCER_H + +#include "auth-client-interface.h" + +#define AUTH_BALANCER_MAX_LINE_LENGTH \ + (AUTH_CLIENT_MAX_LINE_LENGTH + 64) + +struct auth_request_handler; + +void auth_request_balancer_add_child(int fd); +void auth_request_balancer_add_worker(struct auth *auth, int fd); +void auth_request_balancer_worker_destroy(struct auth_balancer_worker *worker); + +void auth_request_balancer_add_handler(struct auth_request_handler *handler, + unsigned int connect_uid); +void auth_request_balancer_remove_handler(unsigned int connect_uid); + +unsigned int auth_request_balancer_send(const char *line); +void auth_request_balancer_send_to(unsigned int id, const char *line); + +void auth_request_handler_balancer_reply(struct auth_request_handler *handler, + const char *line); + +void auth_request_balancer_child_init(void); +void auth_request_balancer_child_deinit(void); + +void auth_request_balancer_worker_init(struct auth *auth); +void auth_request_balancer_worker_deinit(void); + +#endif diff -r 8d15fea729c2 -r eb46a5dee02d src/auth/auth-request-handler-balancer.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/auth/auth-request-handler-balancer.c Sun Jan 09 18:54:48 2005 +0200 @@ -0,0 +1,289 @@ +/* Copyright (C) 2005 Timo Sirainen */ + +#include "common.h" +#include "ioloop.h" +#include "hash.h" +#include "str.h" +#include "str-sanitize.h" +#include "mech.h" +#include "auth-client-interface.h" +#include "auth-request-handler.h" +#include "auth-request-balancer.h" + +#include + +struct auth_balancer_request { + unsigned int client_id; + unsigned int master_id; + unsigned int balancer_pid; + time_t created; +}; + +struct auth_request_handler { + int refcount; + pool_t pool; + struct hash_table *client_requests; + + struct auth *auth; + unsigned int connect_uid, client_pid; + + auth_request_callback_t *callback; + void *context; + + auth_request_callback_t *master_callback; + void *master_context; +}; + +static struct auth_request_handler * +_create(struct auth *auth, int prepend_connect_uid, + auth_request_callback_t *callback, void *context, + auth_request_callback_t *master_callback, void *master_context) +{ + struct auth_request_handler *handler; + pool_t pool; + + i_assert(!prepend_connect_uid); + + pool = pool_alloconly_create("auth request handler", 4096); + + handler = p_new(pool, struct auth_request_handler, 1); + handler->refcount = 1; + handler->pool = pool; + handler->client_requests = + hash_create(default_pool, pool, 0, NULL, NULL); + handler->auth = auth; + handler->callback = callback; + handler->context = context; + handler->master_callback = master_callback; + handler->master_context = master_context; + + return handler; +} + +static void _set(struct auth_request_handler *handler, + unsigned int connect_uid, unsigned int client_pid) +{ + i_assert(handler->connect_uid == 0); + + handler->connect_uid = connect_uid; + handler->client_pid = client_pid; + + auth_request_balancer_add_handler(handler, connect_uid); +} + +static void _unref(struct auth_request_handler *handler) +{ + struct hash_iterate_context *iter; + void *key, *value; + + i_assert(handler->refcount > 0); + if (--handler->refcount > 0) + return; + + auth_request_balancer_remove_handler(handler->connect_uid); + + iter = hash_iterate_init(handler->client_requests); + while (hash_iterate(iter, &key, &value)) + i_free(value); + hash_iterate_deinit(iter); + + /* notify parent that we're done with all requests */ + handler->callback(NULL, handler->context); + + hash_destroy(handler->client_requests); + pool_unref(handler->pool); +} + +static void auth_request_handler_remove(struct auth_request_handler *handler, + struct auth_balancer_request *request) +{ + hash_remove(handler->client_requests, POINTER_CAST(request->client_id)); + i_free(request); +} + +static void _check_timeouts(struct auth_request_handler *handler) +{ + struct hash_iterate_context *iter; + void *key, *value; + + iter = hash_iterate_init(handler->client_requests); + while (hash_iterate(iter, &key, &value)) { + struct auth_balancer_request *request = value; + + if (request->created + AUTH_REQUEST_TIMEOUT < ioloop_time) + auth_request_handler_remove(handler, request); + } + hash_iterate_deinit(iter); +} + +static int _auth_begin(struct auth_request_handler *handler, const char *args) +{ + struct auth_balancer_request *request; + struct mech_module *mech; + const char *mech_name; + string_t *str; + unsigned int id; + + /* [...] */ + id = (unsigned int)strtoul(t_strcut(args, '\t'), NULL, 10); + mech_name = strchr(args, '\t'); + if (mech_name == NULL) { + i_error("BUG: Authentication client %u " + "sent broken AUTH request", handler->client_pid); + return FALSE; + } + args = strchr(++mech_name, '\t'); + mech_name = t_strcut(mech_name, '\t'); + + mech = mech_module_find(mech_name); + if (mech == NULL) { + /* unsupported mechanism */ + i_error("BUG: Authentication client %u requested unsupported " + "authentication mechanism %s", handler->client_pid, + str_sanitize(mech_name, MAX_MECH_NAME_LEN)); + return FALSE; + } + + request = i_new(struct auth_balancer_request, 1); + request->created = ioloop_time; + request->client_id = id; + + str = t_str_new(256); + str_printfa(str, "%u\t%u\tAUTH\t%u\t%s%s", + handler->connect_uid, handler->client_pid, + id, mech->mech_name, args == NULL ? "" : args); + request->balancer_pid = auth_request_balancer_send(str_c(str)); + + hash_insert(handler->client_requests, POINTER_CAST(id), request); + return TRUE; +} + +static int +_auth_continue(struct auth_request_handler *handler, const char *args) +{ + struct auth_balancer_request *request; + const char *data; + string_t *str; + unsigned int id; + + data = strchr(args, '\t'); + if (data++ == NULL) { + i_error("BUG: Authentication client sent broken CONT request"); + return FALSE; + } + + id = (unsigned int)strtoul(args, NULL, 10); + + request = hash_lookup(handler->client_requests, POINTER_CAST(id)); + if (request == NULL) { + data = t_strdup_printf("FAIL\t%u\treason=Timeouted", id); + handler->callback(data, handler->context); + return TRUE; + } + + str = t_str_new(128); + str_printfa(str, "%u\t%u\tCONT\t%u\t%s", + handler->connect_uid, handler->client_pid, id, data); + auth_request_balancer_send_to(request->balancer_pid, str_c(str)); + return TRUE; +} + +static void _master_request(struct auth_request_handler *handler, + unsigned int id, unsigned int client_id) +{ + struct auth_balancer_request *request; + const char *reply; + string_t *str; + + request = hash_lookup(handler->client_requests, + POINTER_CAST(client_id)); + if (request == NULL || request->balancer_pid == 0) { + i_error("Master request %u.%u not found from balancer", + handler->client_pid, client_id); + reply = t_strdup_printf("NOTFOUND\t%u", id); + handler->master_callback(reply, handler->master_context); + return; + } + + request->master_id = id; + + str = t_str_new(128); + str_printfa(str, "%u\t%u\tREQUEST\t%u", + handler->connect_uid, handler->client_pid, client_id); + auth_request_balancer_send_to(request->balancer_pid, str_c(str)); +} + +static void _flush_failures(void) +{ +} + +static void _init(void) +{ + auth_request_balancer_child_init(); +} + +static void _deinit(void) +{ + auth_request_balancer_child_deinit(); +} + +struct auth_request_handler_api auth_request_handler_balancer = { + _create, + _unref, + _set, + _check_timeouts, + _auth_begin, + _auth_continue, + _master_request, + _flush_failures, + _init, + _deinit +}; + +void auth_request_handler_balancer_reply(struct auth_request_handler *handler, + const char *line) +{ + struct auth_balancer_request *request; + const char *cmd, *id_str, *args; + unsigned int id; + + /* [...] */ + args = strchr(line, '\t'); + if (args == NULL) { + i_error("Balancer worker sent invalid reply: %s", line); + return; + } + + cmd = t_strdup_until(line, args); + id_str = args + 1; + args = strchr(id_str, '\t'); + if (args == NULL) + args = ""; + else + id_str = t_strdup_until(id_str, args); + + id = (unsigned int)strtoul(id_str, NULL, 10); + request = hash_lookup(handler->client_requests, POINTER_CAST(id)); + if (request == NULL) { + i_error("Balancer worker sent unknown request %u", id); + return; + } + + if (request->master_id == 0) { + handler->callback(line, handler->context); + if (strcmp(cmd, "CONT") != 0 && + (strcmp(cmd, "OK") != 0 || + strstr(line, "\tnologin") != NULL || + handler->master_callback == NULL)) { + /* this request doesn't have to wait for master + process to pick it up. delete it */ + auth_request_handler_remove(handler, request); + } + } else { + /* replace client id with master id */ + line = t_strdup_printf("%s\t%u%s", cmd, + request->master_id, args); + handler->master_callback(line, handler->master_context); + auth_request_handler_remove(handler, request); + } +} diff -r 8d15fea729c2 -r eb46a5dee02d src/auth/auth-request-handler-default.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/auth/auth-request-handler-default.c Sun Jan 09 18:54:48 2005 +0200 @@ -0,0 +1,514 @@ +/* Copyright (C) 2005 Timo Sirainen */ + +#include "common.h" +#include "ioloop.h" +#include "buffer.h" +#include "base64.h" +#include "hash.h" +#include "str.h" +#include "str-sanitize.h" +#include "auth-request.h" +#include "auth-request-handler.h" + +#include + +struct auth_request_handler { + int refcount; + pool_t pool; + struct hash_table *requests; + + struct auth *auth; + unsigned int connect_uid, client_pid; + + auth_request_callback_t *callback; + void *context; + + auth_request_callback_t *master_callback; + void *master_context; + + unsigned int prepend_connect_uid:1; +}; + +static buffer_t *auth_failures_buf; +static struct timeout *to_auth_failures; + +static struct auth_request_handler * +_create(struct auth *auth, int prepend_connect_uid, + auth_request_callback_t *callback, void *context, + auth_request_callback_t *master_callback, void *master_context) +{ + struct auth_request_handler *handler; + pool_t pool; + + pool = pool_alloconly_create("auth request handler", 4096); + + handler = p_new(pool, struct auth_request_handler, 1); + handler->refcount = 1; + handler->pool = pool; + handler->requests = hash_create(default_pool, pool, 0, NULL, NULL); + handler->auth = auth; + handler->callback = callback; + handler->context = context; + handler->master_callback = master_callback; + handler->master_context = master_context; + handler->prepend_connect_uid = prepend_connect_uid; + return handler; +} + +static void _set(struct auth_request_handler *handler, + unsigned int connect_uid, unsigned int client_pid) +{ + handler->connect_uid = connect_uid; + handler->client_pid = client_pid; +} + +static void _unref(struct auth_request_handler *handler) +{ + struct hash_iterate_context *iter; + void *key, *value; + + i_assert(handler->refcount > 0); + if (--handler->refcount > 0) + return; + + iter = hash_iterate_init(handler->requests); + while (hash_iterate(iter, &key, &value)) + auth_request_unref(value); + hash_iterate_deinit(iter); + + /* notify parent that we're done with all requests */ + handler->callback(NULL, handler->context); + + hash_destroy(handler->requests); + pool_unref(handler->pool); +} + +static void auth_request_handler_remove(struct auth_request_handler *handler, + struct auth_request *request) +{ + hash_remove(handler->requests, POINTER_CAST(request->id)); + auth_request_unref(request); +} + +static void _check_timeouts(struct auth_request_handler *handler) +{ + struct hash_iterate_context *iter; + void *key, *value; + + iter = hash_iterate_init(handler->requests); + while (hash_iterate(iter, &key, &value)) { + struct auth_request *request = value; + + if (request->created + AUTH_REQUEST_TIMEOUT < ioloop_time) + auth_request_handler_remove(handler, request); + } + hash_iterate_deinit(iter); +} + +static const char *get_client_extra_fields(struct auth_request *request) +{ + const char **fields; + unsigned int src, dest; + + if (request->extra_fields == NULL) + return NULL; + + /* we only wish to remove all fields prefixed with "userdb_" */ + if (strstr(request->extra_fields, "userdb_") == NULL) + return request->extra_fields; + + fields = t_strsplit(request->extra_fields, "\t"); + for (src = dest = 0; fields[src] != NULL; src++) { + if (strncmp(fields[src], "userdb_", 7) == 0) + fields[dest++] = fields[src]; + } + fields[dest] = NULL; + return t_strarray_join(fields, "\t"); +} + +static void auth_callback(struct auth_request *request, + enum auth_client_result result, + const void *reply, size_t reply_size) +{ + struct auth_request_handler *handler = request->context; + string_t *str; + const char *fields; + + t_push(); + + str = t_str_new(128 + MAX_BASE64_ENCODED_SIZE(reply_size)); + if (handler->prepend_connect_uid) + str_printfa(str, "%u\t", request->connect_uid); + + switch (result) { + case AUTH_CLIENT_RESULT_CONTINUE: + str_printfa(str, "CONT\t%u\t", request->id); + base64_encode(reply, reply_size, str); + request->accept_input = TRUE; + handler->callback(str_c(str), handler->context); + break; + case AUTH_CLIENT_RESULT_SUCCESS: + str_printfa(str, "OK\t%u\tuser=%s", request->id, request->user); + if (reply_size > 0) { + str_append(str, "\tresp="); + base64_encode(reply, reply_size, str); + } + fields = get_client_extra_fields(request); + if (fields != NULL) { + str_append_c(str, '\t'); + str_append(str, fields); + } + + if (request->no_login || handler->master_callback == NULL) { + /* this request doesn't have to wait for master + process to pick it up. delete it */ + auth_request_handler_remove(handler, request); + } + handler->callback(str_c(str), handler->context); + break; + case AUTH_CLIENT_RESULT_FAILURE: + str_printfa(str, "FAIL\t%u", request->id); + if (request->user != NULL) + str_printfa(str, "\tuser=%s", request->user); + if (request->internal_failure) + str_append(str, "\ttemp"); + fields = get_client_extra_fields(request); + if (fields != NULL) { + str_append_c(str, '\t'); + str_append(str, fields); + } + + if (request->delayed_failure) { + /* we came here from flush_failures() */ + handler->callback(str_c(str), handler->context); + break; + } + + /* remove the request from requests-list */ + auth_request_ref(request); + auth_request_handler_remove(handler, request); + + if (request->no_failure_delay) { + /* passdb specifically requested not to delay the + reply. */ + handler->callback(str_c(str), handler->context); + } else { + /* failure. don't announce it immediately to avoid + a) timing attacks, b) flooding */ + request->delayed_failure = TRUE; + handler->refcount++; + buffer_append(auth_failures_buf, + &request, sizeof(request)); + } + break; + } + /* NOTE: request may be destroyed now */ + + auth_request_handler_unref(handler); + + t_pop(); +} + +static void auth_request_handler_auth_fail(struct auth_request_handler *handler, + struct auth_request *request, + const char *reason) +{ + string_t *reply = t_str_new(64); + + auth_request_log_info(request, request->mech->mech_name, "%s", reason); + + if (handler->prepend_connect_uid) + str_printfa(reply, "%u\t", request->connect_uid); + str_printfa(reply, "FAIL\t%u\treason=%s", request->id, reason); + handler->callback(str_c(reply), handler->context); + + auth_request_handler_remove(handler, request); +} + +static int _auth_begin(struct auth_request_handler *handler, const char *args) +{ + struct mech_module *mech; + struct auth_request *request; + const char *const *list, *name, *arg, *initial_resp; + const void *initial_resp_data; + size_t initial_resp_len; + unsigned int id; + buffer_t *buf; + int valid_client_cert; + + /* [...] */ + list = t_strsplit(args, "\t"); + if (list[0] == NULL || list[1] == NULL) { + i_error("BUG: Authentication client %u " + "sent broken AUTH request", handler->client_pid); + return FALSE; + } + + id = (unsigned int)strtoul(list[0], NULL, 10); + + mech = mech_module_find(list[1]); + if (mech == NULL) { + /* unsupported mechanism */ + i_error("BUG: Authentication client %u requested unsupported " + "authentication mechanism %s", handler->client_pid, + str_sanitize(list[1], MAX_MECH_NAME_LEN)); + return FALSE; + } + + request = auth_request_new(handler->auth, mech, auth_callback, handler); + request->connect_uid = handler->connect_uid; + request->client_pid = handler->client_pid; + request->id = id; + + /* parse optional parameters */ + initial_resp = NULL; + valid_client_cert = FALSE; + for (list += 2; *list != NULL; list++) { + arg = strchr(*list, '='); + if (arg == NULL) { + name = *list; + arg = ""; + } else { + name = t_strdup_until(*list, arg); + arg++; + } + + if (strcmp(name, "lip") == 0) + (void)net_addr2ip(arg, &request->local_ip); + else if (strcmp(name, "rip") == 0) + (void)net_addr2ip(arg, &request->remote_ip); + else if (strcmp(name, "service") == 0) + request->service = p_strdup(request->pool, arg); + else if (strcmp(name, "resp") == 0) + initial_resp = arg; + else if (strcmp(name, "valid-client-cert") == 0) + valid_client_cert = TRUE; + } + + if (request->service == NULL) { + i_error("BUG: Authentication client %u " + "didn't specify service in request", + handler->client_pid); + auth_request_unref(request); + return FALSE; + } + + hash_insert(handler->requests, POINTER_CAST(id), request); + + if (request->auth->ssl_require_client_cert && !valid_client_cert) { + /* we fail without valid certificate */ + auth_request_handler_auth_fail(handler, request, + "Client didn't present valid SSL certificate"); + return TRUE; + } + + if (initial_resp == NULL) { + initial_resp_data = NULL; + initial_resp_len = 0; + } else { + size_t len = strlen(initial_resp); + buf = buffer_create_dynamic(pool_datastack_create(), + MAX_BASE64_DECODED_SIZE(len)); + if (base64_decode(initial_resp, len, NULL, buf) < 0) { + auth_request_handler_auth_fail(handler, request, + "Invalid base64 data in initial response"); + return TRUE; + } + initial_resp_data = buf->data; + initial_resp_len = buf->used; + } + + /* handler is referenced until auth_callback is called. */ + handler->refcount++; + auth_request_initial(request, initial_resp_data, initial_resp_len); + return TRUE; +} + +static int +_auth_continue(struct auth_request_handler *handler, const char *args) +{ + struct auth_request *request; + const char *data; + size_t data_len; + buffer_t *buf; + unsigned int id; + + data = strchr(args, '\t'); + if (data++ == NULL) { + i_error("BUG: Authentication client sent broken CONT request"); + return FALSE; + } + + id = (unsigned int)strtoul(args, NULL, 10); + + request = hash_lookup(handler->requests, POINTER_CAST(id)); + if (request == NULL) { + string_t *reply = t_str_new(64); + + if (handler->prepend_connect_uid) + str_printfa(reply, "%u\t", handler->connect_uid); + str_printfa(reply, "FAIL\t%u\treason=Timeouted", id); + handler->callback(str_c(reply), handler->context); + return TRUE; + } + + /* accept input only once after mechanism has sent a CONT reply */ + if (!request->accept_input) { + auth_request_handler_auth_fail(handler, request, + "Unexpected continuation"); + return TRUE; + } + request->accept_input = FALSE; + + data_len = strlen(data); + buf = buffer_create_dynamic(pool_datastack_create(), + MAX_BASE64_DECODED_SIZE(data_len)); + if (base64_decode(data, data_len, NULL, buf) < 0) { + auth_request_handler_auth_fail(handler, request, + "Invalid base64 data in continued response"); + return TRUE; + } + + /* handler is referenced until auth_callback is called. */ + handler->refcount++; + auth_request_continue(request, buf->data, buf->used); + return TRUE; +} + +static void append_user_reply(string_t *str, const struct user_data *user) +{ + const char *p; + + str_printfa(str, "%s\tuid=%s\tgid=%s", user->virtual_user, + dec2str(user->uid), dec2str(user->gid)); + + if (user->system_user != NULL) + str_printfa(str, "\tsystem_user=%s", user->system_user); + if (user->mail != NULL) + str_printfa(str, "\tmail=%s", user->mail); + + p = user->home != NULL ? strstr(user->home, "/./") : NULL; + if (p == NULL) { + if (user->home != NULL) + str_printfa(str, "\thome=%s", user->home); + } else { + /* wu-ftpd like /./ */ + str_printfa(str, "\thome=%s\tchroot=%s", + p + 3, t_strdup_until(user->home, p)); + } +} + +static void userdb_callback(const struct user_data *user, void *context) +{ + struct auth_request *request = context; + struct auth_request_handler *handler = request->context; + string_t *reply; + + if (user != NULL) { + auth_request_log_debug(request, "userdb", + "uid=%s gid=%s home=%s mail=%s", + dec2str(user->uid), dec2str(user->gid), + user->home != NULL ? user->home : "", + user->mail != NULL ? user->mail : ""); + } + + reply = t_str_new(256); + if (handler->prepend_connect_uid) + str_printfa(reply, "%u\t", request->connect_uid); + if (user == NULL) + str_printfa(reply, "NOTFOUND\t%u", request->id); + else { + str_printfa(reply, "USER\t%u\t", request->id); + append_user_reply(reply, user); + } + handler->master_callback(str_c(reply), handler->master_context); + + auth_request_unref(request); + auth_request_handler_unref(handler); +} + +static void _master_request(struct auth_request_handler *handler, + unsigned int id, unsigned int client_id) +{ + struct auth_request *request; + string_t *reply; + + reply = t_str_new(64); + if (handler->prepend_connect_uid) + str_printfa(reply, "%u\t", handler->connect_uid); + + request = hash_lookup(handler->requests, POINTER_CAST(client_id)); + if (request == NULL) { + i_error("Master request %u.%u not found", + handler->client_pid, client_id); + str_printfa(reply, "NOTFOUND\t%u", id); + handler->master_callback(str_c(reply), handler->master_context); + return; + } + + auth_request_ref(request); + auth_request_handler_remove(handler, request); + + if (!request->successful) { + i_error("Master requested unfinished authentication request " + "%u.%u", handler->client_pid, client_id); + str_printfa(reply, "NOTFOUND\t%u", id); + handler->master_callback(str_c(reply), handler->master_context); + } else { + /* the request isn't being referenced anywhere anymore, + so we can do a bit of kludging.. replace the request's + old client_id with master's id. */ + request->id = id; + request->context = handler; + + /* handler is referenced until userdb_callback is called. */ + handler->refcount++; + auth_request_lookup_user(request, userdb_callback, request); + } +} + +static void _flush_failures(void) +{ + struct auth_request **auth_request; + size_t i, size; + + auth_request = buffer_get_modifyable_data(auth_failures_buf, &size); + size /= sizeof(*auth_request); + + for (i = 0; i < size; i++) { + auth_request[i]->callback(auth_request[i], + AUTH_CLIENT_RESULT_FAILURE, NULL, 0); + auth_request_unref(auth_request[i]); + } + buffer_set_used_size(auth_failures_buf, 0); +} + +static void auth_failure_timeout(void *context __attr_unused__) +{ + _flush_failures(); +} + +static void _init(void) +{ + auth_failures_buf = buffer_create_dynamic(default_pool, 1024); + to_auth_failures = timeout_add(2000, auth_failure_timeout, NULL); +} + +static void _deinit(void) +{ + buffer_free(auth_failures_buf); + timeout_remove(to_auth_failures); +} + +struct auth_request_handler_api auth_request_handler_default = { + _create, + _unref, + _set, + _check_timeouts, + _auth_begin, + _auth_continue, + _master_request, + _flush_failures, + _init, + _deinit +}; diff -r 8d15fea729c2 -r eb46a5dee02d src/auth/auth-request-handler.c --- a/src/auth/auth-request-handler.c Sun Jan 09 18:52:30 2005 +0200 +++ b/src/auth/auth-request-handler.c Sun Jan 09 18:54:48 2005 +0200 @@ -1,486 +1,73 @@ /* Copyright (C) 2005 Timo Sirainen */ #include "common.h" -#include "ioloop.h" -#include "buffer.h" -#include "base64.h" -#include "hash.h" -#include "str.h" -#include "str-sanitize.h" -#include "auth-request.h" #include "auth-request-handler.h" - -#include - -/* Used only for string sanitization. */ -#define MAX_MECH_NAME_LEN 64 +#include "auth-request-balancer.h" -struct auth_request_handler { - int refcount; - pool_t pool; - struct hash_table *requests; - - struct auth *auth; - unsigned int connect_uid, client_pid; - - auth_request_callback_t *callback; - void *context; - - auth_request_callback_t *master_callback; - void *master_context; -}; - -static buffer_t *auth_failures_buf; -static struct timeout *to_auth_failures; +struct auth_request_handler_api *auth_request_handler_api; struct auth_request_handler * -auth_request_handler_create(struct auth *auth, - unsigned int connect_uid, unsigned int client_pid, +auth_request_handler_create(struct auth *auth, int prepend_connect_uid, auth_request_callback_t *callback, void *context, auth_request_callback_t *master_callback, void *master_context) { - struct auth_request_handler *handler; - pool_t pool; - - pool = pool_alloconly_create("auth request handler", 4096); + return auth_request_handler_api-> + create(auth, prepend_connect_uid, callback, context, + master_callback, master_context); +} - handler = p_new(pool, struct auth_request_handler, 1); - handler->refcount = 1; - handler->pool = pool; - handler->requests = hash_create(default_pool, pool, 0, NULL, NULL); - handler->auth = auth; - handler->connect_uid = connect_uid; - handler->client_pid = client_pid; - handler->callback = callback; - handler->context = context; - handler->master_callback = master_callback; - handler->master_context = master_context; - return handler; +void auth_request_handler_set(struct auth_request_handler *handler, + unsigned int connect_uid, + unsigned int client_pid) +{ + auth_request_handler_api->set(handler, connect_uid, client_pid); } void auth_request_handler_unref(struct auth_request_handler *handler) { - struct hash_iterate_context *iter; - void *key, *value; - - i_assert(handler->refcount > 0); - if (--handler->refcount > 0) - return; - - iter = hash_iterate_init(handler->requests); - while (hash_iterate(iter, &key, &value)) - auth_request_unref(value); - hash_iterate_deinit(iter); - - /* notify parent that we're done with all requests */ - handler->callback(NULL, handler->context); - - hash_destroy(handler->requests); - pool_unref(handler->pool); + auth_request_handler_api->unref(handler); } void auth_request_handler_check_timeouts(struct auth_request_handler *handler) { - struct hash_iterate_context *iter; - void *key, *value; - - iter = hash_iterate_init(handler->requests); - while (hash_iterate(iter, &key, &value)) { - struct auth_request *auth_request = value; - - if (auth_request->created + AUTH_REQUEST_TIMEOUT < ioloop_time) - hash_remove(handler->requests, key); - } - hash_iterate_deinit(iter); -} - -static void auth_request_handler_remove(struct auth_request_handler *handler, - struct auth_request *request) -{ - hash_remove(handler->requests, POINTER_CAST(request->id)); - auth_request_unref(request); -} - -static const char *get_client_extra_fields(struct auth_request *request) -{ - const char **fields; - unsigned int src, dest; - - if (request->extra_fields == NULL) - return NULL; - - /* we only wish to remove all fields prefixed with "userdb_" */ - if (strstr(request->extra_fields, "userdb_") == NULL) - return request->extra_fields; - - fields = t_strsplit(request->extra_fields, "\t"); - for (src = dest = 0; fields[src] != NULL; src++) { - if (strncmp(fields[src], "userdb_", 7) == 0) - fields[dest++] = fields[src]; - } - fields[dest] = NULL; - return t_strarray_join(fields, "\t"); -} - -static void auth_callback(struct auth_request *request, - enum auth_client_result result, - const void *reply, size_t reply_size) -{ - struct auth_request_handler *handler = request->context; - string_t *str = NULL; - const char *fields; - - t_push(); - - switch (result) { - case AUTH_CLIENT_RESULT_CONTINUE: - str = t_str_new(32 + MAX_BASE64_ENCODED_SIZE(reply_size)); - str_printfa(str, "CONT\t%u\t", request->id); - base64_encode(reply, reply_size, str); - request->accept_input = TRUE; - handler->callback(str_c(str), handler->context); - break; - case AUTH_CLIENT_RESULT_SUCCESS: - str = t_str_new(128 + MAX_BASE64_ENCODED_SIZE(reply_size)); - str_printfa(str, "OK\t%u\tuser=%s", request->id, request->user); - if (reply_size > 0) { - str_append(str, "\tresp="); - base64_encode(reply, reply_size, str); - } - fields = get_client_extra_fields(request); - if (fields != NULL) { - str_append_c(str, '\t'); - str_append(str, fields); - } - - if (request->no_login || handler->master_callback == NULL) { - /* this request doesn't have to wait for master - process to pick it up. delete it */ - auth_request_handler_remove(handler, request); - } - handler->callback(str_c(str), handler->context); - break; - case AUTH_CLIENT_RESULT_FAILURE: - str = t_str_new(128); - str_printfa(str, "FAIL\t%u", request->id); - if (request->user != NULL) - str_printfa(str, "\tuser=%s", request->user); - if (request->internal_failure) - str_append(str, "\ttemp"); - fields = get_client_extra_fields(request); - if (fields != NULL) { - str_append_c(str, '\t'); - str_append(str, fields); - } - - if (request->delayed_failure) { - /* we came here from flush_failures() */ - handler->callback(str_c(str), handler->context); - break; - } - - /* remove the request from requests-list */ - auth_request_ref(request); - auth_request_handler_remove(handler, request); - - if (request->no_failure_delay) { - /* passdb specifically requested not to delay the - reply. */ - handler->callback(str_c(str), handler->context); - } else { - /* failure. don't announce it immediately to avoid - a) timing attacks, b) flooding */ - request->delayed_failure = TRUE; - handler->refcount++; - buffer_append(auth_failures_buf, - &request, sizeof(request)); - } - break; - } - /* NOTE: request may be destroyed now */ - - auth_request_handler_unref(handler); - - t_pop(); -} - -static void auth_request_handler_auth_fail(struct auth_request_handler *handler, - struct auth_request *request, - const char *reason) -{ - const char *reply; - - auth_request_log_info(request, request->mech->mech_name, "%s", reason); - - reply = t_strdup_printf("FAIL\t%u\treason=%s", request->id, reason); - handler->callback(reply, handler->context); - auth_request_handler_remove(handler, request); + auth_request_handler_api->check_timeouts(handler); } int auth_request_handler_auth_begin(struct auth_request_handler *handler, const char *args) { - struct mech_module *mech; - struct auth_request *request; - const char *const *list, *name, *arg, *initial_resp; - const void *initial_resp_data; - size_t initial_resp_len; - unsigned int id; - buffer_t *buf; - int valid_client_cert; - - /* [...] */ - list = t_strsplit(args, "\t"); - if (list[0] == NULL || list[1] == NULL) { - i_error("BUG: Authentication client %u " - "sent broken AUTH request", handler->client_pid); - return FALSE; - } - - id = (unsigned int)strtoul(list[0], NULL, 10); - - mech = mech_module_find(list[1]); - if (mech == NULL) { - /* unsupported mechanism */ - i_error("BUG: Authentication client %u requested unsupported " - "authentication mechanism %s", handler->client_pid, - str_sanitize(list[1], MAX_MECH_NAME_LEN)); - return FALSE; - } - - request = auth_request_new(handler->auth, mech, auth_callback, handler); - request->connect_uid = handler->connect_uid; - request->client_pid = handler->client_pid; - request->id = id; - - /* parse optional parameters */ - initial_resp = NULL; - valid_client_cert = FALSE; - for (list += 2; *list != NULL; list++) { - arg = strchr(*list, '='); - if (arg == NULL) { - name = *list; - arg = ""; - } else { - name = t_strdup_until(*list, arg); - arg++; - } - - if (strcmp(name, "lip") == 0) - (void)net_addr2ip(arg, &request->local_ip); - else if (strcmp(name, "rip") == 0) - (void)net_addr2ip(arg, &request->remote_ip); - else if (strcmp(name, "service") == 0) - request->service = p_strdup(request->pool, arg); - else if (strcmp(name, "resp") == 0) - initial_resp = arg; - else if (strcmp(name, "valid-client-cert") == 0) - valid_client_cert = TRUE; - } - - if (request->service == NULL) { - i_error("BUG: Authentication client %u " - "didn't specify service in request", - handler->client_pid); - auth_request_unref(request); - return FALSE; - } - - hash_insert(handler->requests, POINTER_CAST(id), request); - - if (request->auth->ssl_require_client_cert && !valid_client_cert) { - /* we fail without valid certificate */ - auth_request_handler_auth_fail(handler, request, - "Client didn't present valid SSL certificate"); - return TRUE; - } - - if (initial_resp == NULL) { - initial_resp_data = NULL; - initial_resp_len = 0; - } else { - size_t len = strlen(initial_resp); - buf = buffer_create_dynamic(pool_datastack_create(), - MAX_BASE64_DECODED_SIZE(len)); - if (base64_decode(initial_resp, len, NULL, buf) < 0) { - auth_request_handler_auth_fail(handler, request, - "Invalid base64 data in initial response"); - return TRUE; - } - initial_resp_data = buf->data; - initial_resp_len = buf->used; - } - - /* handler is referenced until auth_callback is called. */ - handler->refcount++; - auth_request_initial(request, initial_resp_data, initial_resp_len); - return TRUE; + return auth_request_handler_api->auth_begin(handler, args); } int auth_request_handler_auth_continue(struct auth_request_handler *handler, const char *args) { - struct auth_request *request; - const char *data; - size_t data_len; - buffer_t *buf; - unsigned int id; - - data = strchr(args, '\t'); - if (data++ == NULL) { - i_error("BUG: Authentication client sent broken CONT request"); - return FALSE; - } - - id = (unsigned int)strtoul(args, NULL, 10); - - request = hash_lookup(handler->requests, POINTER_CAST(id)); - if (request == NULL) { - data = t_strdup_printf("FAIL\t%u\treason=Timeouted", id); - handler->callback(data, handler->context); - return TRUE; - } - - /* accept input only once after mechanism has sent a CONT reply */ - if (!request->accept_input) { - auth_request_handler_auth_fail(handler, request, - "Unexpected continuation"); - return TRUE; - } - request->accept_input = FALSE; - - data_len = strlen(data); - buf = buffer_create_dynamic(pool_datastack_create(), - MAX_BASE64_DECODED_SIZE(data_len)); - if (base64_decode(data, data_len, NULL, buf) < 0) { - auth_request_handler_auth_fail(handler, request, - "Invalid base64 data in continued response"); - return TRUE; - } - - /* handler is referenced until auth_callback is called. */ - handler->refcount++; - auth_request_continue(request, buf->data, buf->used); - return TRUE; -} - -static void append_user_reply(string_t *str, const struct user_data *user) -{ - const char *p; - - str_printfa(str, "%s\tuid=%s\tgid=%s", user->virtual_user, - dec2str(user->uid), dec2str(user->gid)); - - if (user->system_user != NULL) - str_printfa(str, "\tsystem_user=%s", user->system_user); - if (user->mail != NULL) - str_printfa(str, "\tmail=%s", user->mail); - - p = user->home != NULL ? strstr(user->home, "/./") : NULL; - if (p == NULL) { - if (user->home != NULL) - str_printfa(str, "\thome=%s", user->home); - } else { - /* wu-ftpd like /./ */ - str_printfa(str, "\thome=%s\tchroot=%s", - p + 3, t_strdup_until(user->home, p)); - } -} - -static void userdb_callback(const struct user_data *user, void *context) -{ - struct auth_request *request = context; - struct auth_request_handler *handler = request->context; - string_t *reply; - - if (user != NULL) { - auth_request_log_debug(request, "userdb", - "uid=%s gid=%s home=%s mail=%s", - dec2str(user->uid), dec2str(user->gid), - user->home != NULL ? user->home : "", - user->mail != NULL ? user->mail : ""); - } - - reply = t_str_new(256); - if (user == NULL) - str_printfa(reply, "NOTFOUND\t%u", request->id); - else { - str_printfa(reply, "USER\t%u\t", request->id); - append_user_reply(reply, user); - } - handler->master_callback(str_c(reply), handler->master_context); - - auth_request_unref(request); - auth_request_handler_unref(handler); + return auth_request_handler_api->auth_continue(handler, args); } void auth_request_handler_master_request(struct auth_request_handler *handler, unsigned int id, unsigned int client_id) { - struct auth_request *request; - const char *reply; - - request = hash_lookup(handler->requests, POINTER_CAST(client_id)); - if (request == NULL) { - i_error("Master request %u.%u not found", - handler->client_pid, client_id); - reply = t_strdup_printf("NOTFOUND\t%u", id); - handler->master_callback(reply, handler->master_context); - return; - } - - auth_request_ref(request); - auth_request_handler_remove(handler, request); - - if (!request->successful) { - i_error("Master requested unfinished authentication request " - "%u.%u", handler->client_pid, client_id); - reply = t_strdup_printf("NOTFOUND\t%u", id); - handler->master_callback(reply, handler->master_context); - } else { - /* the request isn't being referenced anywhere anymore, - so we can do a bit of kludging.. replace the request's - old client_id with master's id. */ - request->id = id; - request->context = handler; - - /* handler is referenced until userdb_callback is called. */ - handler->refcount++; - auth_request_lookup_user(request, userdb_callback, request); - } + auth_request_handler_api->master_request(handler, id, client_id); } void auth_request_handlers_flush_failures(void) { - struct auth_request **auth_request; - size_t i, size; - - auth_request = buffer_get_modifyable_data(auth_failures_buf, &size); - size /= sizeof(*auth_request); - - for (i = 0; i < size; i++) { - auth_request[i]->callback(auth_request[i], - AUTH_CLIENT_RESULT_FAILURE, NULL, 0); - auth_request_unref(auth_request[i]); - } - buffer_set_used_size(auth_failures_buf, 0); + auth_request_handler_api->flush_failures(); } -static void auth_failure_timeout(void *context __attr_unused__) +void auth_request_handlers_init(int balancer) { - auth_request_handlers_flush_failures(); -} + /* use balancer if we have it */ + auth_request_handler_api = balancer ? + &auth_request_handler_balancer : &auth_request_handler_default; -void auth_request_handlers_init(void) -{ - auth_failures_buf = buffer_create_dynamic(default_pool, 1024); - to_auth_failures = timeout_add(2000, auth_failure_timeout, NULL); + auth_request_handler_api->init(); } void auth_request_handlers_deinit(void) { - buffer_free(auth_failures_buf); - timeout_remove(to_auth_failures); + auth_request_handler_api->deinit(); } diff -r 8d15fea729c2 -r eb46a5dee02d src/auth/auth-request-handler.h --- a/src/auth/auth-request-handler.h Sun Jan 09 18:52:30 2005 +0200 +++ b/src/auth/auth-request-handler.h Sun Jan 09 18:54:48 2005 +0200 @@ -1,16 +1,50 @@ #ifndef __AUTH_REQUEST_HANDLER_H #define __AUTH_REQUEST_HANDLER_H +struct auth_request; + typedef void auth_request_callback_t(const char *reply, void *context); +struct auth_request_handler_api { + struct auth_request_handler * + (*create)(struct auth *auth, int prepend_connect_uid, + auth_request_callback_t *callback, void *context, + auth_request_callback_t *master_callback, + void *master_context); + void (*unref)(struct auth_request_handler *handler); + + void (*set)(struct auth_request_handler *handler, + unsigned int connect_uid, unsigned int client_pid); + + void (*check_timeouts)(struct auth_request_handler *handler); + int (*auth_begin)(struct auth_request_handler *handler, + const char *args); + int (*auth_continue)(struct auth_request_handler *handler, + const char *args); + void (*master_request)(struct auth_request_handler *handler, + unsigned int id, unsigned int client_id); + + void (*flush_failures)(void); + + void (*init)(void); + void (*deinit)(void); +}; + +extern struct auth_request_handler_api auth_request_handler_default; +extern struct auth_request_handler_api auth_request_handler_balancer; +extern struct auth_request_handler_api *auth_request_handler_api; + struct auth_request_handler * -auth_request_handler_create(struct auth *auth, - unsigned int connect_uid, unsigned int client_pid, +auth_request_handler_create(struct auth *auth, int prepend_connect_uid, auth_request_callback_t *callback, void *context, auth_request_callback_t *master_callback, void *master_context); void auth_request_handler_unref(struct auth_request_handler *handler); +void auth_request_handler_set(struct auth_request_handler *handler, + unsigned int connect_uid, + unsigned int client_pid); + void auth_request_handler_check_timeouts(struct auth_request_handler *handler); int auth_request_handler_auth_begin(struct auth_request_handler *handler, @@ -23,7 +57,7 @@ void auth_request_handlers_flush_failures(void); -void auth_request_handlers_init(void); +void auth_request_handlers_init(int balancer); void auth_request_handlers_deinit(void); #endif diff -r 8d15fea729c2 -r eb46a5dee02d src/auth/auth.c --- a/src/auth/auth.c Sun Jan 09 18:52:30 2005 +0200 +++ b/src/auth/auth.c Sun Jan 09 18:54:48 2005 +0200 @@ -1,13 +1,18 @@ /* Copyright (C) 2005 Timo Sirainen */ #include "common.h" +#include "network.h" +#include "buffer.h" #include "str.h" #include "mech.h" #include "userdb.h" #include "passdb.h" #include "auth.h" +#include "auth-request-handler.h" +#include "auth-request-balancer.h" #include +#include struct auth *auth_preinit(void) { @@ -169,5 +174,8 @@ userdb_deinit(auth); passdb_deinit(auth); + if (auth->balancer_worker != NULL) + auth_request_balancer_worker_destroy(auth->balancer_worker); + str_free(auth->mech_handshake); } diff -r 8d15fea729c2 -r eb46a5dee02d src/auth/auth.h --- a/src/auth/auth.h Sun Jan 09 18:52:30 2005 +0200 +++ b/src/auth/auth.h Sun Jan 09 18:54:48 2005 +0200 @@ -1,7 +1,11 @@ #ifndef __AUTH_H #define __AUTH_H +struct auth_balancer_worker; + struct auth { + struct auth_balancer_worker *balancer_worker; + struct mech_module_list *mech_modules; buffer_t *mech_handshake; diff -r 8d15fea729c2 -r eb46a5dee02d src/auth/common.h --- a/src/auth/common.h Sun Jan 09 18:52:30 2005 +0200 +++ b/src/auth/common.h Sun Jan 09 18:54:48 2005 +0200 @@ -5,7 +5,8 @@ #include "auth.h" #define MASTER_SOCKET_FD 0 -#define LOGIN_LISTEN_FD 3 +#define CLIENT_LISTEN_FD 3 +#define BALANCER_LISTEN_FD 4 extern struct ioloop *ioloop; extern int standalone; diff -r 8d15fea729c2 -r eb46a5dee02d src/auth/main.c --- a/src/auth/main.c Sun Jan 09 18:52:30 2005 +0200 +++ b/src/auth/main.c Sun Jan 09 18:54:48 2005 +0200 @@ -12,6 +12,8 @@ #include "mech.h" #include "auth.h" #include "auth-request-handler.h" +#include "auth-request-balancer.h" +#include "auth-master-interface.h" #include "auth-master-connection.h" #include "auth-client-connection.h" @@ -29,6 +31,7 @@ static buffer_t *masters_buf; static struct auth *auth; +static int balancer = FALSE, balancer_worker = FALSE; static void sig_quit(int signo __attr_unused__) { @@ -156,11 +159,13 @@ master = auth_master_connection_create(auth, -1); if (master_fd != -1) { auth_master_connection_add_listener(master, master_fd, - master_path, FALSE); + master_path, + LISTENER_MASTER); } if (client_fd != -1) { auth_master_connection_add_listener(master, client_fd, - client_path, TRUE); + client_path, + LISTENER_CLIENT); } auth_client_connections_init(master); buffer_append(masters_buf, &master, sizeof(master)); @@ -181,7 +186,8 @@ password_schemes_init(); masters_buf = buffer_create_dynamic(default_pool, 64); - add_extra_listeners(); + if (!balancer_worker) + add_extra_listeners(); /* Password lookups etc. may require roots, allow it. */ restrict_access_by_env(FALSE); @@ -192,13 +198,11 @@ struct auth_master_connection *master, **master_p; size_t i, size; - process_start_time = ioloop_time; - - process_start_time = ioloop_time; + process_start_time = ioloop_time; mech_init(); auth_init(auth); - auth_request_handlers_init(); + auth_request_handlers_init(balancer); lib_init_signals(sig_quit); @@ -228,12 +232,23 @@ if (chdir("/") < 0) i_fatal("chdir(/) failed: %m"); } - } else { + } else if (!balancer_worker) { master = auth_master_connection_create(auth, MASTER_SOCKET_FD); - auth_master_connection_add_listener(master, LOGIN_LISTEN_FD, - NULL, TRUE); + auth_master_connection_add_listener(master, CLIENT_LISTEN_FD, + NULL, LISTENER_CLIENT); + if (balancer) { + auth_master_connection_add_listener(master, + BALANCER_LISTEN_FD, + NULL, + LISTENER_BALANCER); + } auth_client_connections_init(master); buffer_append(masters_buf, &master, sizeof(master)); + } else { + master = auth_master_connection_create(auth, MASTER_SOCKET_FD); + buffer_append(masters_buf, &master, sizeof(master)); + + auth_request_balancer_worker_init(auth); } /* everything initialized, notify masters that all is well */ @@ -253,6 +268,9 @@ auth_request_handlers_flush_failures(); + if (balancer_worker) + auth_request_balancer_worker_deinit(); + master = buffer_get_modifyable_data(masters_buf, &size); size /= sizeof(*master); for (i = 0; i < size; i++) @@ -268,20 +286,32 @@ closelog(); } -int main(int argc, char *argv[]) +int main(int argc __attr_unused__, char *argv[]) { + int foreground; + #ifdef DEBUG if (getenv("GDB") == NULL) - fd_debug_verify_leaks(4, 1024); + fd_debug_verify_leaks(BALANCER_LISTEN_FD + 1, 1024); #endif /* NOTE: we start rooted, so keep the code minimal until restrict_access_by_env() is called */ lib_init(); ioloop = io_loop_create(system_pool); + while (argv[1] != NULL) { + if (strcmp(argv[1], "-F") == 0) + foreground = TRUE; + else if (strcmp(argv[1], "-b") == 0) + balancer = TRUE; + else if (strcmp(argv[1], "-bw") == 0) + balancer_worker = TRUE; + argv++; + } + drop_privileges(); - main_init(argc > 1 && strcmp(argv[1], "-F") == 0); + main_init(foreground); io_loop_run(ioloop); main_deinit(); diff -r 8d15fea729c2 -r eb46a5dee02d src/auth/mech.h --- a/src/auth/mech.h Sun Jan 09 18:52:30 2005 +0200 +++ b/src/auth/mech.h Sun Jan 09 18:54:48 2005 +0200 @@ -17,6 +17,9 @@ #include "auth-request.h" +/* Used only for string sanitization. */ +#define MAX_MECH_NAME_LEN 64 + struct mech_module { const char *mech_name; diff -r 8d15fea729c2 -r eb46a5dee02d src/master/auth-process.c --- a/src/master/auth-process.c Sun Jan 09 18:52:30 2005 +0200 +++ b/src/master/auth-process.c Sun Jan 09 18:54:48 2005 +0200 @@ -27,10 +27,13 @@ struct auth_process_group *next; int listen_fd; + int balancer_listen_fd; struct auth_settings *set; unsigned int process_count; struct auth_process *processes; + + unsigned int need_balancer:1; }; struct auth_process { @@ -45,7 +48,7 @@ struct hash_table *requests; - unsigned int private_listener:1; + unsigned int balancer:1; unsigned int external:1; unsigned int version_received:1; unsigned int initialized:1; @@ -251,7 +254,8 @@ } static struct auth_process * -auth_process_new(pid_t pid, int fd, struct auth_process_group *group) +auth_process_new(pid_t pid, int fd, struct auth_process_group *group, + int balancer) { struct auth_process *p; const char *handshake; @@ -270,24 +274,26 @@ FALSE); p->requests = hash_create(default_pool, default_pool, 0, NULL, NULL); + if (balancer) { + p->balancer = TRUE; + group->need_balancer = FALSE; + } else { + group->process_count++; + } + handshake = t_strdup_printf("VERSION\t%u\t%u\n", AUTH_MASTER_PROTOCOL_MAJOR_VERSION, AUTH_MASTER_PROTOCOL_MINOR_VERSION); (void)o_stream_send_str(p->output, handshake); - if (group->listen_fd == -1) - p->private_listener = TRUE; - p->next = group->processes; group->processes = p; - group->process_count++; return p; } static void auth_process_destroy(struct auth_process *p) { struct hash_iterate_context *iter; - const char *path; void *key, *value; struct auth_process **pos; @@ -302,13 +308,11 @@ break; } } - p->group->process_count--; - - if (p->private_listener) { - path = t_strconcat(p->group->set->parent->defaults->login_dir, - "/", p->group->set->name, dec2str(p->pid), - NULL); - (void)unlink(path); + if (p->balancer) { + /* the balancer process died, restart it */ + p->group->need_balancer = TRUE; + } else { + p->group->process_count--; } iter = hash_iterate_init(p->requests); @@ -354,24 +358,21 @@ net_set_nonblock(fd, TRUE); fd_close_on_exec(fd, TRUE); - auth = auth_process_new(0, fd, group); + auth = auth_process_new(0, fd, group, FALSE); auth->external = TRUE; return 0; } -static int auth_process_socket_create(struct auth_settings *auth_set, pid_t pid) +static int unix_socket_create(const char *path, int mode, + uid_t uid, gid_t gid, int backlog) { - const char *path; mode_t old_umask; int fd; - path = t_strconcat(auth_set->parent->defaults->login_dir, "/", - auth_set->name, pid == 0 ? NULL : dec2str(pid), - NULL); (void)unlink(path); - old_umask = umask(0117); /* we want 0660 mode for the socket */ - fd = net_listen_unix(path, 16); + old_umask = umask(0777 ^ mode); + fd = net_listen_unix(path, backlog); umask(old_umask); if (fd < 0) @@ -379,22 +380,26 @@ net_set_nonblock(fd, TRUE); fd_close_on_exec(fd, TRUE); - /* set correct permissions */ - if (chown(path, master_uid, auth_set->parent->login_gid) < 0) { - i_fatal("login: chown(%s, %s, %s) failed: %m", - path, dec2str(master_uid), - dec2str(auth_set->parent->login_gid)); + if (uid != (uid_t)-1 || gid != (gid_t)-1) { + /* set correct permissions */ + if (chown(path, uid, gid) < 0) { + i_fatal("login: chown(%s, %s, %s) failed: %m", + path, dec2str(uid), dec2str(gid)); + } } return fd; } -static int create_auth_process(struct auth_process_group *group) +static int create_auth_process(struct auth_process_group *group, + int balancer_worker) { struct auth_socket_settings *as; - const char *prefix, *str; + const char *prefix, *str, *executable; struct log_io *log; pid_t pid; - int fd[2], log_fd, listen_fd, i; + int fd[2], log_fd, i, balancer; + + balancer = group->balancer_listen_fd != -1 && !balancer_worker; /* see if this is a connect socket */ as = group->set->sockets; @@ -430,7 +435,7 @@ net_set_nonblock(fd[0], TRUE); fd_close_on_exec(fd[0], TRUE); - auth_process_new(pid, fd[0], group); + auth_process_new(pid, fd[0], group, balancer); (void)close(fd[1]); (void)close(log_fd); return 0; @@ -455,15 +460,20 @@ child_process_init_env(); - /* move login communication handle to 3. do it last so we can be - sure it's not closed afterwards. */ - listen_fd = group->listen_fd != -1 ? group->listen_fd : - auth_process_socket_create(group->set, getpid()); - if (listen_fd != 3) { - if (dup2(listen_fd, 3) < 0) + i_assert(group->balancer_listen_fd != 3); + if (group->listen_fd != 3) { + if (dup2(group->listen_fd, 3) < 0) i_fatal("dup2() failed: %m"); } + if (balancer) { + if (group->balancer_listen_fd != 4) { + if (dup2(group->balancer_listen_fd, 4) < 0) + i_fatal("dup2() failed: %m"); + } + fd_close_on_exec(4, FALSE); + } + for (i = 0; i <= 3; i++) fd_close_on_exec(i, FALSE); @@ -474,6 +484,7 @@ /* set other environment */ env_put("DOVECOT_MASTER=1"); + env_put(t_strconcat("AUTH_NAME=", group->set->name, NULL)); env_put(t_strconcat("MECHANISMS=", group->set->mechanisms, NULL)); env_put(t_strconcat("REALMS=", group->set->realms, NULL)); env_put(t_strconcat("DEFAULT_REALM=", group->set->default_realm, NULL)); @@ -510,9 +521,14 @@ any errors above will be logged */ closelog(); - client_process_exec(group->set->executable, ""); - i_fatal_status(FATAL_EXEC, "execv(%s) failed: %m", - group->set->executable); + executable = group->set->executable; + if (balancer || balancer_worker) { + /* we are running in balancer mode */ + executable = t_strconcat(executable, balancer_worker ? + " -bw" : " -b", NULL); + } + client_process_exec(executable, ""); + i_fatal_status(FATAL_EXEC, "execv(%s) failed: %m", executable); return -1; } @@ -534,6 +550,7 @@ static void auth_process_group_create(struct auth_settings *auth_set) { struct auth_process_group *group; + const char *path; group = i_new(struct auth_process_group, 1); group->set = auth_set; @@ -545,14 +562,21 @@ strcmp(auth_set->sockets->type, "connect") == 0) return; - /* If we keep long running login processes, we want them to use - all the auth processes in round robin. this means we have to create - separate socket for all of them. So, group->listen_fd is only - used with login_process_per_connection. */ - if (auth_set->parent->defaults->login_process_per_connection) - group->listen_fd = auth_process_socket_create(auth_set, 0); - else - group->listen_fd = -1; + path = t_strconcat(auth_set->parent->defaults->login_dir, "/", + auth_set->name, NULL); + group->listen_fd = unix_socket_create(path, 0660, master_uid, + auth_set->parent->login_gid, 16); + + if (auth_set->count <= 1) + group->balancer_listen_fd = -1; + else { + path = t_strconcat(auth_set->parent->defaults->base_dir, "/", + auth_set->name, "-balancer", NULL); + group->balancer_listen_fd = + unix_socket_create(path, 0600, (uid_t)-1, (gid_t)-1, + group->set->count); + group->need_balancer = TRUE; + } } static void auth_process_group_destroy(struct auth_process_group *group) @@ -566,14 +590,18 @@ group->processes = next; } - if (group->listen_fd != -1) { - path = t_strconcat(group->set->parent->defaults->login_dir, "/", - group->set->name, NULL); + path = t_strconcat(group->set->parent->defaults->login_dir, "/", + group->set->name, NULL); + (void)unlink(path); + + if (group->balancer_listen_fd != -1) { + path = t_strconcat(group->set->parent->defaults->base_dir, "/", + group->set->name, "-balancer", NULL); (void)unlink(path); + } - if (close(group->listen_fd) < 0) - i_error("close(%s) failed: %m", path); - } + if (close(group->listen_fd) < 0) + i_error("close(%s) failed: %m", path); i_free(group); } @@ -606,6 +634,7 @@ { struct auth_process_group *group; unsigned int count; + int balancer_worker; if (process_groups == NULL) { /* first time here, create the groups */ @@ -613,9 +642,13 @@ } for (group = process_groups; group != NULL; group = group->next) { + if (group->need_balancer) + (void)create_auth_process(group, FALSE); + + balancer_worker = group->balancer_listen_fd != -1; count = group->process_count; for (; count < group->set->count; count++) - (void)create_auth_process(group); + (void)create_auth_process(group, balancer_worker); } }