# HG changeset patch # User Timo Sirainen # Date 1241573314 14400 # Node ID 2e2b957f1cca829a028721edc1d74c36f193062a # Parent 783a81c3deb7479b49fdcc74ce5bfc715fba30b3 Implemented anvil service, which is used to implement mail_max_userip_connections. diff -r 783a81c3deb7 -r 2e2b957f1cca .hgignore --- a/.hgignore Tue May 05 20:26:40 2009 -0400 +++ b/.hgignore Tue May 05 21:28:34 2009 -0400 @@ -51,9 +51,11 @@ doc/wiki/*.txt doc/wiki/Makefile.am +src/anvil/anvil src/auth/checkpassword-reply src/auth/dovecot-auth src/config/all-settings.c +src/config/config src/config/doveconf src/lda/dovecot-lda src/dict/dict diff -r 783a81c3deb7 -r 2e2b957f1cca TODO --- a/TODO Tue May 05 20:26:40 2009 -0400 +++ b/TODO Tue May 05 21:28:34 2009 -0400 @@ -1,6 +1,6 @@ - - mail_max_userip_connections + - move ssl proxying code to lib-master + - rawlog is broken because it can't get $HOME etc. - dovecot stop, dovecot reload - - make sure status/log messages which are important get through to the server - log prefixes work in a weird way now. failures.c prefixes are used only when not doing internal logging. that won't work in future.. - library dependency tracking still broken. .la changes get noticed, diff -r 783a81c3deb7 -r 2e2b957f1cca configure.in --- a/configure.in Tue May 05 20:26:40 2009 -0400 +++ b/configure.in Tue May 05 21:28:34 2009 -0400 @@ -2383,6 +2383,7 @@ src/lib-storage/index/raw/Makefile src/lib-storage/index/shared/Makefile src/lib-storage/register/Makefile +src/anvil/Makefile src/auth/Makefile src/config/Makefile src/lda/Makefile diff -r 783a81c3deb7 -r 2e2b957f1cca dovecot-master-example.conf --- a/dovecot-master-example.conf Tue May 05 20:26:40 2009 -0400 +++ b/dovecot-master-example.conf Tue May 05 21:28:34 2009 -0400 @@ -2,15 +2,29 @@ type = config executable = config user = dovecot - drop_priv_before_exec = yes + unix_listener { path = config + mode = 0666 } } service log { type = log executable = log + process_limit = 1 +} + +service anvil { + type = anvil + executable = anvil + process_limit = 1 + user = dovecot + chroot = empty + + unix_listener { + path = anvil + } } service auth { @@ -56,7 +70,6 @@ } service imap { - type = auth-destination executable = imap } @@ -80,7 +93,6 @@ } service pop3 { - type = auth-destination executable = pop3 } diff -r 783a81c3deb7 -r 2e2b957f1cca src/anvil/Makefile.am --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/anvil/Makefile.am Tue May 05 21:28:34 2009 -0400 @@ -0,0 +1,24 @@ +pkglibexecdir = $(libexecdir)/dovecot + +pkglibexec_PROGRAMS = anvil + +AM_CPPFLAGS = \ + -I$(top_srcdir)/src/lib \ + -I$(top_srcdir)/src/lib-settings \ + -I$(top_srcdir)/src/lib-master + +anvil_LDADD = \ + $(LIBDOVECOT) \ + $(MODULE_LIBS) \ + $(RAND_LIBS) +anvil_DEPENDENCIES = $(LIBDOVECOT) + +anvil_SOURCES = \ + main.c \ + anvil-connection.c \ + connect-limit.c + +noinst_HEADERS = \ + anvil-connection.h \ + common.h \ + connect-limit.h diff -r 783a81c3deb7 -r 2e2b957f1cca src/anvil/anvil-connection.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/anvil/anvil-connection.c Tue May 05 21:28:34 2009 -0400 @@ -0,0 +1,165 @@ +/* Copyright (C) 2009 Dovecot authors, see the included COPYING file */ + +#include "common.h" +#include "llist.h" +#include "istream.h" +#include "ostream.h" +#include "master-interface.h" +#include "connect-limit.h" +#include "anvil-connection.h" + +#include +#include + +#define MAX_INBUF_SIZE 1024 + +#define ANVIL_CLIENT_PROTOCOL_MAJOR_VERSION 1 +#define ANVIL_CLIENT_PROTOCOL_MINOR_VERSION 0 + +struct anvil_connection { + struct anvil_connection *prev, *next; + + int fd; + struct istream *input; + struct ostream *output; + struct io *io; + + unsigned int version_received:1; + unsigned int handshaked:1; +}; + +struct anvil_connection *anvil_connections = NULL; + +static const char *const * +anvil_connection_next_line(struct anvil_connection *conn) +{ + const char *line; + + line = i_stream_next_line(conn->input); + if (line == NULL) + return NULL; + + return t_strsplit(line, "\t"); +} + +static int +anvil_connection_request(struct anvil_connection *conn, + const char *const *args, const char **error_r) +{ + const char *cmd = args[0]; + unsigned int count; + pid_t pid; + + args++; + if (strcmp(cmd, "CONNECT") == 0) { + if (args[0] == NULL || args[1] == NULL) { + *error_r = "CONNECT: Not enough parameters"; + return -1; + } + pid = strtol(args[0], NULL, 10); + connect_limit_connect(connect_limit, pid, args[1]); + return 0; + } else if (strcmp(cmd, "DISCONNECT") == 0) { + if (args[0] == NULL || args[1] == NULL) { + *error_r = "DISCONNECT: Not enough parameters"; + return -1; + } + pid = strtol(args[0], NULL, 10); + connect_limit_disconnect(connect_limit, pid, args[1]); + return 0; + } else if (strcmp(cmd, "KILL") == 0) { + if (args[0] == NULL) { + *error_r = "KILL: Not enough parameters"; + return -1; + } + if (conn->fd != MASTER_LISTEN_FD_FIRST) { + *error_r = "KILL sent by a non-master connection"; + return -1; + } + pid = strtol(args[0], NULL, 10); + connect_limit_disconnect_pid(connect_limit, pid); + return 0; + } else if (strcmp(cmd, "LOOKUP") == 0) { + if (args[0] == NULL) { + *error_r = "LOOKUP: Not enough parameters"; + return -1; + } + count = connect_limit_lookup(connect_limit, args[0]); + (void)o_stream_send_str(conn->output, + t_strdup_printf("%u\n", count)); + return 0; + } else { + *error_r = t_strconcat("Unknown command: ", cmd, NULL); + return -1; + } +} + +static void anvil_connection_input(void *context) +{ + struct anvil_connection *conn = context; + const char *const *args, *line, *error; + + switch (i_stream_read(conn->input)) { + case -2: + i_error("BUG: Anvil client connection sent too much data"); + anvil_connection_destroy(conn); + return; + case -1: + anvil_connection_destroy(conn); + return; + } + + if (!conn->version_received) { + line = i_stream_next_line(conn->input); + if (line == NULL) + return; + + if (strncmp(line, "VERSION\t", 8) != 0 || + atoi(t_strcut(line + 8, '\t')) != + ANVIL_CLIENT_PROTOCOL_MAJOR_VERSION) { + i_error("Anvil client not compatible with this server " + "(mixed old and new binaries?)"); + anvil_connection_destroy(conn); + return; + } + conn->version_received = TRUE; + } + + while ((args = anvil_connection_next_line(conn)) != NULL) { + if (args[0] != NULL) { + if (anvil_connection_request(conn, args, &error) < 0) + i_error("Anvil client input error: %s", error); + } + } +} + +struct anvil_connection *anvil_connection_create(int fd) +{ + struct anvil_connection *conn; + + conn = i_new(struct anvil_connection, 1); + conn->fd = fd; + conn->input = i_stream_create_fd(fd, MAX_INBUF_SIZE, FALSE); + conn->output = o_stream_create_fd(fd, (size_t)-1, FALSE); + conn->io = io_add(fd, IO_READ, anvil_connection_input, conn); + DLLIST_PREPEND(&anvil_connections, conn); + return conn; +} + +void anvil_connection_destroy(struct anvil_connection *conn) +{ + DLLIST_REMOVE(&anvil_connections, conn); + + io_remove(&conn->io); + i_stream_destroy(&conn->input); + o_stream_destroy(&conn->output); + if (close(conn->fd) < 0) + i_error("close(anvil conn) failed: %m"); + i_free(conn); +} + +void anvil_connections_destroy_all(void) +{ + while (anvil_connections != NULL) + anvil_connection_destroy(anvil_connections); +} diff -r 783a81c3deb7 -r 2e2b957f1cca src/anvil/anvil-connection.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/anvil/anvil-connection.h Tue May 05 21:28:34 2009 -0400 @@ -0,0 +1,9 @@ +#ifndef ANVIL_CONNECTION_H +#define ANVIL_CONNECTION_H + +struct anvil_connection *anvil_connection_create(int fd); +void anvil_connection_destroy(struct anvil_connection *conn); + +void anvil_connections_destroy_all(void); + +#endif diff -r 783a81c3deb7 -r 2e2b957f1cca src/anvil/common.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/anvil/common.h Tue May 05 21:28:34 2009 -0400 @@ -0,0 +1,8 @@ +#ifndef COMMON_H +#define COMMON_H + +#include "lib.h" + +extern struct connect_limit *connect_limit; + +#endif diff -r 783a81c3deb7 -r 2e2b957f1cca src/anvil/connect-limit.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/anvil/connect-limit.c Tue May 05 21:28:34 2009 -0400 @@ -0,0 +1,166 @@ +/* Copyright (C) 2009 Dovecot authors, see the included COPYING file */ + +#include "common.h" +#include "hash.h" +#include "connect-limit.h" + +struct ident_pid { + /* ident string points to ident_hash keys */ + const char *ident; + pid_t pid; + unsigned int refcount; +}; + +struct connect_limit { + /* ident => refcount */ + struct hash_table *ident_hash; + /* struct ident_pid => struct ident_pid */ + struct hash_table *ident_pid_hash; +}; + +static unsigned int ident_pid_hash(const void *p) +{ + const struct ident_pid *i = p; + + return str_hash(i->ident) ^ i->pid; +} + +static int ident_pid_cmp(const void *p1, const void *p2) +{ + const struct ident_pid *i1 = p1, *i2 = p2; + + if (i1->pid < i2->pid) + return -1; + else if (i1->pid > i2->pid) + return 1; + else + return strcmp(i1->ident, i2->ident); +} + +struct connect_limit *connect_limit_init(void) +{ + struct connect_limit *limit; + + limit = i_new(struct connect_limit, 1); + limit->ident_hash = + hash_table_create(default_pool, default_pool, 0, + str_hash, (hash_cmp_callback_t *)strcmp); + limit->ident_pid_hash = + hash_table_create(default_pool, default_pool, 0, + ident_pid_hash, ident_pid_cmp); + return limit; +} + +void connect_limit_deinit(struct connect_limit **_limit) +{ + struct connect_limit *limit = *_limit; + + *_limit = NULL; + hash_table_destroy(&limit->ident_hash); + hash_table_destroy(&limit->ident_pid_hash); + i_free(limit); +} + +unsigned int connect_limit_lookup(struct connect_limit *limit, + const char *ident) +{ + void *value; + + value = hash_table_lookup(limit->ident_hash, ident); + if (value == NULL) + return 0; + + return POINTER_CAST_TO(value, unsigned int); +} + +void connect_limit_connect(struct connect_limit *limit, pid_t pid, + const char *ident) +{ + struct ident_pid *i, lookup_i; + void *key, *value; + + if (!hash_table_lookup_full(limit->ident_hash, ident, &key, &value)) { + key = i_strdup(ident); + value = POINTER_CAST(1); + hash_table_insert(limit->ident_hash, key, value); + } else { + value = POINTER_CAST(POINTER_CAST_TO(value, unsigned int) + 1); + hash_table_update(limit->ident_hash, key, value); + } + + lookup_i.ident = ident; + lookup_i.pid = pid; + i = hash_table_lookup(limit->ident_pid_hash, &lookup_i); + if (i == NULL) { + i = i_new(struct ident_pid, 1); + i->ident = key; + i->pid = pid; + i->refcount = 1; + hash_table_insert(limit->ident_pid_hash, i, i); + } else { + i->refcount++; + } +} + +static void +connect_limit_ident_hash_unref(struct connect_limit *limit, const char *ident) +{ + void *key, *value; + unsigned int new_refcount; + + if (!hash_table_lookup_full(limit->ident_hash, ident, &key, &value)) + i_panic("connect limit hash tables are inconsistent"); + + new_refcount = POINTER_CAST_TO(value, unsigned int) - 1; + if (new_refcount > 0) { + value = POINTER_CAST(new_refcount); + hash_table_update(limit->ident_hash, key, value); + } else { + hash_table_remove(limit->ident_hash, key); + i_free(key); + } +} + +void connect_limit_disconnect(struct connect_limit *limit, pid_t pid, + const char *ident) +{ + struct ident_pid *i, lookup_i; + + lookup_i.ident = ident; + lookup_i.pid = pid; + + i = hash_table_lookup(limit->ident_pid_hash, &lookup_i); + if (i == NULL) { + i_error("connect limit: disconnection for unknown " + "pid %s + ident %s", dec2str(pid), ident); + return; + } + + if (--i->refcount == 0) { + hash_table_remove(limit->ident_pid_hash, i); + i_free(i); + } + + connect_limit_ident_hash_unref(limit, ident); +} + +void connect_limit_disconnect_pid(struct connect_limit *limit, pid_t pid) +{ + struct hash_iterate_context *iter; + struct ident_pid *i; + void *key, *value; + + /* this should happen rarely (or never), so this slow implementation + should be fine. */ + iter = hash_table_iterate_init(limit->ident_pid_hash); + while (hash_table_iterate(iter, &key, &value)) { + i = key; + if (i->pid == pid) { + hash_table_remove(limit->ident_pid_hash, i); + for (; i->refcount > 0; i->refcount--) + connect_limit_ident_hash_unref(limit, i->ident); + i_free(i); + } + } + hash_table_iterate_deinit(&iter); +} diff -r 783a81c3deb7 -r 2e2b957f1cca src/anvil/connect-limit.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/anvil/connect-limit.h Tue May 05 21:28:34 2009 -0400 @@ -0,0 +1,15 @@ +#ifndef CONNECT_LIMIT_H +#define CONNECT_LIMIT_H + +struct connect_limit *connect_limit_init(void); +void connect_limit_deinit(struct connect_limit **limit); + +unsigned int connect_limit_lookup(struct connect_limit *limit, + const char *ident); +void connect_limit_connect(struct connect_limit *limit, pid_t pid, + const char *ident); +void connect_limit_disconnect(struct connect_limit *limit, pid_t pid, + const char *ident); +void connect_limit_disconnect_pid(struct connect_limit *limit, pid_t pid); + +#endif diff -r 783a81c3deb7 -r 2e2b957f1cca src/anvil/main.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/anvil/main.c Tue May 05 21:28:34 2009 -0400 @@ -0,0 +1,42 @@ +/* Copyright (C) 2009 Dovecot authors, see the included COPYING file */ + +#include "common.h" +#include "array.h" +#include "env-util.h" +#include "master-service.h" +#include "connect-limit.h" +#include "anvil-connection.h" + +#include +#include + +struct connect_limit *connect_limit; + +static struct master_service *service; + +static void client_connected(const struct master_service_connection *conn) +{ + anvil_connection_create(conn->fd); +} + +int main(int argc, char *argv[]) +{ + int c; + + service = master_service_init("anvil", 0, argc, argv); + while ((c = getopt(argc, argv, master_service_getopt_string())) > 0) { + if (!master_service_parse_option(service, c, optarg)) + exit(FATAL_DEFAULT); + } + + master_service_init_log(service, "anvil: ", 0); + master_service_init_finish(service); + connect_limit = connect_limit_init(); + + master_service_run(service, client_connected); + + connect_limit_deinit(&connect_limit); + anvil_connections_destroy_all(); + master_service_deinit(&service); + return 0; +} diff -r 783a81c3deb7 -r 2e2b957f1cca src/imap-login/client.c --- a/src/imap-login/client.c Tue May 05 20:26:40 2009 -0400 +++ b/src/imap-login/client.c Tue May 05 21:28:34 2009 -0400 @@ -49,7 +49,7 @@ #define AUTH_MASTER_WAITING_MSG \ "* OK Waiting for authentication master process to respond.." -const char *login_protocol = "IMAP"; +const char *login_protocol = "imap"; const char *login_process_name = "imap-login"; static void client_set_title(struct imap_client *client) diff -r 783a81c3deb7 -r 2e2b957f1cca src/imap/imap-client.c --- a/src/imap/imap-client.c Tue May 05 20:26:40 2009 -0400 +++ b/src/imap/imap-client.c Tue May 05 21:28:34 2009 -0400 @@ -4,6 +4,7 @@ #include "ioloop.h" #include "llist.h" #include "str.h" +#include "hostpid.h" #include "network.h" #include "istream.h" #include "ostream.h" @@ -34,6 +35,7 @@ { struct client *client; struct mail_namespace *ns; + const char *ident; /* always use nonblocking I/O */ net_set_nonblock(fd_in, TRUE); @@ -68,6 +70,13 @@ str_append(client->capability_string, *set->imap_capability != '\0' ? set->imap_capability : CAPABILITY_STRING); + ident = mail_user_get_anvil_userip_ident(client->user); + if (ident != NULL) { + master_service_anvil_send(service, t_strconcat("CONNECT\t", + my_pid, "\t", ident, "/imap\n", NULL)); + client->anvil_sent = TRUE; + } + i_assert(my_client == NULL); my_client = client; @@ -174,6 +183,12 @@ client_search_updates_free(client); mailbox_close(&client->mailbox); } + if (client->anvil_sent) { + master_service_anvil_send(service, t_strconcat("DISCONNECT\t", + my_pid, "\t", + mail_user_get_anvil_userip_ident(client->user), "/imap" + "\n", NULL)); + } mail_user_unref(&client->user); if (client->free_parser != NULL) diff -r 783a81c3deb7 -r 2e2b957f1cca src/imap/imap-client.h --- a/src/imap/imap-client.h Tue May 05 20:26:40 2009 -0400 +++ b/src/imap/imap-client.h Tue May 05 21:28:34 2009 -0400 @@ -139,6 +139,7 @@ unsigned int syncing:1; unsigned int id_logged:1; unsigned int mailbox_examined:1; + unsigned int anvil_sent:1; unsigned int input_skip_line:1; /* skip all the data until we've found a new line */ unsigned int modseqs_sent_since_sync:1; diff -r 783a81c3deb7 -r 2e2b957f1cca src/lib-master/master-interface.h --- a/src/lib-master/master-interface.h Tue May 05 20:26:40 2009 -0400 +++ b/src/lib-master/master-interface.h Tue May 05 21:28:34 2009 -0400 @@ -60,9 +60,7 @@ enum master_auth_status { MASTER_AUTH_STATUS_OK, - MASTER_AUTH_STATUS_INTERNAL_ERROR, - /* user reached max. simultaneous connections */ - MASTER_AUTH_STATUS_MAX_CONNECTIONS + MASTER_AUTH_STATUS_INTERNAL_ERROR }; struct master_auth_reply { @@ -85,8 +83,9 @@ /* getenv(MASTER_DOVECOT_VERSION_ENV) provides master's version number */ #define MASTER_DOVECOT_VERSION_ENV "DOVECOT_VERSION" -/* points to /dev/null for now */ -#define MASTER_RESERVED_FD 3 +/* Write pipe to anvil. Currently available only for auth destination + services, for others it's /dev/null. */ +#define MASTER_ANVIL_FD 3 /* Socket for sending master_auth_requests. Also used by auth server process as a master socket. */ diff -r 783a81c3deb7 -r 2e2b957f1cca src/lib-master/master-service-settings.c --- a/src/lib-master/master-service-settings.c Tue May 05 20:26:40 2009 -0400 +++ b/src/lib-master/master-service-settings.c Tue May 05 21:28:34 2009 -0400 @@ -59,7 +59,7 @@ binary_path = service->argv[0]; path = getenv("PATH"); - if (*service->argv[0] != '/') { + if (*service->argv[0] != '/' && path != NULL) { /* we have to find our executable from path */ paths = t_strsplit(path, ":"); for (; *paths != NULL; paths++) { diff -r 783a81c3deb7 -r 2e2b957f1cca src/lib-master/master-service.c --- a/src/lib-master/master-service.c Tue May 05 20:26:40 2009 -0400 +++ b/src/lib-master/master-service.c Tue May 05 21:28:34 2009 -0400 @@ -356,6 +356,23 @@ io_loop_stop(service->ioloop); } +void master_service_anvil_send(struct master_service *service, const char *cmd) +{ + ssize_t ret; + + if ((service->flags & MASTER_SERVICE_FLAG_STANDALONE) != 0) + return; + + ret = write(MASTER_ANVIL_FD, cmd, strlen(cmd)); + if (ret < 0) + i_error("write(anvil) failed: %m"); + else if (ret == 0) + i_error("write(anvil) failed: EOF"); + else { + i_assert((size_t)ret == strlen(cmd)); + } +} + void master_service_client_connection_destroyed(struct master_service *service) { if (service->listeners == NULL) { diff -r 783a81c3deb7 -r 2e2b957f1cca src/lib-master/master-service.h --- a/src/lib-master/master-service.h Tue May 05 20:26:40 2009 -0400 +++ b/src/lib-master/master-service.h Tue May 05 21:28:34 2009 -0400 @@ -75,6 +75,8 @@ /* Stop a running service. */ void master_service_stop(struct master_service *service); +/* Send command to anvil process, if we have fd to it. */ +void master_service_anvil_send(struct master_service *service, const char *cmd); /* Call whenever a client connection is destroyed. */ void master_service_client_connection_destroyed(struct master_service *service); diff -r 783a81c3deb7 -r 2e2b957f1cca src/lib-storage/mail-user.c --- a/src/lib-storage/mail-user.c Tue May 05 20:26:40 2009 -0400 +++ b/src/lib-storage/mail-user.c Tue May 05 21:28:34 2009 -0400 @@ -5,6 +5,7 @@ #include "hostpid.h" #include "network.h" #include "str.h" +#include "strescape.h" #include "var-expand.h" #include "settings-parser.h" #include "auth-master.h" @@ -317,6 +318,14 @@ return mail_storage_get_temp_prefix(ns->storage); } +const char *mail_user_get_anvil_userip_ident(struct mail_user *user) +{ + if (user->remote_ip == NULL) + return NULL; + return t_strconcat(net_ip2addr(user->remote_ip), "/", + str_tabescape(user->username), NULL); +} + void mail_users_init(const char *auth_socket_path, bool debug) { auth_master_conn = auth_master_init(auth_socket_path, debug); diff -r 783a81c3deb7 -r 2e2b957f1cca src/lib-storage/mail-user.h --- a/src/lib-storage/mail-user.h Tue May 05 20:26:40 2009 -0400 +++ b/src/lib-storage/mail-user.h Tue May 05 21:28:34 2009 -0400 @@ -105,5 +105,7 @@ const char *mail_user_home_expand(struct mail_user *user, const char *path); /* Returns 0 if ok, -1 if home directory isn't set. */ int mail_user_try_home_expand(struct mail_user *user, const char **path); +/* Returns unique user+ip identifier for anvil. */ +const char *mail_user_get_anvil_userip_ident(struct mail_user *user); #endif diff -r 783a81c3deb7 -r 2e2b957f1cca src/login-common/common.h --- a/src/login-common/common.h Tue May 05 20:26:40 2009 -0400 +++ b/src/login-common/common.h Tue May 05 21:28:34 2009 -0400 @@ -16,6 +16,7 @@ extern struct auth_client *auth_client; extern bool closing_down; +extern int anvil_fd; extern struct master_service *service; extern struct login_settings *login_settings; diff -r 783a81c3deb7 -r 2e2b957f1cca src/login-common/login-settings.c --- a/src/login-common/login-settings.c Tue May 05 20:26:40 2009 -0400 +++ b/src/login-common/login-settings.c Tue May 05 21:28:34 2009 -0400 @@ -43,6 +43,7 @@ DEF(SET_BOOL, verbose_proctitle), DEF(SET_UINT, login_max_connections), + DEF(SET_UINT, mail_max_userip_connections), SETTING_DEFINE_LIST_END }; @@ -75,7 +76,8 @@ MEMBER(auth_debug) FALSE, MEMBER(verbose_proctitle) FALSE, - MEMBER(login_max_connections) 256 + MEMBER(login_max_connections) 256, + MEMBER(mail_max_userip_connections) 10 }; struct setting_parser_info login_setting_parser_info = { diff -r 783a81c3deb7 -r 2e2b957f1cca src/login-common/login-settings.h --- a/src/login-common/login-settings.h Tue May 05 20:26:40 2009 -0400 +++ b/src/login-common/login-settings.h Tue May 05 21:28:34 2009 -0400 @@ -31,6 +31,7 @@ bool verbose_proctitle; unsigned int login_max_connections; + unsigned int mail_max_userip_connections; /* generated: */ const char *const *log_format_elements_split; diff -r 783a81c3deb7 -r 2e2b957f1cca src/login-common/main.c --- a/src/login-common/main.c Tue May 05 20:26:40 2009 -0400 +++ b/src/login-common/main.c Tue May 05 21:28:34 2009 -0400 @@ -20,6 +20,7 @@ struct auth_client *auth_client; bool closing_down; +int anvil_fd = -1; struct master_service *service; struct login_settings *login_settings; @@ -65,6 +66,21 @@ clients_notify_auth_connected(); } +static int anvil_connect(void) +{ +#define ANVIL_HANDSHAKE "VERSION\t1\t0\n" + int fd; + + fd = net_connect_unix("anvil"); + if (fd < 0) + i_fatal("net_connect_unix(anvil) failed: %m"); + net_set_nonblock(fd, FALSE); + + if (write(fd, ANVIL_HANDSHAKE, strlen(ANVIL_HANDSHAKE)) < 0) + i_fatal("write(anvil) failed: %m"); + return fd; +} + static void main_preinit(void) { unsigned int max_fds; @@ -85,6 +101,9 @@ i_assert(strcmp(login_settings->ssl, "no") == 0 || ssl_initialized); + if (login_settings->mail_max_userip_connections > 0) + anvil_fd = anvil_connect(); + restrict_access_by_env(NULL, TRUE); } @@ -113,6 +132,11 @@ if (auth_client != NULL) auth_client_free(&auth_client); clients_deinit(); + + if (anvil_fd != -1) { + if (close(anvil_fd) < 0) + i_error("close(anvil) failed: %m"); + } master_auth_deinit(service); } diff -r 783a81c3deb7 -r 2e2b957f1cca src/login-common/sasl-server.c --- a/src/login-common/sasl-server.c Tue May 05 20:26:40 2009 -0400 +++ b/src/login-common/sasl-server.c Tue May 05 21:28:34 2009 -0400 @@ -4,6 +4,8 @@ #include "base64.h" #include "buffer.h" #include "istream.h" +#include "write-full.h" +#include "strescape.h" #include "str-sanitize.h" #include "auth-client.h" #include "ssl-proxy.h" @@ -11,6 +13,13 @@ #include "master-auth.h" #include "client-common.h" +#include +#include + +#define ERR_TOO_MANY_USERIP_CONNECTIONS \ + "Maximum number of connections from user+IP exceeded " \ + "(mail_max_userip_connections)" + static enum auth_request_flags client_get_auth_flags(struct client *client) { @@ -54,10 +63,6 @@ break; case MASTER_AUTH_STATUS_INTERNAL_ERROR: break; - case MASTER_AUTH_STATUS_MAX_CONNECTIONS: - data = "Maximum number of connections from user+IP exceeded " - "(mail_max_userip_connections)"; - break; } client->mail_pid = reply->mail_pid; call_client_callback(client, sasl_reply, data, NULL); @@ -92,6 +97,35 @@ master_auth_callback, client); } +static bool anvil_has_too_many_connections(struct client *client) +{ + const char *ident; + char buf[64]; + ssize_t ret; + + if (client->virtual_user == NULL) + return FALSE; + if (login_settings->mail_max_userip_connections == 0) + return FALSE; + + ident = t_strconcat("LOOKUP\t", net_ip2addr(&client->ip), "/", + str_tabescape(client->virtual_user), "/", + login_protocol, "\n", NULL); + if (write_full(anvil_fd, ident, strlen(ident)) < 0) + i_fatal("write(anvil) failed: %m"); + ret = read(anvil_fd, buf, sizeof(buf)-1); + if (ret < 0) + i_fatal("read(anvil) failed: %m"); + else if (ret == 0) + i_fatal("read(anvil) failed: EOF"); + if (buf[ret-1] != '\n') + i_fatal("anvil lookup failed: Invalid input in reply"); + buf[ret-1] = '\0'; + + return strtoul(buf, NULL, 10) >= + login_settings->mail_max_userip_connections; +} + static void authenticate_callback(struct auth_request *request, int status, const char *data_base64, const char *const *args, void *context) @@ -133,6 +167,11 @@ client->authenticating = FALSE; call_client_callback(client, SASL_SERVER_REPLY_SUCCESS, NULL, args); + } else if (anvil_has_too_many_connections(client)) { + client->authenticating = FALSE; + call_client_callback(client, + SASL_SERVER_REPLY_MASTER_FAILED, + ERR_TOO_MANY_USERIP_CONNECTIONS, NULL); } else { master_send_request(client, request); } diff -r 783a81c3deb7 -r 2e2b957f1cca src/master/Makefile.am --- a/src/master/Makefile.am Tue May 05 20:26:40 2009 -0400 +++ b/src/master/Makefile.am Tue May 05 21:28:34 2009 -0400 @@ -25,12 +25,14 @@ dup2-array.c \ main.c \ master-settings.c \ + service-anvil.c \ service-auth-server.c \ service-auth-source.c \ service-listen.c \ service-log.c \ service-monitor.c \ service-process.c \ + service-process-notify.c \ service.c noinst_HEADERS = \ @@ -38,10 +40,12 @@ common.h \ dup2-array.h \ master-settings.h \ + service-anvil.h \ service-auth-server.h \ service-auth-source.h \ service-listen.h \ service-log.h \ service-monitor.h \ service-process.h \ + service-process-notify.h \ service.h diff -r 783a81c3deb7 -r 2e2b957f1cca src/master/master-settings.c --- a/src/master/master-settings.c Tue May 05 20:26:40 2009 -0400 +++ b/src/master/master-settings.c Tue May 05 21:28:34 2009 -0400 @@ -283,6 +283,7 @@ if (*services[i]->type != '\0' && strcmp(services[i]->type, "log") != 0 && strcmp(services[i]->type, "config") != 0 && + strcmp(services[i]->type, "anvil") != 0 && strcmp(services[i]->type, "auth") != 0 && strcmp(services[i]->type, "auth-source") != 0) { *error_r = t_strconcat("Unknown service type: ", @@ -428,7 +429,7 @@ bool master_settings_do_fixes(const struct master_settings *set) { - const char *login_dir; + const char *login_dir, *empty_dir; struct stat st; gid_t gid; @@ -479,5 +480,11 @@ return FALSE; } } + + empty_dir = t_strconcat(set->base_dir, "/empty", NULL); + if (safe_mkdir(empty_dir, 0755, master_uid, getegid()) == 0) { + i_warning("Corrected permissions for empty directory " + "%s", empty_dir); + } return TRUE; } diff -r 783a81c3deb7 -r 2e2b957f1cca src/master/service-anvil.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/master/service-anvil.c Tue May 05 21:28:34 2009 -0400 @@ -0,0 +1,92 @@ +/* Copyright (c) 2009 Dovecot authors, see the included COPYING file */ + +#include "common.h" +#include "fd-close-on-exec.h" +#include "fd-set-nonblock.h" +#include "service.h" +#include "service-process.h" +#include "service-process-notify.h" +#include "service-anvil.h" + +#include + +#define ANVIL_HANDSHAKE "VERSION\t1\t0\n" + +static int anvil_send_handshake(int fd, const char **error_r) +{ + ssize_t ret; + + ret = write(fd, ANVIL_HANDSHAKE, strlen(ANVIL_HANDSHAKE)); + if (ret < 0) { + *error_r = t_strdup_printf("write(anvil) failed: %m"); + return -1; + } + if (ret == 0) { + *error_r = t_strdup_printf("write(anvil) returned EOF"); + return -1; + } + /* this is a pipe, it either wrote all of it or nothing */ + i_assert(ret == strlen(ANVIL_HANDSHAKE)); + return 0; +} + +static int +service_process_write_anvil_kill(int fd, struct service_process *process) +{ + const char *data; + + data = t_strdup_printf("KILL\t%s\n", dec2str(process->pid)); + if (write(fd, data, strlen(data)) < 0) { + if (errno != EAGAIN) + i_error("write(anvil process) failed: %m"); + return -1; + } + return 0; +} + +int service_list_init_anvil(struct service_list *service_list, + const char **error_r) +{ + if (pipe(service_list->blocking_anvil_fd) < 0) { + *error_r = t_strdup_printf("pipe() failed: %m"); + return -1; + } + if (pipe(service_list->nonblocking_anvil_fd) < 0) { + (void)close(service_list->blocking_anvil_fd[0]); + (void)close(service_list->blocking_anvil_fd[1]); + *error_r = t_strdup_printf("pipe() failed: %m"); + return -1; + } + fd_set_nonblock(service_list->nonblocking_anvil_fd[1], TRUE); + + fd_close_on_exec(service_list->blocking_anvil_fd[0], TRUE); + fd_close_on_exec(service_list->blocking_anvil_fd[1], TRUE); + fd_close_on_exec(service_list->nonblocking_anvil_fd[0], TRUE); + fd_close_on_exec(service_list->nonblocking_anvil_fd[1], TRUE); + + if (anvil_send_handshake(service_list->blocking_anvil_fd[1], + error_r) < 0) + return -1; + if (anvil_send_handshake(service_list->nonblocking_anvil_fd[1], + error_r) < 0) + return -1; + + i_assert(service_list->anvil_kills == NULL); + service_list->anvil_kills = + service_process_notify_init(service_list->nonblocking_anvil_fd[1], + service_process_write_anvil_kill); + return 0; +} + +void service_list_deinit_anvil(struct service_list *service_list) +{ + service_process_notify_deinit(&service_list->anvil_kills); + if (close(service_list->blocking_anvil_fd[0]) < 0) + i_error("close(anvil) failed: %m"); + if (close(service_list->blocking_anvil_fd[1]) < 0) + i_error("close(anvil) failed: %m"); + if (close(service_list->nonblocking_anvil_fd[0]) < 0) + i_error("close(anvil) failed: %m"); + if (close(service_list->nonblocking_anvil_fd[1]) < 0) + i_error("close(anvil) failed: %m"); +} diff -r 783a81c3deb7 -r 2e2b957f1cca src/master/service-anvil.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/master/service-anvil.h Tue May 05 21:28:34 2009 -0400 @@ -0,0 +1,8 @@ +#ifndef SERVICE_ANVIL_H +#define SERVICE_ANVIL_H + +int service_list_init_anvil(struct service_list *service_list, + const char **error_r); +void service_list_deinit_anvil(struct service_list *service_list); + +#endif diff -r 783a81c3deb7 -r 2e2b957f1cca src/master/service-auth-server.c --- a/src/master/service-auth-server.c Tue May 05 20:26:40 2009 -0400 +++ b/src/master/service-auth-server.c Tue May 05 21:28:34 2009 -0400 @@ -107,11 +107,8 @@ request->process->process.service->auth_dest_service; struct service_process *dest_process; - /* FIXME: handle MASTER_AUTH_STATUS_MAX_CONNECTIONS */ dest_process = service_process_create(dest_service, list + 1, - request->fd, - request->data, - request->data_size); + request); status = dest_process != NULL ? MASTER_AUTH_STATUS_OK : MASTER_AUTH_STATUS_INTERNAL_ERROR; diff -r 783a81c3deb7 -r 2e2b957f1cca src/master/service-log.c --- a/src/master/service-log.c Tue May 05 20:26:40 2009 -0400 +++ b/src/master/service-log.c Tue May 05 21:28:34 2009 -0400 @@ -9,6 +9,7 @@ #include "fd-set-nonblock.h" #include "service.h" #include "service-process.h" +#include "service-process-notify.h" #include "service-log.h" #include @@ -48,6 +49,22 @@ return 0; } +static int +service_process_write_log_bye(int fd, struct service_process *process) +{ + const char *data; + + data = t_strdup_printf("%d %s BYE\n", + process->service->log_process_internal_fd, + dec2str(process->pid)); + if (write(fd, data, strlen(data)) < 0) { + if (errno != EAGAIN) + i_error("write(log process) failed: %m"); + return -1; + } + return 0; +} + int services_log_init(struct service_list *service_list) { struct service *const *services; @@ -66,6 +83,11 @@ else fd_set_nonblock(service_list->master_log_fd[1], TRUE); + i_assert(service_list->log_byes == NULL); + service_list->log_byes = + service_process_notify_init(service_list->master_log_fd[1], + service_process_write_log_bye); + n = 1; for (i = 0; i < count; i++) { if (services[i]->type == SERVICE_TYPE_LOG) @@ -90,26 +112,6 @@ return 0; } -void services_log_clear_byes(struct service_list *service_list) -{ - struct service_process *const *processes, *process; - unsigned int i, count; - - if (service_list->io_log_write == NULL) - return; - - processes = array_idx_modifiable(&service_list->bye_arr, 0); - count = aqueue_count(service_list->bye_queue); - for (i = 0; i < count; i++) { - process = processes[aqueue_idx(service_list->bye_queue, i)]; - service_process_unref(process); - } - aqueue_clear(service_list->bye_queue); - array_clear(&service_list->bye_arr); - - io_remove(&service_list->io_log_write); -} - void services_log_deinit(struct service_list *service_list) { struct service *const *services; @@ -131,7 +133,7 @@ services[i]->log_process_internal_fd = -1; } } - services_log_clear_byes(service_list); + service_process_notify_deinit(&service_list->log_byes); if (service_list->master_log_fd[0] != -1) { if (close(service_list->master_log_fd[0]) < 0) i_error("close(master log fd) failed: %m"); diff -r 783a81c3deb7 -r 2e2b957f1cca src/master/service-log.h --- a/src/master/service-log.h Tue May 05 20:26:40 2009 -0400 +++ b/src/master/service-log.h Tue May 05 21:28:34 2009 -0400 @@ -6,7 +6,6 @@ int services_log_init(struct service_list *service_list); void services_log_deinit(struct service_list *service_list); -void services_log_clear_byes(struct service_list *service_list); void services_log_dup2(ARRAY_TYPE(dup2) *dups, struct service_list *service_list, unsigned int first_fd, unsigned int *fd_count); diff -r 783a81c3deb7 -r 2e2b957f1cca src/master/service-monitor.c --- a/src/master/service-monitor.c Tue May 05 20:26:40 2009 -0400 +++ b/src/master/service-monitor.c Tue May 05 21:28:34 2009 -0400 @@ -7,6 +7,7 @@ #include "hash.h" #include "service.h" #include "service-process.h" +#include "service-process-notify.h" #include "service-log.h" #include "service-monitor.h" @@ -129,7 +130,7 @@ } /* create a child process and let it accept() this connection */ - if (service_process_create(service, NULL, -1, NULL, 0) == NULL) + if (service_process_create(service, NULL, NULL) == NULL) service_monitor_throttle(service); else service_monitor_listen_stop(service); @@ -195,9 +196,9 @@ service_monitor_listen_start(services[i]); } - if (service_process_create(service_list->log, NULL, -1, NULL, 0) != NULL) + if (service_process_create(service_list->log, NULL, NULL) != NULL) service_monitor_listen_stop(service_list->log); - if (service_process_create(service_list->config, NULL, -1, NULL, 0) != NULL) + if (service_process_create(service_list->config, NULL, NULL) != NULL) service_monitor_listen_stop(service_list->config); } @@ -235,6 +236,18 @@ services_log_deinit(service_list); } +static void service_process_failure(struct service_process *process, int status) +{ + struct service *service = process->service; + + service_process_log_status_error(process, status); + if (process->total_count == 0) + service_monitor_throttle(service); + + if (service->list->anvil_kills != NULL) + service_process_notify_add(service->list->anvil_kills, process); +} + void services_monitor_reap_children(struct service_list *service_list) { struct service_process *process; @@ -256,12 +269,8 @@ if (service->listen_pending) service_monitor_listen_start(service); } else { - /* failure */ - service_process_log_status_error(process, status); - if (process->total_count == 0) - service_monitor_throttle(service); + service_process_failure(process, status); } - service_process_destroy(process); if (service->process_avail == 0 && service->to_throttle == NULL) diff -r 783a81c3deb7 -r 2e2b957f1cca src/master/service-process-notify.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/master/service-process-notify.c Tue May 05 21:28:34 2009 -0400 @@ -0,0 +1,102 @@ +/* Copyright (c) 2009 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "array.h" +#include "aqueue.h" +#include "ioloop.h" +#include "service.h" +#include "service-process.h" +#include "service-process-notify.h" + +struct service_process_notify { + service_process_notify_callback_t *write_callback; + + int fd; + struct io *io_write; + struct aqueue *process_queue; + ARRAY_DEFINE(processes, struct service_process *); +}; + +struct service_process_notify * +service_process_notify_init(int fd, + service_process_notify_callback_t *write_callback) +{ + struct service_process_notify *notify; + + notify = i_new(struct service_process_notify, 1); + notify->fd = fd; + notify->write_callback = write_callback; + + i_array_init(¬ify->processes, 64); + notify->process_queue = aqueue_init(¬ify->processes.arr); + return notify; +} + +static void service_process_notify_reset(struct service_process_notify *notify) +{ + struct service_process *const *processes, *process; + unsigned int i, count; + + if (notify->io_write == NULL) + return; + + processes = array_idx_modifiable(¬ify->processes, 0); + count = aqueue_count(notify->process_queue); + for (i = 0; i < count; i++) { + process = processes[aqueue_idx(notify->process_queue, i)]; + service_process_unref(process); + } + aqueue_clear(notify->process_queue); + array_clear(¬ify->processes); + + io_remove(¬ify->io_write); +} + +static void notify_flush(struct service_process_notify *notify) +{ + struct service_process *const *processes, *process; + + while (aqueue_count(notify->process_queue) > 0) { + processes = array_idx_modifiable(¬ify->processes, 0); + process = processes[aqueue_idx(notify->process_queue, 0)]; + + if (notify->write_callback(notify->fd, process) < 0) { + if (errno != EAGAIN) + service_process_notify_reset(notify); + return; + } + service_process_unref(process); + aqueue_delete_tail(notify->process_queue); + } + io_remove(¬ify->io_write); +} + +void service_process_notify_deinit(struct service_process_notify **_notify) +{ + struct service_process_notify *notify = *_notify; + + *_notify = NULL; + + service_process_notify_reset(notify); + if (notify->io_write != NULL) + io_remove(¬ify->io_write); + aqueue_deinit(¬ify->process_queue); + array_free(¬ify->processes); + i_free(notify); +} + +void service_process_notify_add(struct service_process_notify *notify, + struct service_process *process) +{ + if (notify->write_callback(notify->fd, process) < 0) { + if (errno != EAGAIN) + return; + + if (notify->io_write == NULL) { + notify->io_write = io_add(notify->fd, IO_WRITE, + notify_flush, notify); + } + aqueue_append(notify->process_queue, &process); + service_process_ref(process); + } +} diff -r 783a81c3deb7 -r 2e2b957f1cca src/master/service-process-notify.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/master/service-process-notify.h Tue May 05 21:28:34 2009 -0400 @@ -0,0 +1,15 @@ +#ifndef SERVICE_PROCESS_NOTIFY_H +#define SERVICE_PROCESS_NOTIFY_H + +typedef int +service_process_notify_callback_t(int fd, struct service_process *process); + +struct service_process_notify * +service_process_notify_init(int fd, + service_process_notify_callback_t *write_callback); +void service_process_notify_deinit(struct service_process_notify **notify); + +void service_process_notify_add(struct service_process_notify *notify, + struct service_process *process); + +#endif diff -r 783a81c3deb7 -r 2e2b957f1cca src/master/service-process.c --- a/src/master/service-process.c Tue May 05 20:26:40 2009 -0400 +++ b/src/master/service-process.c Tue May 05 21:28:34 2009 -0400 @@ -20,6 +20,7 @@ #include "service-log.h" #include "service-auth-server.h" #include "service-auth-source.h" +#include "service-process-notify.h" #include "service-process.h" #include @@ -30,7 +31,8 @@ #include static void -service_dup_fds(struct service *service, int auth_fd, int std_fd) +service_dup_fds(struct service *service, int auth_fd, int std_fd, + bool give_anvil_fd) { struct service_listener *const *listeners; ARRAY_TYPE(dup2) dups; @@ -47,11 +49,24 @@ listeners = array_get(&service->listeners, &count); t_array_init(&dups, count + 10); - if (service->type == SERVICE_TYPE_LOG) { + switch (service->type) { + case SERVICE_TYPE_LOG: i_assert(n == 0); services_log_dup2(&dups, service->list, MASTER_LISTEN_FD_FIRST, &socket_listener_count); n += socket_listener_count; + break; + case SERVICE_TYPE_ANVIL: + /* nonblocking anvil fd must be the first one. anvil treats it + as the master's fd */ + dup2_append(&dups, service->list->nonblocking_anvil_fd[0], + MASTER_LISTEN_FD_FIRST + n++); + dup2_append(&dups, service->list->blocking_anvil_fd[0], + MASTER_LISTEN_FD_FIRST + n++); + socket_listener_count += 2; + break; + default: + break; } /* first add non-ssl listeners */ @@ -77,7 +92,12 @@ } } - dup2_append(&dups, null_fd, MASTER_RESERVED_FD); + if (!give_anvil_fd) + dup2_append(&dups, null_fd, MASTER_ANVIL_FD); + else { + dup2_append(&dups, service->list->blocking_anvil_fd[1], + MASTER_ANVIL_FD); + } dup2_append(&dups, service->status_fd[1], MASTER_STATUS_FD); switch (service->type) { @@ -326,14 +346,32 @@ timeout_remove(&process->to_status); } +static void +handle_request(const struct service_process_auth_request *request) +{ + string_t *str; + + if (request == NULL) + return; + + if (request->data_size > 0) { + str = t_str_new(request->data_size*3); + str_append(str, "CLIENT_INPUT="); + base64_encode(request->data, request->data_size, str); + env_put(str_c(str)); + } + + env_put(t_strconcat("LOCAL_IP=", net_ip2addr(&request->local_ip), NULL)); + env_put(t_strconcat("IP=", net_ip2addr(&request->remote_ip), NULL)); +} + struct service_process * service_process_create(struct service *service, const char *const *auth_args, - int std_fd, const unsigned char *data, size_t data_size) + const struct service_process_auth_request *request) { static unsigned int uid_counter = 0; struct service_process *process; unsigned int uid = ++uid_counter; - string_t *str; int fd[2]; pid_t pid; @@ -366,13 +404,9 @@ if (fd[0] != -1) (void)close(fd[0]); service_process_setup_environment(service, uid); - if (data_size > 0) { - str = t_str_new(data_size*3); - str_append(str, "CLIENT_INPUT="); - base64_encode(data, data_size, str); - env_put(str_c(str)); - } - service_dup_fds(service, fd[1], std_fd); + handle_request(request); + service_dup_fds(service, fd[1], request == NULL ? -1 : + request->fd, auth_args != NULL); drop_privileges(service, auth_args); process_exec(service->executable, NULL); } @@ -418,65 +452,6 @@ return process; } -static int service_process_write_bye(struct service_process *process) -{ - const char *data; - - data = t_strdup_printf("%d %s BYE\n", - process->service->log_process_internal_fd, - dec2str(process->pid)); - if (write(process->service->list->master_log_fd[1], - data, strlen(data)) < 0) { - if (errno != EAGAIN) - i_error("write(log process) failed: %m"); - return -1; - } - return 0; -} - -static void service_list_log_flush_byes(struct service_list *service_list) -{ - struct service_process *const *processes, *process; - - while (aqueue_count(service_list->bye_queue) > 0) { - processes = array_idx_modifiable(&service_list->bye_arr, 0); - process = processes[aqueue_idx(service_list->bye_queue, 0)]; - - if (service_process_write_bye(process) < 0) { - if (errno != EAGAIN) - services_log_clear_byes(service_list); - return; - } - service_process_unref(process); - aqueue_delete_tail(service_list->bye_queue); - } - io_remove(&service_list->io_log_write); -} - -static void service_process_log_bye(struct service_process *process) -{ - struct service_list *service_list = process->service->list; - - if (process->service->log_fd[1] == -1) { - /* stopping all services */ - return; - } - - if (service_process_write_bye(process) < 0) { - if (errno != EAGAIN) - return; - - if (service_list->io_log_write == NULL) { - service_list->io_log_write = - io_add(service_list->master_log_fd[1], IO_WRITE, - service_list_log_flush_byes, - service_list); - } - aqueue_append(service_list->bye_queue, &process); - service_process_ref(process); - } -} - void service_process_destroy(struct service_process *process) { struct service *service = process->service; @@ -502,7 +477,8 @@ break; } - service_process_log_bye(process); + if (service->list->log_byes != NULL) + service_process_notify_add(service->list->log_byes, process); process->destroyed = TRUE; service_process_unref(process); diff -r 783a81c3deb7 -r 2e2b957f1cca src/master/service-process.h --- a/src/master/service-process.h Tue May 05 20:26:40 2009 -0400 +++ b/src/master/service-process.h Tue May 05 21:28:34 2009 -0400 @@ -64,7 +64,7 @@ struct service_process * service_process_create(struct service *service, const char *const *auth_args, - int std_fd, const unsigned char *data, size_t data_size); + const struct service_process_auth_request *request); void service_process_destroy(struct service_process *process); void service_process_ref(struct service_process *process); diff -r 783a81c3deb7 -r 2e2b957f1cca src/master/service.c --- a/src/master/service.c Tue May 05 20:26:40 2009 -0400 +++ b/src/master/service.c Tue May 05 21:28:34 2009 -0400 @@ -6,6 +6,7 @@ #include "hash.h" #include "str.h" #include "service.h" +#include "service-anvil.h" #include "service-process.h" #include "service-monitor.h" @@ -136,6 +137,8 @@ service->type = SERVICE_TYPE_LOG; else if (strcmp(set->type, "config") == 0) service->type = SERVICE_TYPE_CONFIG; + else if (strcmp(set->type, "anvil") == 0) + service->type = SERVICE_TYPE_ANVIL; else if (strcmp(set->type, "auth") == 0) service->type = SERVICE_TYPE_AUTH_SERVER; else if (strcmp(set->type, "auth-source") == 0) @@ -370,10 +373,11 @@ return NULL; } + if (service_list_init_anvil(service_list, error_r) < 0) + return NULL; + service_list->pids = hash_table_create(default_pool, pool, 0, pid_hash, pid_hash_cmp); - p_array_init(&service_list->bye_arr, pool, 64); - service_list->bye_queue = aqueue_init(&service_list->bye_arr.arr); return service_list; } @@ -413,7 +417,7 @@ service_process_destroy(value); hash_table_iterate_deinit(&iter); + service_list_deinit_anvil(service_list); hash_table_destroy(&service_list->pids); - aqueue_deinit(&service_list->bye_queue); pool_unref(&service_list->pool); } diff -r 783a81c3deb7 -r 2e2b957f1cca src/master/service.h --- a/src/master/service.h Tue May 05 20:26:40 2009 -0400 +++ b/src/master/service.h Tue May 05 21:28:34 2009 -0400 @@ -3,6 +3,8 @@ #include "network.h" +struct master_settings; + /* If a service process doesn't send its first status notification in this many seconds, kill the process */ #define SERVICE_FIRST_STATUS_TIMEOUT_SECS 30 @@ -10,6 +12,7 @@ enum service_type { SERVICE_TYPE_UNKNOWN, SERVICE_TYPE_LOG, + SERVICE_TYPE_ANVIL, SERVICE_TYPE_CONFIG, SERVICE_TYPE_AUTH_SERVER, SERVICE_TYPE_AUTH_SOURCE @@ -97,11 +100,13 @@ /* nonblocking log fds usd by master */ int master_log_fd[2]; - /* we're waiting to be able to send "bye" to log process */ - struct io *io_log_write; - /* List of processes who are waiting for the "bye" */ - struct aqueue *bye_queue; - ARRAY_DEFINE(bye_arr, struct service_process *); + struct service_process_notify *log_byes; + + /* passed to auth destination processes */ + int blocking_anvil_fd[2]; + /* used by master process to notify about dying processes */ + int nonblocking_anvil_fd[2]; + struct service_process_notify *anvil_kills; ARRAY_DEFINE(services, struct service *); }; diff -r 783a81c3deb7 -r 2e2b957f1cca src/pop3-login/client.c --- a/src/pop3-login/client.c Tue May 05 20:26:40 2009 -0400 +++ b/src/pop3-login/client.c Tue May 05 21:28:34 2009 -0400 @@ -35,7 +35,7 @@ # error client idle timeout must be smaller than authentication timeout #endif -const char *login_protocol = "POP3"; +const char *login_protocol = "pop3"; const char *login_process_name = "pop3-login"; static void client_set_title(struct pop3_client *client) diff -r 783a81c3deb7 -r 2e2b957f1cca src/pop3/pop3-client.c --- a/src/pop3/pop3-client.c Tue May 05 20:26:40 2009 -0400 +++ b/src/pop3/pop3-client.c Tue May 05 21:28:34 2009 -0400 @@ -7,6 +7,7 @@ #include "istream.h" #include "ostream.h" #include "str.h" +#include "hostpid.h" #include "var-expand.h" #include "master-service.h" #include "mail-storage.h" @@ -215,7 +216,7 @@ const struct pop3_settings *set) { struct mail_storage *storage; - const char *inbox; + const char *inbox, *ident; struct client *client; enum mailbox_open_flags flags; const char *errmsg; @@ -286,6 +287,13 @@ if (!set->pop3_no_flag_updates && client->messages_count > 0) client->seen_bitmask = i_malloc(MSGS_BITMASK_SIZE(client)); + ident = mail_user_get_anvil_userip_ident(client->user); + if (ident != NULL) { + master_service_anvil_send(service, t_strconcat("CONNECT\t", + my_pid, "\t", ident, "/pop3\n", NULL)); + client->anvil_sent = TRUE; + } + i_assert(my_client == NULL); my_client = client; @@ -364,6 +372,12 @@ } if (client->mailbox != NULL) mailbox_close(&client->mailbox); + if (client->anvil_sent) { + master_service_anvil_send(service, t_strconcat("DISCONNECT\t", + my_pid, "\t", + mail_user_get_anvil_userip_ident(client->user), "/pop3" + "\n", NULL)); + } mail_user_unref(&client->user); i_free(client->message_sizes); diff -r 783a81c3deb7 -r 2e2b957f1cca src/pop3/pop3-client.h --- a/src/pop3/pop3-client.h Tue May 05 20:26:40 2009 -0400 +++ b/src/pop3/pop3-client.h Tue May 05 21:28:34 2009 -0400 @@ -56,6 +56,7 @@ unsigned int disconnected:1; unsigned int deleted:1; unsigned int waiting_input:1; + unsigned int anvil_sent:1; }; /* Create new client with specified input/output handles. socket specifies