]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
Initial commit for v2.0 master rewrite. Several features are still missing.
authorTimo Sirainen <tss@iki.fi>
Thu, 23 Apr 2009 23:53:44 +0000 (19:53 -0400)
committerTimo Sirainen <tss@iki.fi>
Thu, 23 Apr 2009 23:53:44 +0000 (19:53 -0400)
--HG--
branch : HEAD

111 files changed:
.hgignore
TODO
configure.in
src/Makefile.am
src/auth/Makefile.am
src/auth/auth-client-connection.c
src/auth/auth-client-connection.h
src/auth/auth-master-connection.c
src/auth/auth-master-connection.h
src/auth/auth-master-interface.h
src/auth/auth-master-listener.c [deleted file]
src/auth/auth-master-listener.h [deleted file]
src/auth/auth-settings.c
src/auth/auth-settings.h
src/auth/auth-worker-client.c
src/auth/common.h
src/auth/db-ldap.c
src/auth/main.c
src/config/Makefile.am
src/config/common.h
src/config/config-connection.c
src/config/config-connection.h
src/config/main.c
src/config/settings-get.pl
src/imap-login/Makefile.am
src/imap-login/client.c
src/imap-login/client.h
src/imap-login/imap-proxy.c
src/imap/main.c
src/lda/main.c
src/lib-master/Makefile.am
src/lib-master/master-auth.c [new file with mode: 0644]
src/lib-master/master-auth.h [new file with mode: 0644]
src/lib-master/master-interface.h [new file with mode: 0644]
src/lib-master/master-service-private.h
src/lib-master/master-service-settings.c
src/lib-master/master-service.c
src/lib-master/master-service.h
src/lib-settings/settings-parser.c
src/lib-settings/settings-parser.h
src/lib-storage/mail-storage-service.c
src/lib-storage/mail-storage-settings.c
src/lib-storage/mail-storage-settings.h
src/lib/failures.c
src/lib/failures.h
src/lib/restrict-access.c
src/lib/restrict-access.h
src/lmtp/client.c
src/lmtp/main.c
src/log/Makefile.am [new file with mode: 0644]
src/log/common.h [new file with mode: 0644]
src/log/log-connection.c [new file with mode: 0644]
src/log/log-connection.h [new file with mode: 0644]
src/log/main.c [new file with mode: 0644]
src/login-common/Makefile.am
src/login-common/client-common.h
src/login-common/common.h
src/login-common/login-proxy.c
src/login-common/login-proxy.h
src/login-common/login-settings.c
src/login-common/login-settings.h
src/login-common/main.c
src/login-common/master.c [deleted file]
src/login-common/master.h [deleted file]
src/login-common/sasl-server.c
src/login-common/sasl-server.h
src/login-common/ssl-proxy-openssl.c
src/login-common/ssl-proxy.c
src/login-common/ssl-proxy.h
src/master/Makefile.am
src/master/auth-process.c [deleted file]
src/master/auth-process.h [deleted file]
src/master/child-process.c [deleted file]
src/master/child-process.h [deleted file]
src/master/common.h
src/master/dict-process.c [deleted file]
src/master/dict-process.h [deleted file]
src/master/listener.c [deleted file]
src/master/listener.h [deleted file]
src/master/log.c [deleted file]
src/master/log.h [deleted file]
src/master/login-process.c [deleted file]
src/master/login-process.h [deleted file]
src/master/mail-process.c [deleted file]
src/master/mail-process.h [deleted file]
src/master/main.c
src/master/master-login-interface.h [deleted file]
src/master/master-settings.c
src/master/master-settings.h
src/master/service-auth-server.c [new file with mode: 0644]
src/master/service-auth-server.h [new file with mode: 0644]
src/master/service-auth-source.c [new file with mode: 0644]
src/master/service-auth-source.h [new file with mode: 0644]
src/master/service-listen.c [new file with mode: 0644]
src/master/service-listen.h [new file with mode: 0644]
src/master/service-log.c [new file with mode: 0644]
src/master/service-log.h [new file with mode: 0644]
src/master/service-monitor.c [new file with mode: 0644]
src/master/service-monitor.h [new file with mode: 0644]
src/master/service-process.c [new file with mode: 0644]
src/master/service-process.h [new file with mode: 0644]
src/master/service.c [new file with mode: 0644]
src/master/service.h [new file with mode: 0644]
src/master/sysinfo-get.c [deleted file]
src/master/sysinfo-get.h [deleted file]
src/plugins/convert/convert-tool.c
src/pop3-login/Makefile.am
src/pop3-login/client.c
src/pop3-login/client.h
src/pop3-login/pop3-proxy.c
src/pop3/main.c

index 8229f1424b4b274eec06badba79bf9eb550a970e..093567bf54a5a5246ce62090c24c244027f2eb67 100644 (file)
--- a/.hgignore
+++ b/.hgignore
@@ -65,6 +65,7 @@ src/lib-dict/dict-drivers-register.c
 src/lib-sql/sql-drivers-register.c
 src/lib-storage/register/mail-storage-register.c
 src/lib-storage/register/mailbox-list-register.c
+src/log/log
 src/lmtp/lmtp
 src/master/dovecot
 src/master/ssl-build-param
diff --git a/TODO b/TODO
index c8e6ccb08559ce3cfd6871d1d6963e6b15ff43e9..5dfb99c9eb8758f6f04b9e760f13fde7b6320a31 100644 (file)
--- a/TODO
+++ b/TODO
@@ -1,21 +1,30 @@
+ - mail_max_userip_connections
+ - 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,
+   .libs/*.a changes not
+ - o_stream_set_file_path()
  - dict pooling
  - config rewrite
-   - go through mail-process.c. nfs test?
-   - support !include and !include_try
-   - add back all setting verification code from master
-   - master_user, acl_groups are now plugin envs.. is it correct?
+   - dovecot -o setting=something overriding
+   - fix dovecot -n, dovecot -a
+   - export only the stuff that's actually wanted by the service. requires
+     some kind of dependencies or something to get e.g. imap to load also mail.
 
        /* currently non-external transactions can be applied multiple times,
           causing multiple increments. */
        //FIXME:i_assert((t->flags & MAIL_INDEX_TRANSACTION_FLAG_EXTERNAL) != 0);
   ^ appears to work now though, probably because of the added syncing stuff..
 
+ - dbox: keep track of total bytes in dbox storage in map header. also if
+   possible keep track of refcount=0 bytes. use these to optimize checks.
  - dbox: save some stuff to map index header so we don't need to keep retrying
    it. like when saving the lowest file_id which to bother checking.
  - transaction log corruption should make sure dovecot.index is rewritten
    and perhaps not delete the file.
  - dbox: test crash-fixing
- - dbox: cleanup tool / remove automatic cleanup. resyncing tool.
  - use backup index in mail_index_fsck()
  - dbox: mail_index_fsck() should perhaps cause dbox to be resynced?
 
@@ -46,7 +55,7 @@
  - add anonymous environment for anon logins
  - fs quota: getquotaroot inbox vs. other-box should return different quotas
    if two quotas are defined
- - deliver: log mailbox name using utf8, not mutf7
+ - lda: log mailbox name using utf8, not mutf7
  - new primes code: are hash tables now being resized too often?
  - auth_log_prefix setting similar to mail_log_prefix
  - LDAP attrs: uid=foo,uid=bar doesn't work
    - Deny deleting non-empty mailboxes
    - Disable IDLE "still here" notifications
 
- - maildir+pop3/deliver fast updates:
+ - maildir+pop3 fast updates:
    - with locking enabled, pop3 could just keep the one and same sync lock and
      do the whole thing using sync transaction
    - don't update dovecot-uidlist if dovecot.index.cache doesn't exist /
  - maildir
    - don't allow more than 26 keywords
    - physical separator could be configurable
-   - deliver+maildir: if new mails are in new/ or cur/ they're not added to
+   - lda+maildir: if new mails are in new/ or cur/ they're not added to
      dovecot-uidlist but newly saved mails are, so UIDs will be in wrong order
    - maildir_copy_with_hardlinks: We're currently first hardlinking to tmp/ and
      then rename()ing. This wouldn't be necessary if uidlist syncing noticed
    http://www.dovecot.org/list/dovecot/2007-April/021532.html
  - EXPUNGE command in read-only mailbox should give an error message if
    there are messages marked as \Deleted?
- - dovecot -o setting=something overriding
  - file_cache: we're growing the mmap in page size blocks, which is horribly
    slow if mremap() doesn't exist.
  - login_max_processes_count shouldn't count proxying processes
index 12ee25d58c2e2e41dd0d6eb997c9c7ce2fbbde59..61ef0ef817e926b81acbf3db2e5816c189ad666c 100644 (file)
@@ -1,5 +1,5 @@
 AC_PREREQ([2.59])
-AC_INIT([Dovecot],[1.3.UNSTABLE],[dovecot@dovecot.org])
+AC_INIT([Dovecot],[2.0.UNSTABLE],[dovecot@dovecot.org])
 AC_CONFIG_SRCDIR([src])
 
 AM_INIT_AUTOMAKE([foreign])
@@ -2200,7 +2200,7 @@ if test "$want_shared_libs" = "yes"; then
   LIBDOVECOT_STORAGE='$(top_builddir)/src/lib-storage/libdovecot-storage.la'
   LIBDOVECOT_LOGIN='$(top_builddir)/src/login-common/libdovecot-login.la'
 else
-  LIBDOVECOT='$(top_builddir)/src/lib-settings/libsettings.la $(top_builddir)/src/lib-dict/libdict.la $(top_builddir)/src/lib-imap/libimap.la $(top_builddir)/src/lib-mail/libmail.la $(top_builddir)/src/lib-auth/libauth.la $(top_builddir)/src/lib-charset/libcharset.la $(top_builddir)/src/lib-master/libmaster.la $(top_builddir)/src/lib/liblib.la $(LIBICONV)'
+  LIBDOVECOT='$(top_builddir)/src/lib-master/libmaster.la $(top_builddir)/src/lib-settings/libsettings.la $(top_builddir)/src/lib-dict/libdict.la $(top_builddir)/src/lib-imap/libimap.la $(top_builddir)/src/lib-mail/libmail.la $(top_builddir)/src/lib-auth/libauth.la $(top_builddir)/src/lib-charset/libcharset.la $(top_builddir)/src/lib/liblib.la $(LIBICONV)'
   LIBDOVECOT_STORAGE_LAST='$(top_builddir)/src/lib-storage/list/libstorage_list.la $(top_builddir)/src/lib-storage/index/libstorage_index.la $(top_builddir)/src/lib-storage/libstorage.la $(top_builddir)/src/lib-index/libindex.la'
   LIBDOVECOT_STORAGE_FIRST='$(top_builddir)/src/lib-storage/libstorage_service.la $(top_builddir)/src/lib-storage/register/libstorage_register.la'
   LIBDOVECOT_STORAGE="$LIBDOVECOT_STORAGE_FIRST $LINKED_STORAGE_LIBS $LIBDOVECOT_STORAGE_LAST"
@@ -2376,6 +2376,7 @@ src/lib-storage/register/Makefile
 src/auth/Makefile
 src/config/Makefile
 src/lda/Makefile
+src/log/Makefile
 src/lmtp/Makefile
 src/dict/Makefile
 src/imap/Makefile
index 9ab9fa22074040c19ae49613b3ecd176b7964b75..90c0a682f9157a2aaac99f5c4e7a280d3317f4ea 100644 (file)
@@ -27,6 +27,7 @@ SUBDIRS = \
        pop3 \
        lda \
        lmtp \
+       log \
        config \
        tests \
        util \
index 71d0e0680b8f145c9b59f4cff68981f2d1df79d9..670388b68693c8336bb10fc9d49c9a39fe6a2b56 100644 (file)
@@ -23,6 +23,7 @@ AM_CPPFLAGS = \
        -I$(top_srcdir)/src/lib-settings \
        -I$(top_srcdir)/src/lib-ntlm \
        -I$(top_srcdir)/src/lib-otp \
+       -I$(top_srcdir)/src/lib-master \
        -DAUTH_MODULE_DIR=\""$(auth_moduledir)"\" \
        -DPKG_LIBEXECDIR=\""$(pkglibexecdir)"\" \
        -DPKG_RUNDIR=\""$(rundir)"\" \
@@ -44,7 +45,7 @@ dovecot_auth_libs = \
        $(LIBDOVECOT_SQL) \
        $(LIBDOVECOT)
 
-dovecot_auth_LDADD = $(dovecot_auth_libs) $(MODULE_LIBS) $(AUTH_LIBS)
+dovecot_auth_LDADD = $(dovecot_auth_libs) $(AUTH_LIBS) $(MODULE_LIBS)
 dovecot_auth_DEPENDENCIES = $(dovecot_auth_libs)
 
 ldap_sources = db-ldap.c passdb-ldap.c userdb-ldap.c
@@ -54,7 +55,6 @@ dovecot_auth_SOURCES = \
        auth-cache.c \
        auth-client-connection.c \
        auth-master-connection.c \
-       auth-master-listener.c \
        auth-request.c \
        auth-request-handler.c \
        auth-settings.c \
@@ -111,7 +111,6 @@ headers = \
        auth-client-interface.h \
        auth-master-interface.h \
        auth-master-connection.h \
-       auth-master-listener.h \
        auth-request.h \
        auth-request-handler.h \
        auth-settings.h \
index 6adc6f3b4f0fa3e5a740f4194927de3767b755a8..f6967072852e12cdca8a0ad0e65b2f69087412fb 100644 (file)
@@ -1,11 +1,12 @@
 /* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
 
 #include "common.h"
+#include "array.h"
 #include "ioloop.h"
 #include "istream.h"
 #include "ostream.h"
 #include "network.h"
-#include "array.h"
+#include "hostpid.h"
 #include "str.h"
 #include "str-sanitize.h"
 #include "safe-memset.h"
 #include "auth-request-handler.h"
 #include "auth-client-interface.h"
 #include "auth-client-connection.h"
-#include "auth-master-listener.h"
 #include "auth-master-connection.h"
 
 #include <stdlib.h>
 
 #define OUTBUF_THROTTLE_SIZE (1024*50)
 
-static void auth_client_connection_unref(struct auth_client_connection **_conn);
+static ARRAY_DEFINE(auth_client_connections, struct auth_client_connection *);
+static struct timeout *to_clients;
 
+static void auth_client_connection_unref(struct auth_client_connection **_conn);
 static void auth_client_input(struct auth_client_connection *conn);
 
 static const char *reply_line_hide_pass(const char *line)
@@ -90,7 +92,7 @@ auth_client_input_cpid(struct auth_client_connection *conn, const char *args)
                return FALSE;
        }
 
-       old = auth_client_connection_lookup(conn->listener, pid);
+       old = auth_client_connection_lookup(pid);
        if (old != NULL) {
                /* already exists. it's possible that it just reconnected,
                   see if the old connection is still there. */
@@ -112,7 +114,7 @@ auth_client_input_cpid(struct auth_client_connection *conn, const char *args)
        conn->request_handler =
                auth_request_handler_create(conn->auth,
                        auth_callback, conn,
-                       array_count(&conn->listener->masters) != 0 ?
+                       array_count(&auth_master_connections) != 0 ?
                        auth_master_request_callback : NULL);
        auth_request_handler_set(conn->request_handler, conn->connect_uid, pid);
 
@@ -261,7 +263,7 @@ static void auth_client_input(struct auth_client_connection *conn)
 }
 
 struct auth_client_connection *
-auth_client_connection_create(struct auth_master_listener *listener, int fd)
+auth_client_connection_create(struct auth *auth, int fd)
 {
        static unsigned int connect_uid_counter = 0;
        struct auth_client_connection *conn;
@@ -269,8 +271,7 @@ auth_client_connection_create(struct auth_master_listener *listener, int fd)
        string_t *str;
 
        conn = i_new(struct auth_client_connection, 1);
-       conn->auth = listener->auth;
-       conn->listener = listener;
+       conn->auth = auth;
        conn->refcount = 1;
        conn->connect_uid = ++connect_uid_counter;
 
@@ -281,13 +282,13 @@ auth_client_connection_create(struct auth_master_listener *listener, int fd)
        o_stream_set_flush_callback(conn->output, auth_client_output, conn);
        conn->io = io_add(fd, IO_READ, auth_client_input, conn);
 
-       array_append(&listener->clients, &conn, 1);
+       array_append(&auth_client_connections, &conn, 1);
 
        str = t_str_new(128);
-       str_printfa(str, "VERSION\t%u\t%u\nSPID\t%u\nCUID\t%u\nDONE\n",
+       str_printfa(str, "VERSION\t%u\t%u\nSPID\t%s\nCUID\t%u\nDONE\n",
                     AUTH_CLIENT_PROTOCOL_MAJOR_VERSION,
                     AUTH_CLIENT_PROTOCOL_MINOR_VERSION,
-                   listener->pid, conn->connect_uid);
+                   my_pid, conn->connect_uid);
 
        iov[0].iov_base = str_data(conn->auth->mech_handshake);
        iov[0].iov_len = str_len(conn->auth->mech_handshake);
@@ -310,10 +311,10 @@ void auth_client_connection_destroy(struct auth_client_connection **_conn)
        if (conn->fd == -1)
                return;
 
-       clients = array_get(&conn->listener->clients, &count);
+       clients = array_get(&auth_client_connections, &count);
        for (i = 0; i < count; i++) {
                if (clients[i] == conn) {
-                       array_delete(&conn->listener->clients, i, 1);
+                       array_delete(&auth_client_connections, i, 1);
                        break;
                }
        }
@@ -347,13 +348,12 @@ static void auth_client_connection_unref(struct auth_client_connection **_conn)
 }
 
 struct auth_client_connection *
-auth_client_connection_lookup(struct auth_master_listener *listener,
-                             unsigned int pid)
+auth_client_connection_lookup(unsigned int pid)
 {
        struct auth_client_connection *const *clients;
        unsigned int i, count;
 
-       clients = array_get(&listener->clients, &count);
+       clients = array_get(&auth_client_connections, &count);
        for (i = 0; i < count; i++) {
                if (clients[i]->pid == pid)
                        return clients[i];
@@ -362,12 +362,12 @@ auth_client_connection_lookup(struct auth_master_listener *listener,
        return NULL;
 }
 
-static void request_timeout(struct auth_master_listener *listener)
+static void request_timeout(void *context ATTR_UNUSED)
 {
        struct auth_client_connection *const *clients;
        unsigned int i, count;
 
-       clients = array_get(&listener->clients, &count);
+       clients = array_get(&auth_client_connections, &count);
        for (i = 0; i < count; i++) {
                if (clients[i]->request_handler != NULL) {
                        auth_request_handler_check_timeouts(
@@ -376,13 +376,22 @@ static void request_timeout(struct auth_master_listener *listener)
        }
 }
 
-void auth_client_connections_init(struct auth_master_listener *listener)
+void auth_client_connections_init(void)
 {
-       listener->to_clients = timeout_add(5000, request_timeout, listener);
+       i_array_init(&auth_client_connections, 16);
+       to_clients = timeout_add(5000, request_timeout, NULL);
 }
 
-void auth_client_connections_deinit(struct auth_master_listener *listener)
+void auth_client_connections_deinit(void)
 {
-       if (listener->to_clients != NULL)
-               timeout_remove(&listener->to_clients);
+       struct auth_client_connection **clients;
+       unsigned int i, count;
+
+       if (to_clients != NULL)
+               timeout_remove(&to_clients);
+
+       clients = array_get_modifiable(&auth_client_connections, &count);
+       for (i = count; i > 0; i--)
+               auth_client_connection_destroy(&clients[i-1]);
+       array_free(&auth_client_connections);
 }
index 3996c0fd12be5f3d7775f206c48aa6d7d8fa1ed6..475c67fa48983d0bcbe84fafa09dfe38d73a1e00 100644 (file)
@@ -3,7 +3,6 @@
 
 struct auth_client_connection {
        struct auth *auth;
-       struct auth_master_listener *listener;
        int refcount;
 
        int fd;
@@ -19,14 +18,13 @@ struct auth_client_connection {
 };
 
 struct auth_client_connection *
-auth_client_connection_create(struct auth_master_listener *listener, int fd);
+auth_client_connection_create(struct auth *auth, int fd);
 void auth_client_connection_destroy(struct auth_client_connection **conn);
 
 struct auth_client_connection *
-auth_client_connection_lookup(struct auth_master_listener *listener,
-                             unsigned int pid);
+auth_client_connection_lookup(unsigned int pid);
 
-void auth_client_connections_init(struct auth_master_listener *listener);
-void auth_client_connections_deinit(struct auth_master_listener *listener);
+void auth_client_connections_init(void);
+void auth_client_connections_deinit(void);
 
 #endif
index e794fee932a208f4b5815ca2a21a2d2545ae5f5f..ee5370450194f76c57dae3115f9ac7af7248eb87 100644 (file)
@@ -2,19 +2,19 @@
 
 #include "common.h"
 #include "array.h"
-#include "buffer.h"
 #include "hash.h"
 #include "str.h"
+#include "hostpid.h"
 #include "str-sanitize.h"
 #include "ioloop.h"
+#include "network.h"
 #include "istream.h"
 #include "ostream.h"
-#include "network.h"
+#include "master-service.h"
 #include "userdb.h"
 #include "auth-request-handler.h"
 #include "auth-master-interface.h"
 #include "auth-client-connection.h"
-#include "auth-master-listener.h"
 #include "auth-master-connection.h"
 
 #include <unistd.h>
@@ -29,6 +29,8 @@ struct master_userdb_request {
        struct auth_request *auth_request;
 };
 
+ARRAY_TYPE(auth_master_connections) auth_master_connections;
+
 void auth_master_request_callback(struct auth_stream_reply *reply,
                                  void *context)
 {
@@ -38,7 +40,7 @@ void auth_master_request_callback(struct auth_stream_reply *reply,
 
        reply_str = auth_stream_reply_export(reply);
 
-       if (conn->listener->auth->set->debug)
+       if (conn->auth->set->debug)
                i_info("master out: %s", reply_str);
 
        iov[0].iov_base = reply_str;
@@ -67,7 +69,7 @@ master_input_request(struct auth_master_connection *conn, const char *args)
        client_pid = (unsigned int)strtoul(list[1], NULL, 10);
        client_id = (unsigned int)strtoul(list[2], NULL, 10);
 
-       client_conn = auth_client_connection_lookup(conn->listener, client_pid);
+       client_conn = auth_client_connection_lookup(client_pid);
        if (client_conn == NULL) {
                i_error("Master requested auth for nonexisting client %u",
                        client_pid);
@@ -105,7 +107,7 @@ user_callback(enum userdb_result result,
                break;
        }
 
-       if (conn->listener->auth->set->debug)
+       if (conn->auth->set->debug)
                i_info("master out: %s", str_c(str));
 
        str_append_c(str, '\n');
@@ -126,7 +128,7 @@ master_input_user(struct auth_master_connection *conn, const char *args)
                return FALSE;
        }
 
-       auth_request = auth_request_new_dummy(conn->listener->auth);
+       auth_request = auth_request_new_dummy(conn->auth);
        auth_request->id = (unsigned int)strtoul(list[0], NULL, 10);
        auth_request->context = conn;
 
@@ -163,7 +165,7 @@ master_input_user(struct auth_master_connection *conn, const char *args)
 static bool
 auth_master_input_line(struct auth_master_connection *conn, const char *line)
 {
-       if (conn->listener->auth->set->debug)
+       if (conn->auth->set->debug)
                i_info("master in: %s", line);
 
        if (strncmp(line, "REQUEST\t", 8) == 0)
@@ -248,41 +250,34 @@ static int master_output(struct auth_master_connection *conn)
 }
 
 struct auth_master_connection *
-auth_master_connection_create(struct auth_master_listener *listener, int fd)
+auth_master_connection_create(struct auth *auth, int fd)
 {
        struct auth_master_connection *conn;
+       const char *line;
 
        conn = i_new(struct auth_master_connection, 1);
-       conn->listener = listener;
        conn->refcount = 1;
        conn->fd = fd;
+       conn->auth = auth;
        conn->input = i_stream_create_fd(fd, MAX_INBUF_SIZE, FALSE);
        conn->output = o_stream_create_fd(fd, (size_t)-1, FALSE);
        o_stream_set_flush_callback(conn->output, master_output, conn);
        conn->io = io_add(fd, IO_READ, master_input, conn);
 
-       array_append(&listener->masters, &conn, 1);
-       return conn;
-}
-
-void auth_master_connection_send_handshake(struct auth_master_connection *conn)
-{
-       const char *line;
-
-       if (conn->output == NULL)
-               return;
-
-       line = t_strdup_printf("VERSION\t%u\t%u\nSPID\t%u\n",
+       line = t_strdup_printf("VERSION\t%u\t%u\nSPID\t%s\n",
                               AUTH_MASTER_PROTOCOL_MAJOR_VERSION,
                               AUTH_MASTER_PROTOCOL_MINOR_VERSION,
-                              conn->listener->pid);
+                              my_pid);
        (void)o_stream_send_str(conn->output, line);
+
+       array_append(&auth_master_connections, &conn, 1);
+       return conn;
 }
 
 void auth_master_connection_destroy(struct auth_master_connection **_conn)
 {
         struct auth_master_connection *conn = *_conn;
-        struct auth_master_connection *const *conns;
+        struct auth_master_connection *const *masters;
        unsigned int i, count;
 
        *_conn = NULL;
@@ -290,6 +285,14 @@ void auth_master_connection_destroy(struct auth_master_connection **_conn)
                return;
        conn->destroyed = TRUE;
 
+       masters = array_get(&auth_master_connections, &count);
+       for (i = 0; i < count; i++) {
+               if (masters[i] == conn) {
+                       array_delete(&auth_master_connections, i, 1);
+                       break;
+               }
+       }
+
        if (conn->input != NULL)
                i_stream_close(conn->input);
        if (conn->output != NULL)
@@ -302,16 +305,7 @@ void auth_master_connection_destroy(struct auth_master_connection **_conn)
                conn->fd = -1;
        }
 
-       conns = array_get(&conn->listener->masters, &count);
-       for (i = 0; i < count; i++) {
-               if (conns[i] == conn) {
-                       array_delete(&conn->listener->masters, i, 1);
-                       break;
-               }
-       }
-       if (!standalone && auth_master_listeners_masters_left() == 0)
-               io_loop_stop(ioloop);
-
+        master_service_client_connection_destroyed(service);
        auth_master_connection_unref(&conn);
 }
 
@@ -339,3 +333,19 @@ void auth_master_connection_unref(struct auth_master_connection **_conn)
 
        i_free(conn);
 }
+
+void auth_master_connections_init(void)
+{
+       i_array_init(&auth_master_connections, 16);
+}
+
+void auth_master_connections_deinit(void)
+{
+       struct auth_master_connection **masters;
+       unsigned int i, count;
+
+       masters = array_get_modifiable(&auth_master_connections, &count);
+       for (i = count; i > 0; i--)
+               auth_master_connection_destroy(&masters[i-1]);
+       array_free(&auth_master_connections);
+}
index 637c3bef009b2cae1b8ca83222d31562fa3a8a88..9eee52193695ea50351aa283f8f649c75a80291c 100644 (file)
@@ -4,7 +4,7 @@
 struct auth_stream_reply;
 
 struct auth_master_connection {
-       struct auth_master_listener *listener;
+       struct auth *auth;
        int refcount;
 
        int fd;
@@ -15,18 +15,21 @@ struct auth_master_connection {
        unsigned int version_received:1;
        unsigned int destroyed:1;
 };
+ARRAY_DEFINE_TYPE(auth_master_connections, struct auth_master_connection *);
+
+extern ARRAY_TYPE(auth_master_connections) auth_master_connections;
 
 struct auth_master_connection *
-auth_master_connection_create(struct auth_master_listener *listener, int fd);
+auth_master_connection_create(struct auth *auth, int fd);
 void auth_master_connection_destroy(struct auth_master_connection **conn);
 
 void auth_master_connection_ref(struct auth_master_connection *conn);
 void auth_master_connection_unref(struct auth_master_connection **conn);
 
-void auth_master_connection_send_handshake(struct auth_master_connection *conn);
-void auth_master_connections_send_handshake(void);
-
 void auth_master_request_callback(struct auth_stream_reply *reply,
                                  void *context);
 
+void auth_master_connections_init(void);
+void auth_master_connections_deinit(void);
+
 #endif
index 934e5294d6890aaa6c8ca1399c5c54c61af99fc7..4ef2db47c75aa71eded4116f78e63783621d5500 100644 (file)
@@ -1,6 +1,8 @@
 #ifndef AUTH_MASTER_INTERFACE_H
 #define AUTH_MASTER_INTERFACE_H
 
+#include "master-interface.h"
+
 /* Major version changes are not backwards compatible,
    minor version numbers can be ignored. */
 #define AUTH_MASTER_PROTOCOL_MAJOR_VERSION 1
diff --git a/src/auth/auth-master-listener.c b/src/auth/auth-master-listener.c
deleted file mode 100644 (file)
index abb9049..0000000
+++ /dev/null
@@ -1,179 +0,0 @@
-/* Copyright (c) 2005-2009 Dovecot authors, see the included COPYING file */
-
-#include "common.h"
-#include "array.h"
-#include "ioloop.h"
-#include "network.h"
-#include "istream.h"
-#include "ostream.h"
-#include "auth-master-listener.h"
-#include "auth-master-connection.h"
-#include "auth-client-connection.h"
-
-#include <unistd.h>
-
-struct auth_master_listener_socket {
-       struct auth_master_listener *listener;
-
-       enum listener_type type;
-       int fd;
-       char *path;
-       struct io *io;
-};
-
-static ARRAY_DEFINE(master_listeners, struct auth_master_listener *);
-
-struct auth_master_listener *auth_master_listener_create(struct auth *auth)
-{
-       struct auth_master_listener *listener;
-
-       listener = i_new(struct auth_master_listener, 1);
-       listener->auth = auth;
-       listener->pid = (unsigned int)getpid();
-       i_array_init(&listener->sockets, 16);
-       i_array_init(&listener->masters, 16);
-       i_array_init(&listener->clients, 16);
-       auth_client_connections_init(listener);
-
-       array_append(&master_listeners, &listener, 1);
-       return listener;
-}
-
-static void
-auth_master_listener_socket_free(struct auth_master_listener_socket *s)
-{
-       if (s->path != NULL) {
-               (void)unlink(s->path);
-               i_free(s->path);
-       }
-
-       io_remove(&s->io);
-       net_disconnect(s->fd);
-       i_free(s);
-}
-
-void auth_master_listener_destroy(struct auth_master_listener *listener)
-{
-       struct auth_master_listener *const *listeners;
-       struct auth_master_listener_socket **sockets;
-       struct auth_master_connection **masters;
-       struct auth_client_connection **clients;
-       unsigned int i, count;
-
-       listeners = array_get(&master_listeners, &count);
-       for (i = 0; i < count; i++) {
-               if (listeners[i] == listener) {
-                       array_delete(&master_listeners, i, 1);
-                       break;
-               }
-       }
-
-       sockets = array_get_modifiable(&listener->sockets, &count);
-       for (i = count; i > 0; i--)
-               auth_master_listener_socket_free(sockets[i-1]);
-
-       masters = array_get_modifiable(&listener->masters, &count);
-       for (i = count; i > 0; i--)
-               auth_master_connection_destroy(&masters[i-1]);
-
-       clients = array_get_modifiable(&listener->clients, &count);
-       for (i = count; i > 0; i--)
-               auth_client_connection_destroy(&clients[i-1]);
-
-        auth_client_connections_deinit(listener);
-       array_free(&listener->sockets);
-       array_free(&listener->masters);
-       array_free(&listener->clients);
-       i_free(listener);
-}
-
-static void auth_master_listener_accept(struct auth_master_listener_socket *s)
-{
-       struct auth_master_connection *master;
-       int fd;
-
-       fd = net_accept(s->fd, NULL, NULL);
-       if (fd < 0) {
-               if (fd < -1)
-                       i_error("accept(type %d) failed: %m", s->type);
-       } else {
-               net_set_nonblock(fd, TRUE);
-
-               switch (s->type) {
-               case LISTENER_CLIENT:
-                       (void)auth_client_connection_create(s->listener, fd);
-                       break;
-               case LISTENER_MASTER:
-                       /* we'll just replace the previous master.. */
-                       master = auth_master_connection_create(s->listener, fd);
-                       auth_master_connection_send_handshake(master);
-                       break;
-               }
-       }
-}
-
-void auth_master_listener_add(struct auth_master_listener *listener,
-                             int fd, const char *path,
-                             enum listener_type type)
-{
-       struct auth_master_listener_socket *s;
-
-       s = i_new(struct auth_master_listener_socket, 1);
-       s->listener = listener;
-       s->fd = fd;
-       s->path = i_strdup(path);
-       s->type = type;
-       s->io = io_add(fd, IO_READ, auth_master_listener_accept, s);
-
-       array_append(&listener->sockets, &s, 1);
-}
-
-static void
-auth_master_listener_send_handshakes(struct auth_master_listener *listener)
-{
-        struct auth_master_connection *const *masters;
-       unsigned int i, count;
-
-       masters = array_get(&listener->masters, &count);
-       for (i = 0; i < count; i++)
-               auth_master_connection_send_handshake(masters[i]);
-}
-
-void auth_master_listeners_send_handshake(void)
-{
-        struct auth_master_listener *const *listeners;
-       unsigned int i, count;
-
-       listeners = array_get(&master_listeners, &count);
-       for (i = 0; i < count; i++)
-               auth_master_listener_send_handshakes(listeners[i]);
-}
-
-bool auth_master_listeners_masters_left(void)
-{
-        struct auth_master_listener *const *listeners;
-       unsigned int i, count;
-
-       listeners = array_get(&master_listeners, &count);
-       for (i = 0; i < count; i++) {
-               if (array_count(&listeners[i]->masters) > 0)
-                       return TRUE;
-       }
-       return FALSE;
-}
-
-void auth_master_listeners_init(void)
-{
-       i_array_init(&master_listeners, 2);
-}
-
-void auth_master_listeners_deinit(void)
-{
-        struct auth_master_listener **listeners;
-       unsigned int i, count;
-
-       listeners = array_get_modifiable(&master_listeners, &count);
-       for (i = count; i > 0; i--)
-               auth_master_listener_destroy(listeners[i-1]);
-       array_free(&master_listeners);
-}
diff --git a/src/auth/auth-master-listener.h b/src/auth/auth-master-listener.h
deleted file mode 100644 (file)
index 312fc01..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-#ifndef AUTH_MASTER_LISTENER_H
-#define AUTH_MASTER_LISTENER_H
-
-enum listener_type {
-       LISTENER_MASTER,
-       LISTENER_CLIENT
-};
-
-struct auth_master_listener {
-       struct auth *auth;
-       unsigned int pid;
-
-       ARRAY_DEFINE(sockets, struct auth_master_listener_socket *);
-       ARRAY_DEFINE(masters, struct auth_master_connection *);
-       ARRAY_DEFINE(clients, struct auth_client_connection *);
-
-       struct timeout *to_clients;
-};
-
-struct auth_master_listener *auth_master_listener_create(struct auth *auth);
-void auth_master_listener_destroy(struct auth_master_listener *listener);
-
-void auth_master_listener_add(struct auth_master_listener *listener,
-                             int fd, const char *path,
-                             enum listener_type type);
-
-void auth_master_listeners_send_handshake(void);
-bool auth_master_listeners_masters_left(void);
-
-void auth_master_listeners_init(void);
-void auth_master_listeners_deinit(void);
-
-#endif
index 0dd9a63e5ff0d8e367ff4135b5fafff5c31fe593..3e7094d4884bb970915eaabeaaeafe455d2c8056 100644 (file)
 
 #include "lib.h"
 #include "array.h"
-#include "hostpid.h"
 #include "settings-parser.h"
+#include "master-service-settings.h"
 #include "auth-settings.h"
 
 #include <stddef.h>
 
-extern struct setting_parser_info auth_socket_setting_parser_info;
 extern struct setting_parser_info auth_setting_parser_info;
 extern struct setting_parser_info auth_root_setting_parser_info;
 
-static bool auth_settings_check(void *_set, pool_t pool, const char **error_r);
-
-#undef DEF
-#define DEF(type, name) \
-       { type, #name, offsetof(struct auth_socket_unix_settings, name), NULL }
-
-static struct setting_define auth_socket_client_setting_defines[] = {
-       DEF(SET_STR, path),
-       DEF(SET_UINT, mode),
-       DEF(SET_STR, user),
-       DEF(SET_STR, group),
-
-       SETTING_DEFINE_LIST_END
-};
-
-static struct auth_socket_unix_settings auth_socket_client_default_settings = {
-       MEMBER(path) "auth-client",
-       MEMBER(mode) 0660,
-       MEMBER(user) "",
-       MEMBER(group) ""
-};
-
-struct setting_parser_info auth_socket_client_setting_parser_info = {
-       MEMBER(defines) auth_socket_client_setting_defines,
-       MEMBER(defaults) &auth_socket_client_default_settings,
-
-       MEMBER(parent) &auth_socket_setting_parser_info,
-       MEMBER(dynamic_parsers) NULL,
-
-       MEMBER(parent_offset) (size_t)-1,
-       MEMBER(type_offset) (size_t)-1,
-       MEMBER(struct_size) sizeof(struct auth_socket_unix_settings)
-};
-
-#undef DEF
-#define DEF(type, name) \
-       { type, #name, offsetof(struct auth_socket_unix_settings, name), NULL }
-
-static struct setting_define auth_socket_master_setting_defines[] = {
-       DEF(SET_STR, path),
-       DEF(SET_UINT, mode),
-       DEF(SET_STR, user),
-       DEF(SET_STR, group),
-
-       SETTING_DEFINE_LIST_END
-};
-
-static struct auth_socket_unix_settings auth_socket_master_default_settings = {
-       MEMBER(path) "auth-master",
-       MEMBER(mode) 0660,
-       MEMBER(user) "",
-       MEMBER(group) ""
-};
-
-struct setting_parser_info auth_socket_master_setting_parser_info = {
-       MEMBER(defines) auth_socket_master_setting_defines,
-       MEMBER(defaults) &auth_socket_master_default_settings,
-
-       MEMBER(parent) &auth_socket_setting_parser_info,
-       MEMBER(dynamic_parsers) NULL,
-
-       MEMBER(parent_offset) (size_t)-1,
-       MEMBER(type_offset) (size_t)-1,
-       MEMBER(struct_size) sizeof(struct auth_socket_unix_settings)
-};
-
-#undef DEF
-#undef DEFLIST
-#define DEF(type, name) \
-       { type, #name, offsetof(struct auth_socket_settings, name), NULL }
-#define DEFLIST(field, name, defines) \
-       { SET_DEFLIST, name, offsetof(struct auth_socket_settings, field), defines }
-
-static struct setting_define auth_socket_setting_defines[] = {
-       DEF(SET_ENUM, type),
-
-       DEFLIST(clients, "client", &auth_socket_client_setting_parser_info),
-       DEFLIST(masters, "master", &auth_socket_master_setting_parser_info),
-
-       SETTING_DEFINE_LIST_END
-};
-
-static struct auth_socket_settings auth_socket_default_settings = {
-       MEMBER(type) "listen:connect"
-};
-
-struct setting_parser_info auth_socket_setting_parser_info = {
-       MEMBER(defines) auth_socket_setting_defines,
-       MEMBER(defaults) &auth_socket_default_settings,
-
-       MEMBER(parent) &auth_setting_parser_info,
-       MEMBER(dynamic_parsers) NULL,
-
-       MEMBER(parent_offset) (size_t)-1,
-       MEMBER(type_offset) offsetof(struct auth_socket_settings, type),
-       MEMBER(struct_size) sizeof(struct auth_socket_settings)
-};
-
 #undef DEF
 #define DEF(type, name) \
        { type, #name, offsetof(struct auth_passdb_settings, name), NULL }
@@ -191,7 +92,6 @@ static struct setting_define auth_setting_defines[] = {
 
        DEF(SET_UINT, worker_max_count),
 
-       DEFLIST(sockets, "socket", &auth_socket_setting_parser_info),
        DEFLIST(passdbs, "passdb", &auth_passdb_setting_parser_info),
        DEFLIST(userdbs, "userdb", &auth_userdb_setting_parser_info),
 
@@ -227,7 +127,6 @@ static struct auth_settings auth_default_settings = {
 
        MEMBER(worker_max_count) 30,
 
-       MEMBER(sockets) ARRAY_INIT,
        MEMBER(passdbs) ARRAY_INIT,
        MEMBER(userdbs) ARRAY_INIT
 };
@@ -242,7 +141,7 @@ struct setting_parser_info auth_setting_parser_info = {
        MEMBER(parent_offset) offsetof(struct auth_settings, root),
        MEMBER(type_offset) offsetof(struct auth_settings, name),
        MEMBER(struct_size) sizeof(struct auth_settings),
-       MEMBER(check_func) auth_settings_check
+       MEMBER(check_func) NULL
 };
 
 #undef DEF
@@ -253,14 +152,12 @@ struct setting_parser_info auth_setting_parser_info = {
        { SET_DEFLIST, name, offsetof(struct auth_root_settings, field), defines }
 
 static struct setting_define auth_root_setting_defines[] = {
-       DEF(SET_STR, base_dir),
        DEFLIST(auths, "auth", &auth_setting_parser_info),
 
        SETTING_DEFINE_LIST_END
 };
 
 static struct auth_root_settings auth_root_default_settings = {
-       MEMBER(base_dir) PKG_RUNDIR,
        MEMBER(auths) ARRAY_INIT
 };
 
@@ -276,76 +173,25 @@ struct setting_parser_info auth_root_setting_parser_info = {
        MEMBER(struct_size) sizeof(struct auth_root_settings)
 };
 
-static pool_t settings_pool = NULL;
-
-static void fix_base_path(struct auth_settings *set, const char **str)
-{
-       if (*str != NULL && **str != '\0' && **str != '/') {
-               *str = p_strconcat(settings_pool,
-                                  set->root->base_dir, "/", *str, NULL);
-       }
-}
-
-/* <settings checks> */
-static bool auth_settings_check(void *_set ATTR_UNUSED, pool_t pool ATTR_UNUSED,
-                               const char **error_r ATTR_UNUSED)
-{
-#ifndef CONFIG_BINARY
-       struct auth_settings *set = _set;
-       struct auth_socket_unix_settings *const *u;
-       struct auth_socket_settings *const *sockets;
-       unsigned int i, j, count, count2;
-
-       if (!array_is_created(&set->sockets))
-               return TRUE;
-
-       sockets = array_get(&set->sockets, &count);
-       for (i = 0; i < count; i++) {
-               if (array_is_created(&sockets[i]->masters)) {
-                       u = array_get(&sockets[i]->masters, &count2);
-                       for (j = 0; j < count2; j++)
-                               fix_base_path(set, &u[j]->path);
-               }
-               if (array_is_created(&sockets[i]->clients)) {
-                       u = array_get(&sockets[i]->clients, &count2);
-                       for (j = 0; j < count2; j++)
-                               fix_base_path(set, &u[j]->path);
-               }
-       }
-#endif
-       return TRUE;
-}
-/* </settings checks> */
-
-struct auth_settings *auth_settings_read(const char *name)
+struct auth_settings *
+auth_settings_read(struct master_service *service, const char *name)
 {
-       struct setting_parser_context *parser;
-       struct auth_root_settings *set;
-       struct auth_settings *const *auths;
+       static const struct setting_parser_info *set_roots[] = {
+               &auth_root_setting_parser_info,
+               NULL
+       };
        const char *error;
+       void **sets;
+       struct auth_settings *const *auths;
+       struct auth_root_settings *set;
        unsigned int i, count;
 
-       if (settings_pool == NULL)
-               settings_pool = pool_alloconly_create("auth settings", 1024);
-       else
-               p_clear(settings_pool);
-
-       parser = settings_parser_init(settings_pool,
-                                     &auth_root_setting_parser_info,
-                                     SETTINGS_PARSER_FLAG_IGNORE_UNKNOWN_KEYS);
-
-       auth_default_settings.gssapi_hostname = my_hostname;
-
-       if (settings_parse_environ(parser) < 0) {
-               i_fatal("Error reading configuration: %s",
-                       settings_parser_get_error(parser));
-       }
-
-       if (settings_parser_check(parser, settings_pool, &error) < 0)
-               i_fatal("Invalid settings: %s", error);
+       if (master_service_settings_read(service, set_roots, NULL, FALSE,
+                                        &error) < 0)
+               i_fatal("Error reading configuration: %s", error);
 
-       set = settings_parser_get(parser);
-       settings_parser_deinit(&parser);
+       sets = master_service_settings_get_others(service);
+       set = sets[0];
 
        if (array_is_created(&set->auths)) {
                auths = array_get(&set->auths, &count);
@@ -355,5 +201,4 @@ struct auth_settings *auth_settings_read(const char *name)
                }
        }
        i_fatal("Error reading configuration: No auth section: %s", name);
-       return NULL;
 }
index bf9c16654afb9d30bc689d751aac54c3b9865a00..699ec33cccb04fc1abc2e85cae3ec81ef5266052 100644 (file)
@@ -1,19 +1,7 @@
 #ifndef AUTH_SETTINGS_H
 #define AUTH_SETTINGS_H
 
-struct auth_socket_unix_settings {
-       const char *path;
-       unsigned int mode;
-       const char *user;
-       const char *group;
-};
-
-struct auth_socket_settings {
-       const char *type;
-
-       ARRAY_DEFINE(clients, struct auth_socket_unix_settings *);
-       ARRAY_DEFINE(masters, struct auth_socket_unix_settings *);
-};
+struct master_service;
 
 struct auth_passdb_settings {
        const char *driver;
@@ -55,17 +43,15 @@ struct auth_settings {
 
        unsigned int worker_max_count;
 
-       ARRAY_DEFINE(sockets, struct auth_socket_settings *);
        ARRAY_DEFINE(passdbs, struct auth_passdb_settings *);
        ARRAY_DEFINE(userdbs, struct auth_userdb_settings *);
 };
 
 struct auth_root_settings {
-       const char *base_dir;
-
        ARRAY_DEFINE(auths, struct auth_settings *);
 };
 
-struct auth_settings *auth_settings_read(const char *name);
+struct auth_settings *
+auth_settings_read(struct master_service *service, const char *name);
 
 #endif
index 559db4b809de1465fe03fc388404ab230e79779d..6ae57d702333dc95754aa78e54660b05b34bb7ee 100644 (file)
@@ -7,6 +7,7 @@
 #include "istream.h"
 #include "ostream.h"
 #include "str.h"
+#include "master-service.h"
 #include "auth-request.h"
 #include "auth-worker-client.h"
 
@@ -504,7 +505,7 @@ void auth_worker_client_destroy(struct auth_worker_client **_client)
        net_disconnect(client->fd);
        client->fd = -1;
 
-       io_loop_stop(ioloop);
+        master_service_client_connection_destroyed(service);
 }
 
 void auth_worker_client_unref(struct auth_worker_client **_client)
index 3ca08837b84ba1322fac78e1adf058bdb321ad27..5c74989ea5bb887b2538e93abe71cac62328dfa5 100644 (file)
@@ -4,12 +4,8 @@
 #include "lib.h"
 #include "auth.h"
 
-#define MASTER_SOCKET_FD 0
-#define CLIENT_LISTEN_FD 3
-#define WORKER_SERVER_FD 4
-
-extern struct ioloop *ioloop;
-extern bool standalone, worker, shutdown_request;
+extern struct master_service *service;
+extern bool worker, shutdown_request;
 extern time_t process_start_time;
 
 #endif
index 7df15d09356dff53e5dfb2309e3a92c88b269e3f..634b8291c9d108df46f75abd69abaa1653b029cd 100644 (file)
@@ -18,6 +18,7 @@
 
 #include <stddef.h>
 #include <stdlib.h>
+#include <unistd.h>
 
 #define HAVE_LDAP_SASL
 #ifdef HAVE_SASL_SASL_H
@@ -657,7 +658,7 @@ static void db_ldap_get_fd(struct ldap_connection *conn)
                i_fatal("LDAP: Can't get connection fd: %s",
                        ldap_err2string(ret));
        }
-       if (conn->fd <= CLIENT_LISTEN_FD) {
+       if (conn->fd <= STDERR_FILENO) {
                /* Solaris LDAP library seems to be broken */
                i_fatal("LDAP: Buggy LDAP library returned wrong fd: %d",
                        conn->fd);
index ddf263d0ffc0ec4db81ec6b15b910c24179c23f9..cd6a5c3651ee777bc3433c454b3b3851ce8d84e0 100644 (file)
@@ -6,11 +6,11 @@
 #include "network.h"
 #include "lib-signals.h"
 #include "restrict-access.h"
-#include "fd-close-on-exec.h"
 #include "child-wait.h"
 #include "sql-api.h"
 #include "module-dir.h"
 #include "randgen.h"
+#include "master-service.h"
 #include "password-scheme.h"
 #include "mech.h"
 #include "auth.h"
 #include "auth-worker-server.h"
 #include "auth-worker-client.h"
 #include "auth-master-interface.h"
-#include "auth-master-listener.h"
 #include "auth-master-connection.h"
 #include "auth-client-connection.h"
 
-#include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
-#include <syslog.h>
-#include <pwd.h>
-#include <grp.h>
-#include <sys/stat.h>
+#include <sys/un.h>
 
-struct ioloop *ioloop;
-bool standalone = FALSE, worker = FALSE, shutdown_request = FALSE;
+enum auth_socket_type {
+       AUTH_SOCKET_UNKNOWN = 0,
+       AUTH_SOCKET_CLIENT,
+       AUTH_SOCKET_MASTER
+};
+
+struct master_service *service;
+bool worker = FALSE, shutdown_request = FALSE;
 time_t process_start_time;
 
 static struct module *modules = NULL;
 static struct auth *auth;
 static struct auth_worker_client *worker_client;
+static ARRAY_DEFINE(listen_fd_types, enum auth_socket_type);
 
-static void sig_die(const siginfo_t *si, void *context ATTR_UNUSED)
-{
-       /* warn about being killed because of some signal, except SIGINT (^C)
-          which is too common at least while testing :) */
-       if (si->si_signo != SIGINT) {
-               i_warning("Killed with signal %d (by pid=%s uid=%s code=%s)",
-                         si->si_signo, dec2str(si->si_pid),
-                         dec2str(si->si_uid),
-                         lib_signal_code_to_str(si->si_signo, si->si_code));
-       }
-       io_loop_stop(ioloop);
-}
-
-static void open_logfile(void)
-{
-       const char *env;
-
-       if (getenv("LOG_TO_MASTER") != NULL) {
-               i_set_failure_internal();
-               return;
-       }
-
-       if (getenv("USE_SYSLOG") != NULL) {
-               env = getenv("SYSLOG_FACILITY");
-               i_set_failure_syslog("dovecot-auth", LOG_NDELAY,
-                                    env == NULL ? LOG_MAIL : atoi(env));
-       } else {
-               /* log to file or stderr */
-               i_set_failure_file(getenv("LOGFILE"), "dovecot-auth: ");
-       }
-
-       if (getenv("INFOLOGFILE") != NULL)
-               i_set_info_file(getenv("INFOLOGFILE"));
-
-       i_set_failure_timestamp_format(getenv("LOGSTAMP"));
-}
-
-static uid_t get_uid(const char *user)
-{
-       struct passwd *pw;
-
-       if (*user == '\0')
-               return (uid_t)-1;
-       if (is_numeric(user, '\0'))
-               return strtoul(user, NULL, 10);
-
-       errno = 0;
-       if ((pw = getpwnam(user)) == NULL) {
-               if (errno != 0)
-                       i_fatal("User '%s' lookup failed: %m", user);
-               setpwent();
-               if (getpwent() == NULL) {
-                       if (errno != 0)
-                               i_fatal("getpwent() failed: %m");
-                       i_fatal("getpwnam() failed for some reason. "
-                               "Is auth_process_size set to too low?");
-               }
-               i_fatal("User doesn't exist: %s", user);
-       }
-       return pw->pw_uid;
-}
-
-static gid_t get_gid(const char *group)
-{
-       struct group *gr;
-
-       if (*group == '\0')
-               return (gid_t)-1;
-       if (is_numeric(group, '\0'))
-               return strtoul(group, NULL, 10);
-
-       errno = 0;
-       if ((gr = getgrnam(group)) == NULL) {
-               if (errno != 0)
-                       i_fatal("Group '%s' lookup failed: %m", group);
-               else
-                       i_fatal("Group doesn't exist: %s", group);
-       }
-       return gr->gr_gid;
-}
-
-static int create_unix_listener(const struct auth_socket_unix_settings *set,
-                               int backlog)
-{
-       mode_t old_umask;
-       uid_t uid;
-       gid_t gid;
-       int fd;
-
-       old_umask = umask((set->mode ^ 0777) & 0777);
-       fd = net_listen_unix_unlink_stale(set->path, backlog);
-       umask(old_umask);
-       if (fd == -1) {
-               if (errno == EADDRINUSE)
-                       i_fatal("Socket already exists: %s", set->path);
-               else
-                       i_fatal("net_listen_unix(%s) failed: %m", set->path);
-       }
-
-       uid = get_uid(set->user); gid = get_gid(set->group);
-       if (chown(set->path, uid, gid) < 0) {
-               i_fatal("chown(%s, %s(%s), %s(%s)) failed: %m",
-                       set->path, dec2str(uid), set->user,
-                       dec2str(gid), set->group);
-       }
-       return fd;
-}
-
-static void
-add_extra_unix_listeners(struct auth_master_listener *listener,
-                        struct auth_socket_unix_settings *const *sets,
-                        unsigned int count, enum listener_type type)
-{
-       unsigned int i;
-       int fd;
-
-       for (i = 0; i < count; i++) {
-               fd = create_unix_listener(sets[i], 128);
-               auth_master_listener_add(listener, fd, sets[i]->path, type);
-       }
-}
-
-static void add_extra_listeners(struct auth *auth)
-{
-       struct auth_master_listener *listener;
-       struct auth_socket_settings *const *sockets;
-       struct auth_socket_unix_settings *const *unix_sockets;
-       unsigned int i, count, count2;
-
-       if (!array_is_created(&auth->set->sockets))
-               return;
-
-       sockets = array_get(&auth->set->sockets, &count);
-       for (i = 0; i < count; i++) {
-               if (strcmp(sockets[i]->type, "listen") != 0)
-                       continue;
-
-               listener = auth_master_listener_create(auth);
-
-               if (array_is_created(&sockets[i]->masters)) {
-                       unix_sockets = array_get(&sockets[i]->masters, &count2);
-                       add_extra_unix_listeners(listener, unix_sockets, count2,
-                                                LISTENER_MASTER);
-               }
-               if (array_is_created(&sockets[i]->clients)) {
-                       unix_sockets = array_get(&sockets[i]->clients, &count2);
-                       add_extra_unix_listeners(listener, unix_sockets, count2,
-                                                LISTENER_CLIENT);
-               }
-       }
-}
-
-static void drop_privileges(void)
+static void main_preinit(struct auth_settings *set)
 {
-       const char *version, *name;
-
-       version = getenv("DOVECOT_VERSION");
-       if (version != NULL && strcmp(version, PACKAGE_VERSION) != 0) {
-               i_fatal("Dovecot version mismatch: "
-                       "Master is v%s, dovecot-auth is v"PACKAGE_VERSION" "
-                       "(if you don't care, set version_ignore=yes)", version);
-       }
-
-       standalone = getenv("DOVECOT_MASTER") == NULL;
-       if (standalone && getenv("AUTH_1") == NULL) {
-               i_fatal("dovecot-auth is usually started through "
-                       "dovecot master process. If you wish to run "
-                       "it standalone, you'll need to set AUTH_* "
-                       "environment variables (AUTH_1 isn't set).");
-       }
-       name = getenv("AUTH_NAME");
-       if (name == NULL)
-               i_fatal("Missing AUTH_NAME environment");
-
-       open_logfile();
-
        /* Open /dev/urandom before chrooting */
        random_init();
 
@@ -225,30 +53,22 @@ static void drop_privileges(void)
           only by root. Also load all modules here. */
        passdbs_init();
        userdbs_init();
-       modules = module_dir_load(AUTH_MODULE_DIR, NULL, TRUE, version);
+       modules = module_dir_load(AUTH_MODULE_DIR, NULL, TRUE,
+                                 master_service_get_version_string(service));
        module_dir_init(modules);
-       auth = auth_preinit(auth_settings_read(name));
-       auth_master_listeners_init();
-       if (!worker)
-               add_extra_listeners(auth);
+       auth = auth_preinit(set);
 
        /* Password lookups etc. may require roots, allow it. */
        restrict_access_by_env(NULL, FALSE);
        restrict_access_allow_coredumps(TRUE);
 }
 
-static void main_init(bool nodaemon)
+static void main_init(void)
 {
-       struct auth_master_listener *listener;
+       i_array_init(&listen_fd_types, 8);
 
         process_start_time = ioloop_time;
 
-       lib_signals_init();
-        lib_signals_set_handler(SIGINT, TRUE, sig_die, NULL);
-        lib_signals_set_handler(SIGTERM, TRUE, sig_die, NULL);
-        lib_signals_ignore(SIGPIPE, TRUE);
-        lib_signals_ignore(SIGALRM, FALSE);
-
        /* If auth caches aren't used, just ignore these signals */
        lib_signals_ignore(SIGHUP, TRUE);
        lib_signals_ignore(SIGUSR2, TRUE);
@@ -258,40 +78,16 @@ static void main_init(bool nodaemon)
        password_schemes_init();
        auth_init(auth);
        auth_request_handler_init();
+       auth_master_connections_init();
+       auth_client_connections_init();
 
        if (worker) {
-               worker_client =
-                       auth_worker_client_create(auth, WORKER_SERVER_FD);
-               return;
+               /* workers have only a single connection from the master
+                  auth process */
+               master_service_set_client_limit(service, 1);
+       } else if (getenv("MASTER_AUTH_FD") != NULL) {
+               (void)auth_master_connection_create(auth, MASTER_AUTH_FD);
        }
-
-       if (getenv("DOVECOT_MASTER") == NULL) {
-               /* starting standalone */
-               if (!nodaemon) {
-                       switch (fork()) {
-                       case -1:
-                               i_fatal("fork() failed: %m");
-                       case 0:
-                               break;
-                       default:
-                               exit(0);
-                       }
-
-                       if (setsid() < 0)
-                               i_fatal("setsid() failed: %m");
-
-                       if (chdir("/") < 0)
-                               i_fatal("chdir(/) failed: %m");
-               }
-       } else {
-               listener = auth_master_listener_create(auth);
-               (void)auth_master_connection_create(listener, MASTER_SOCKET_FD);
-               auth_master_listener_add(listener, CLIENT_LISTEN_FD,
-                                        NULL, LISTENER_CLIENT);
-       }
-
-       /* everything initialized, notify masters that all is well */
-       auth_master_listeners_send_handshake();
 }
 
 static void main_deinit(void)
@@ -301,8 +97,9 @@ static void main_deinit(void)
        else
                auth_request_handler_flush_failures(TRUE);
 
+       auth_client_connections_deinit();
+       auth_master_connections_deinit();
         auth_worker_server_deinit();
-       auth_master_listeners_deinit();
 
        module_dir_unload(&modules);
        userdbs_deinit();
@@ -314,41 +111,88 @@ static void main_deinit(void)
        sql_drivers_deinit();
        random_deinit();
 
-       child_wait_deinit();
-       lib_signals_deinit();
-       closelog();
+       array_free(&listen_fd_types);
 }
 
-int main(int argc ATTR_UNUSED, char *argv[])
+static void worker_connected(const struct master_service_connection *conn)
 {
-       bool foreground = FALSE;
+        auth_worker_client_create(auth, conn->fd);
+}
 
-#ifdef DEBUG
-       if (getenv("GDB") == NULL)
-               fd_debug_verify_leaks(WORKER_SERVER_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();
+static void client_connected(const struct master_service_connection *conn)
+{
+       enum auth_socket_type *type;
 
-       while (argv[1] != NULL) {
-               if (strcmp(argv[1], "-F") == 0)
-                       foreground = TRUE;
-               else if (strcmp(argv[1], "-w") == 0)
-                       worker = TRUE;
-               argv++;
+       if (worker) {
+               worker_client = auth_worker_client_create(auth, conn->fd);
+               return;
        }
 
-       T_BEGIN {
-               drop_privileges();
-               main_init(foreground);
-       } T_END;
-        io_loop_run(ioloop);
-       main_deinit();
+       type = array_idx_modifiable(&listen_fd_types, conn->listen_fd);
+       if (*type == AUTH_SOCKET_UNKNOWN) {
+               /* figure out if this is a server or network socket by
+                  checking the socket path name. */
+               struct sockaddr_un sa;
+               socklen_t addrlen = sizeof(sa);
+               size_t len;
+
+               if (getsockname(conn->listen_fd, (void *)&sa, &addrlen) < 0)
+                       i_fatal("getsockname(%d) failed: %m", conn->listen_fd);
+               if (sa.sun_family != AF_UNIX) {
+                       i_fatal("getsockname(%d) isn't UNIX socket",
+                               conn->listen_fd);
+               }
 
-       io_loop_destroy(&ioloop);
-       lib_deinit();
+               len = strlen(sa.sun_path);
+               if (len > 7 && strcmp(sa.sun_path + len - 7, "-master") == 0)
+                       *type = AUTH_SOCKET_MASTER;
+               else
+                       *type = AUTH_SOCKET_CLIENT;
+       }
 
+       switch (*type) {
+       case AUTH_SOCKET_MASTER:
+               (void)auth_master_connection_create(auth, conn->fd);
+               break;
+       case AUTH_SOCKET_CLIENT:
+               (void)auth_client_connection_create(auth, conn->fd);
+               break;
+       default:
+               i_unreached();
+       }
+}
+
+int main(int argc, char *argv[])
+{
+       const char *getopt_str, *auth_name = "default";
+       int c;
+
+       service = master_service_init("auth", 0, argc, argv);
+       master_service_init_log(service, "auth: ", 0);
+
+        getopt_str = t_strconcat("w", master_service_getopt_string(), NULL);
+       while ((c = getopt(argc, argv, getopt_str)) > 0) {
+               switch (c) {
+               case 'a':
+                       auth_name = optarg;
+                       break;
+               case 'w':
+                       worker = TRUE;
+                       break;
+               default:
+                       if (!master_service_parse_option(service, c, optarg))
+                               exit(FATAL_DEFAULT);
+                       break;
+               }
+       }
+
+       main_preinit(auth_settings_read(service, auth_name));
+
+       master_service_init_finish(service);
+       main_init();
+       master_service_run(service, worker ? worker_connected :
+                          client_connected);
+       main_deinit();
+       master_service_deinit(&service);
         return 0;
 }
index fb3b9ce66e0b5d5edfc01e90312d2ae6c507d4ae..4b5f94218c7bcf9bc2171a60c7194fc110e0f332 100644 (file)
@@ -1,8 +1,11 @@
+pkglibexecdir = $(libexecdir)/dovecot
+
 bin_PROGRAMS = doveconf
 
 AM_CPPFLAGS = \
        -I$(top_srcdir)/src/lib \
        -I$(top_srcdir)/src/lib-settings \
+       -I$(top_srcdir)/src/lib-master \
        -DSYSCONFDIR=\""$(sysconfdir)"\" \
        -DPKG_RUNDIR=\""$(rundir)"\" \
        -DPKG_LIBEXECDIR=\""$(pkglibexecdir)"\" \
index 646f3cd2e0584bdd0e199bc2329057ca21d07e24..3af205f988c369c3a35848b1f18a5ce7cbfeab95 100644 (file)
@@ -3,7 +3,6 @@
 
 #include "lib.h"
 
-extern struct master_service *service;
 extern ARRAY_TYPE(const_string) config_strings;
 
 #endif
index a2b8a83821a445b49e1711b85aad52c96acae0c0..a9fc4e62889a5c01eb73fe67e27d7d278dbc71ae 100644 (file)
@@ -3,6 +3,7 @@
 #include "common.h"
 #include "array.h"
 #include "str.h"
+#include "llist.h"
 #include "ioloop.h"
 #include "network.h"
 #include "istream.h"
@@ -20,6 +21,8 @@
 #define CONFIG_CLIENT_PROTOCOL_MINOR_VERSION 0
 
 struct config_connection {
+       struct config_connection *prev, *next;
+
        int fd;
        struct istream *input;
        struct ostream *output;
@@ -29,6 +32,8 @@ struct config_connection {
        unsigned int handshaked:1;
 };
 
+struct config_connection *config_connections = NULL;
+
 static const char *const *
 config_connection_next_line(struct config_connection *conn)
 {
@@ -93,14 +98,12 @@ static void config_connection_input(void *context)
                conn->version_received = TRUE;
        }
 
-       t_push();
        while ((args = config_connection_next_line(conn)) != NULL) {
                if (args[0] == NULL)
                        continue;
                if (strcmp(args[0], "REQ") == 0)
                        config_connection_request(conn, args + 1, 0);
        }
-       t_pop();
 }
 
 struct config_connection *config_connection_create(int fd)
@@ -112,11 +115,14 @@ struct config_connection *config_connection_create(int 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, config_connection_input, conn);
+       DLLIST_PREPEND(&config_connections, conn);
        return conn;
 }
 
 void config_connection_destroy(struct config_connection *conn)
 {
+       DLLIST_REMOVE(&config_connections, conn);
+
        io_remove(&conn->io);
        i_stream_destroy(&conn->input);
        o_stream_destroy(&conn->output);
@@ -147,3 +153,9 @@ void config_connection_putenv(void)
                                    strings[i+1], NULL));
        } T_END;
 }
+
+void config_connections_destroy_all(void)
+{
+       while (config_connections != NULL)
+               config_connection_destroy(config_connections);
+}
index 38dfe50f7d2fe6ae618554ade8361fb76c39a532..ba3911891a5747a88621e01f28851bf479ba193c 100644 (file)
@@ -13,4 +13,6 @@ void config_connection_dump_request(int fd, const char *service,
                                    enum config_dump_flags flags);
 void config_connection_putenv(void);
 
+void config_connections_destroy_all(void);
+
 #endif
index 1fb048ffdcd082a2cee103fd1cb2018a4446d7f6..da4cff525c5b4ed719f9f1732da318407ce8dc7a 100644 (file)
@@ -1,11 +1,9 @@
 /* Copyright (C) 2005-2009 Dovecot authors, see the included COPYING file */
 
 #include "common.h"
-#include "lib-signals.h"
-#include "ioloop.h"
 #include "array.h"
 #include "env-util.h"
-#include "str.h"
+#include "master-service.h"
 #include "config-connection.h"
 #include "config-parser.h"
 
 
 ARRAY_TYPE(const_string) config_strings;
 
-static const char *config_path = SYSCONFDIR "/" PACKAGE ".conf";
+static struct master_service *service;
 static pool_t config_pool;
 
-static void main_init(const char *service)
+static void main_init(const char *service_name)
 {
-       if (getenv("LOG_TO_MASTER") != NULL)
-               i_set_failure_internal();
-
        config_pool = pool_alloconly_create("config parser", 10240);
        p_array_init(&config_strings, config_pool, 256);
-       config_parse_file(config_pool, &config_strings, config_path, service);
+       config_parse_file(config_pool, &config_strings,
+                         master_service_get_config_path(service),
+                         service_name);
+}
+
+static void client_connected(const struct master_service_connection *conn)
+{
+       config_connection_create(conn->fd);
 }
 
 int main(int argc, char *argv[])
 {
        enum config_dump_flags flags = 0;
-       struct ioloop *ioloop;
-       const char *service = "";
+       const char *getopt_str, *service_name = "";
        char **exec_args = NULL;
-       int i;
+       int c;
 
-       lib_init();
+       service = master_service_init("config", 0, argc, argv);
 
-       for (i = 1; i < argc; i++) {
-               if (strcmp(argv[i], "-a") == 0) {
+       getopt_str = t_strconcat("anp:e", master_service_getopt_string(), NULL);
+       while ((c = getopt(argc, argv, getopt_str)) > 0) {
+               if (c == 'e')
+                       break;
+               switch (c) {
+               case 'a':
                        flags |= CONFIG_DUMP_FLAG_HUMAN |
                                CONFIG_DUMP_FLAG_DEFAULTS;
-               } else if (strcmp(argv[i], "-c") == 0) {
-                       /* config file */
-                       i++;
-                       if (i == argc) i_fatal("Missing config file argument");
-                       config_path = argv[i];
-               } else if (strcmp(argv[i], "-n") == 0) {
+                       break;
+               case 'n':
                        flags |= CONFIG_DUMP_FLAG_HUMAN;
-               } else if (strcmp(argv[i], "-s") == 0) {
-                       /* service */
-                       i++;
-                       if (i == argc) i_fatal("Missing service argument");
-                       service = argv[i];
-               } else if (strcmp(argv[i], "--exec") == 0) {
-                       /* <command> [<args>] */
-                       i++;
-                       if (i == argc) i_fatal("Missing exec binary argument");
-                       exec_args = &argv[i];
                        break;
-               } else {
-                       i_fatal("Unknown parameter: %s", argv[i]);
+               case 'p':
+                       service_name = optarg;
+                       break;
+               default:
+                       if (!master_service_parse_option(service, c, optarg))
+                               exit(FATAL_DEFAULT);
                }
        }
+       if (argv[optind] != NULL)
+               exec_args = &argv[optind];
+
+       master_service_init_log(service, "doveconf: ", 0);
+       master_service_init_finish(service);
+       main_init(service_name);
 
-       main_init(service);
-       ioloop = io_loop_create();
-       if (exec_args == NULL)
+       if (master_service_get_socket_count(service) > 0)
+               master_service_run(service, client_connected);
+       else if (exec_args == NULL)
                config_connection_dump_request(STDOUT_FILENO, "master", flags);
        else {
                config_connection_putenv();
@@ -74,8 +75,8 @@ int main(int argc, char *argv[])
                execvp(exec_args[0], exec_args);
                i_fatal("execvp(%s) failed: %m", exec_args[0]);
        }
+       config_connections_destroy_all();
        pool_unref(&config_pool);
-       io_loop_destroy(&ioloop);
-       lib_deinit();
+       master_service_deinit(&service);
         return 0;
 }
index 7ddd9cfe451a5de1725e62b2d961ad704e0ffd93..08148ebee93a088ed74813a5e8f273417266ea29 100755 (executable)
@@ -38,7 +38,7 @@ foreach my $file (@ARGV) {
        $code .= $_;
       }
 
-      if (/#define.*DEF/ || /^#undef.*DEF/) {
+      if (/#define.*DEF/ || /^#undef.*DEF/ || /ARRAY_DEFINE_TYPE.*_settings/) {
        $write = 1;
        $state = 2 if (/\\$/);
       }
index 7b39f75a3d62e69b770578901750886edc5a0ca8..1dc66a878b6b49bb0d19b6d640c59857a668cca3 100644 (file)
@@ -6,6 +6,7 @@ AM_CPPFLAGS = \
        -I$(top_srcdir)/src/lib \
        -I$(top_srcdir)/src/lib-auth \
        -I$(top_srcdir)/src/lib-imap \
+       -I$(top_srcdir)/src/lib-master \
        -I$(top_srcdir)/src/login-common
 
 imap_login_LDADD = \
index fbd8c57335e416959d71916cac16ef4a7373fb54..6d0b9b6198b5fb0c0dc68cba37a65a19d4951b11 100644 (file)
@@ -11,6 +11,8 @@
 #include "strescape.h"
 #include "imap-parser.h"
 #include "imap-id.h"
+#include "master-service.h"
+#include "master-auth.h"
 #include "client.h"
 #include "client-authenticate.h"
 #include "auth-client.h"
 #  error client idle timeout must be smaller than authentication timeout
 #endif
 
-#define AUTH_WAITING_MSG \
+#define AUTH_SERVER_WAITING_MSG \
        "* OK Waiting for authentication process to respond.."
+#define AUTH_MASTER_WAITING_MSG \
+       "* OK Waiting for authentication master process to respond.."
 
 const char *login_protocol = "IMAP";
+const char *login_process_name = "imap-login";
 
 static void client_set_title(struct imap_client *client)
 {
@@ -119,7 +124,6 @@ static void client_start_tls(struct imap_client *client)
        int fd_ssl;
 
        client_ref(client);
-       connection_queue_add(1);
        if (!client_unref(client) || client->destroyed)
                return;
 
@@ -132,6 +136,7 @@ static void client_start_tls(struct imap_client *client)
                return;
        }
 
+       client->common.proxying = TRUE;
        client->common.tls = TRUE;
        client->common.secured = TRUE;
        client_set_title(client);
@@ -410,7 +415,7 @@ void client_input(struct imap_client *client)
        if (!auth_client_is_connected(auth_client)) {
                /* we're not yet connected to auth process -
                   don't allow any commands */
-               client_send_line(client, AUTH_WAITING_MSG);
+               client_send_line(client, AUTH_SERVER_WAITING_MSG);
                if (client->to_auth_waiting != NULL)
                        timeout_remove(&client->to_auth_waiting);
 
@@ -483,7 +488,8 @@ static void client_idle_disconnect_timeout(struct imap_client *client)
 
 static void client_auth_waiting_timeout(struct imap_client *client)
 {
-       client_send_line(client, AUTH_WAITING_MSG);
+       client_send_line(client, client->common.master_tag == 0 ?
+                        AUTH_SERVER_WAITING_MSG : AUTH_MASTER_WAITING_MSG);
        timeout_remove(&client->to_auth_waiting);
 }
 
@@ -502,7 +508,11 @@ struct client *client_create(int fd, bool ssl, const struct ip_addr *local_ip,
 
        i_assert(fd != -1);
 
-       connection_queue_add(1);
+       if (clients_get_count() >= login_settings->login_max_connections) {
+               /* reached max. users count, kill few of the
+                  oldest connections */
+               client_destroy_oldest();
+       }
 
        /* always use nonblocking I/O */
        net_set_nonblock(fd, TRUE);
@@ -524,8 +534,6 @@ struct client *client_create(int fd, bool ssl, const struct ip_addr *local_ip,
 
        client_link(&client->common);
 
-       main_ref();
-
        if (auth_client_is_connected(auth_client))
                client_send_greeting(client);
        else
@@ -559,10 +567,11 @@ void client_destroy(struct imap_client *client, const char *reason)
        if (client->output != NULL)
                o_stream_close(client->output);
 
-       if (client->common.master_tag != 0)
-               master_request_abort(&client->common);
-
-       if (client->common.auth_request != NULL) {
+       if (client->common.master_tag != 0) {
+               i_assert(client->common.auth_request == NULL);
+               i_assert(client->common.authenticating);
+               master_auth_request_abort(service, client->common.master_tag);
+       } else if (client->common.auth_request != NULL) {
                i_assert(client->common.authenticating);
                sasl_server_auth_client_error(&client->common, NULL);
        } else {
@@ -602,9 +611,6 @@ void client_destroy(struct imap_client *client, const char *reason)
                client->common.proxy = NULL;
        }
        client_unref(client);
-
-       main_listen_start();
-       main_unref();
 }
 
 void client_destroy_success(struct imap_client *client, const char *reason)
@@ -640,10 +646,14 @@ bool client_unref(struct imap_client *client)
        if (client->output != NULL)
                o_stream_unref(&client->output);
 
+       if (!client->common.proxying) {
+               i_assert(client->common.proxy == NULL);
+               master_service_client_connection_destroyed(service);
+       }
+
        i_free(client->common.virtual_user);
        i_free(client->common.auth_mech_name);
        i_free(client);
-
        return FALSE;
 }
 
index 925d415327ec3b34e5fcaf1783968b94a34206fd..09c4e53f590892172e0c6cd451d3aa658790d570 100644 (file)
@@ -2,7 +2,6 @@
 #define CLIENT_H
 
 #include "network.h"
-#include "master.h"
 #include "client-common.h"
 
 /* Disconnect client after idling this many milliseconds */
index a18e12c3a17a58a5d38a7df86b7ad0e0473fb673..d3c0338c3310849583d16c7ea4f226241168ceb6 100644 (file)
@@ -244,6 +244,7 @@ static int proxy_input_line(struct imap_client *client,
                client->common.input = NULL;
                client->output = NULL;
                client->common.fd = -1;
+               client->common.proxying = TRUE;
                client_destroy_success(client, str_c(str));
                return 1;
        } else if (strncmp(line, "L ", 2) == 0) {
@@ -362,7 +363,6 @@ int imap_proxy_new(struct imap_client *client, const char *host,
        }
 
        i_assert(client->refcount > 1);
-       connection_queue_add(1);
 
        if (client->destroyed) {
                /* connection_queue_add() decided that we were the oldest
index 44eccc34892a0a2add267933ef0e1a02ba5724e7..fc39a77e7c2e9959422924ec79721a1c507ae401 100644 (file)
@@ -19,7 +19,7 @@
 #include <unistd.h>
 
 #define IS_STANDALONE() \
-        (getenv("IMAPLOGINTAG") == NULL)
+        (getenv("CLIENT_INPUT") == NULL)
 
 struct client_workaround_list {
        const char *name;
@@ -70,12 +70,44 @@ parse_workarounds(const struct imap_settings *set)
        return client_workarounds;
 }
 
+static void client_add_input(struct client *client, const char *input)
+{
+       buffer_t *buf;
+       const char *tag;
+       unsigned int data_pos;
+
+       buf = input == NULL ? NULL : t_base64_decode_str(input);
+       if (buf != NULL && buf->used > 0) {
+               tag = t_strndup(buf->data, buf->used);
+               data_pos = strlen(tag) + 1;
+               if (data_pos > buf->used &&
+                   !i_stream_add_data(client->input,
+                                      CONST_PTR_OFFSET(buf->data, data_pos),
+                                      buf->used - data_pos))
+                       i_panic("Couldn't add client input to stream");
+       } else {
+               /* IMAPLOGINTAG environment is compatible with mailfront */
+               tag = getenv("IMAPLOGINTAG");
+       }
+
+       if (tag == NULL) {
+               client_send_line(client, t_strconcat(
+                       "* PREAUTH [CAPABILITY ",
+                       str_c(client->capability_string), "] "
+                       "Logged in as ", client->user->username, NULL));
+       } else {
+               client_send_line(client, t_strconcat(
+                       tag, " OK [CAPABILITY ",
+                       str_c(client->capability_string), "] Logged in", NULL));
+       }
+       (void)client_handle_input(client);
+}
+
 static void main_init(const struct imap_settings *set, struct mail_user *user,
                      bool dump_capability)
 {
        struct client *client;
        struct ostream *output;
-       const char *str, *tag;
 
        if (set->shutdown_clients && !dump_capability) {
                /* If master dies, the log fd gets closed and we'll quit */
@@ -96,29 +128,7 @@ static void main_init(const struct imap_settings *set, struct mail_user *user,
        output = client->output;
        o_stream_ref(output);
        o_stream_cork(output);
-
-       /* IMAPLOGINTAG environment is compatible with mailfront */
-       tag = getenv("IMAPLOGINTAG");
-       if (tag == NULL) {
-               client_send_line(client, t_strconcat(
-                       "* PREAUTH [CAPABILITY ",
-                       str_c(client->capability_string), "] "
-                       "Logged in as ", user->username, NULL));
-       } else {
-               client_send_line(client, t_strconcat(
-                       tag, " OK [CAPABILITY ",
-                       str_c(client->capability_string), "] Logged in", NULL));
-       }
-       str = getenv("CLIENT_INPUT");
-       if (str != NULL) T_BEGIN {
-               buffer_t *buf = t_base64_decode_str(str);
-               if (buf->used > 0) {
-                       if (!i_stream_add_data(client->input, buf->data,
-                                              buf->used))
-                               i_panic("Couldn't add client input to stream");
-                       (void)client_handle_input(client);
-               }
-       } T_END;
+       client_add_input(client, getenv("CLIENT_INPUT"));
         o_stream_uncork(output);
        o_stream_unref(&output);
 }
@@ -130,6 +140,12 @@ static void main_deinit(void)
        clients_deinit();
 }
 
+static void client_connected(const struct master_service_connection *conn)
+{
+       /* FIXME: we can't handle this yet */
+       (void)close(conn->fd);
+}
+
 int main(int argc, char *argv[], char *envp[])
 {
        const struct setting_parser_info *set_roots[] = {
@@ -145,10 +161,6 @@ int main(int argc, char *argv[], char *envp[])
        const char *value;
        int c;
 
-#ifdef DEBUG
-       if (!IS_STANDALONE() && getenv("GDB") == NULL)
-               fd_debug_verify_leaks(3, 1024);
-#endif
        if (IS_STANDALONE() && getuid() == 0 &&
            net_getpeername(1, NULL, NULL) == 0) {
                printf("* BAD [ALERT] imap binary must not be started from "
@@ -170,7 +182,7 @@ int main(int argc, char *argv[], char *envp[])
        service = master_service_init("imap", service_flags, argc, argv);
        while ((c = getopt(argc, argv, master_service_getopt_string())) > 0) {
                if (!master_service_parse_option(service, c, optarg))
-                       i_fatal("Unknown argument: %c", c);
+                       exit(FATAL_DEFAULT);
        }
 
        memset(&input, 0, sizeof(input));
@@ -200,9 +212,11 @@ int main(int argc, char *argv[], char *envp[])
           while initializing */
        io_loop_set_running(current_ioloop);
 
-       main_init(set, mail_user, dump_capability);
+       T_BEGIN {
+               main_init(set, mail_user, dump_capability);
+       } T_END;
        if (io_loop_is_running(current_ioloop))
-               master_service_run(service);
+               master_service_run(service, client_connected);
 
        main_deinit();
        mail_storage_service_deinit_user();
index 0b8d0af83c8ecbb677f1ac655d3443f0b496820d..060311146aeeecb270b91fa381642b35875de736 100644 (file)
@@ -224,9 +224,6 @@ int main(int argc, char *argv[])
 
        service = master_service_init("lda", MASTER_SERVICE_FLAG_STANDALONE,
                                      argc, argv);
-#ifdef SIGXFSZ
-        lib_signals_ignore(SIGXFSZ, TRUE);
-#endif
 
        memset(&ctx, 0, sizeof(ctx));
        ctx.pool = pool_alloconly_create("mail deliver context", 256);
@@ -282,8 +279,7 @@ int main(int argc, char *argv[])
                default:
                        if (!master_service_parse_option(service, c, optarg)) {
                                print_help();
-                               i_fatal_status(EX_USAGE,
-                                              "Unknown argument: %c", c);
+                               exit(EX_USAGE);
                        }
                        break;
                }
@@ -325,6 +321,9 @@ int main(int argc, char *argv[])
        ctx.dest_user = mail_storage_service_init_user(service, &service_input,
                                                       set_roots,
                                                       service_flags);
+#ifdef SIGXFSZ
+        lib_signals_ignore(SIGXFSZ, TRUE);
+#endif
        ctx.set = mail_storage_service_get_settings(service);
         duplicate_init(mail_user_set_get_storage_set(ctx.dest_user->set));
 
index a0afbe0b8d3cd78eb8fc1360f6a6d8457573b970..cdb8fefd8a2e8d448d9b45349b0008af61153a58 100644 (file)
@@ -8,11 +8,14 @@ AM_CPPFLAGS = \
        -DPKG_RUNDIR=\""$(rundir)"\"
 
 libmaster_la_SOURCES = \
+       master-auth.c \
        master-service.c \
        master-service-settings.c \
        syslog-util.c
 
 noinst_HEADERS = \
+       master-auth.h \
+       master-interface.h \
        master-service.h \
        master-service-private.h \
        master-service-settings.h \
diff --git a/src/lib-master/master-auth.c b/src/lib-master/master-auth.c
new file mode 100644 (file)
index 0000000..fbee1f7
--- /dev/null
@@ -0,0 +1,191 @@
+/* Copyright (C) 2005 Timo Sirainen */
+
+#include "lib.h"
+#include "ioloop.h"
+#include "fdpass.h"
+#include "buffer.h"
+#include "hash.h"
+#include "master-service-private.h"
+#include "master-auth.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+struct master_auth {
+       struct master_service *service;
+       pool_t pool;
+
+       int fd;
+       struct io *io;
+
+       unsigned int tag_counter;
+       struct hash_table *requests;
+
+       char buf[sizeof(struct master_auth_reply)];
+       unsigned int buf_pos;
+
+       /* linked list, node->context is the next pointer */
+       struct master_auth_request_node *free_nodes;
+};
+
+struct master_auth_request_node {
+       master_auth_callback_t *callback;
+       void *context;
+};
+
+static struct master_auth_request_node aborted_node;
+
+unsigned int master_auth_request(struct master_service *service, int fd,
+                                const struct master_auth_request *request,
+                                const unsigned char *data,
+                                master_auth_callback_t *callback,
+                                void *context)
+{
+       struct master_auth *auth = service->auth;
+        struct master_auth_request_node *node;
+       struct master_auth_request req;
+       buffer_t *buf;
+       struct stat st;
+       ssize_t ret;
+
+       i_assert(request->auth_pid != 0);
+
+       req = *request;
+       req.tag = ++auth->tag_counter;
+       if (req.tag == 0)
+               req.tag = ++auth->tag_counter;
+
+       if (fstat(fd, &st) < 0)
+               i_fatal("fstat(auth dest fd) failed: %m");
+       req.ino = st.st_ino;
+
+       buf = buffer_create_dynamic(pool_datastack_create(),
+                                   sizeof(req) + req.data_size);
+       buffer_append(buf, &req, sizeof(req));
+       buffer_append(buf, data, req.data_size);
+
+       ret = fd_send(auth->fd, fd, buf->data, buf->used);
+       if (ret < 0)
+               i_fatal("fd_send(%d) failed: %m", fd);
+       if ((size_t)ret != buf->used) {
+               i_fatal("fd_send() sent only %d of %d bytes",
+                       (int)ret, (int)buf->used);
+       }
+
+       if (auth->free_nodes == NULL)
+               node = p_new(auth->pool, struct master_auth_request_node, 1);
+       else {
+               node = auth->free_nodes;
+                auth->free_nodes = node->context;
+       }
+       node->callback = callback;
+       node->context = context;
+
+       hash_table_insert(auth->requests, POINTER_CAST(req.tag), node);
+       return req.tag;
+}
+
+void master_auth_request_abort(struct master_service *service, unsigned int tag)
+{
+       struct master_auth *auth = service->auth;
+        struct master_auth_request_node *node;
+
+       node = hash_table_lookup(auth->requests, POINTER_CAST(tag));
+       if (node == NULL)
+               i_panic("master_auth_request_abort(): tag %u not found", tag);
+
+       i_assert(node != &aborted_node);
+       hash_table_update(auth->requests, POINTER_CAST(tag), &aborted_node);
+
+       node->callback = NULL;
+       node->context = auth->free_nodes;
+       auth->free_nodes = node;
+}
+
+static void request_handle(struct master_auth *auth,
+                          struct master_auth_reply *reply)
+{
+        struct master_auth_request_node *node;
+
+       node = hash_table_lookup(auth->requests, POINTER_CAST(reply->tag));
+       if (node == NULL)
+               i_error("Master sent reply with unknown tag %u", reply->tag);
+
+       if (node != &aborted_node) {
+               node->callback(reply, node->context);
+
+               /* the callback may have called master_auth_request_abort(),
+                  which would have put the node to free_nodes list already */
+               if (node->callback != NULL) {
+                       node->callback = NULL;
+                       node->context = auth->free_nodes;
+                       auth->free_nodes = node;
+               }
+       }
+
+       hash_table_remove(auth->requests, POINTER_CAST(reply->tag));
+}
+
+static void master_auth_input(void *context)
+{
+       struct master_auth *auth = context;
+       int ret;
+
+       ret = net_receive(auth->fd, auth->buf + auth->buf_pos,
+                         sizeof(auth->buf) - auth->buf_pos);
+       if (ret < 0) {
+               /* master died, kill all clients logging in */
+                master_service_stop(auth->service);
+               return;
+       }
+
+       auth->buf_pos += ret;
+       if (auth->buf_pos < sizeof(auth->buf))
+               return;
+
+       /* reply is now read */
+       request_handle(auth, (struct master_auth_reply *) auth->buf);
+       auth->buf_pos = 0;
+}
+
+void master_auth_init(struct master_service *service)
+{
+       struct master_auth *auth;
+       struct ip_addr ip;
+       pool_t pool;
+
+       i_assert(service->auth == NULL);
+
+       if (getenv("MASTER_AUTH_FD") == NULL)
+               i_fatal("auth_dest_service setting not set");
+
+       if (net_getsockname(MASTER_AUTH_FD, &ip, NULL) < 0 ||
+           ip.family != AF_UNIX)
+               i_fatal("MASTER_AUTH_FD not given");
+
+       pool = pool_alloconly_create("master auth", 1024);
+       auth = p_new(pool, struct master_auth, 1);
+       auth->pool = pool;
+       auth->service = service;
+       auth->fd = MASTER_AUTH_FD;
+       auth->requests = hash_table_create(default_pool, pool, 0, NULL, NULL);
+       auth->io = io_add(auth->fd, IO_READ, master_auth_input, auth);
+
+       service->auth = auth;
+}
+
+void master_auth_deinit(struct master_service *service)
+{
+       struct master_auth *auth = service->auth;
+
+       i_assert(service->auth != NULL);
+
+       hash_table_destroy(&auth->requests);
+       if (auth->io != NULL)
+               io_remove(&auth->io);
+       if (close(auth->fd) < 0)
+               i_fatal("close(master auth) failed: %m");
+       pool_unref(&auth->pool);
+       service->auth = NULL;
+}
diff --git a/src/lib-master/master-auth.h b/src/lib-master/master-auth.h
new file mode 100644 (file)
index 0000000..6d1b743
--- /dev/null
@@ -0,0 +1,26 @@
+#ifndef MASTER_AUTH_H
+#define MASTER_AUTH_H
+
+struct master_service;
+struct master_auth_request;
+struct master_auth_reply;
+
+typedef void master_auth_callback_t(const struct master_auth_reply *reply,
+                                   void *context);
+
+/* Send an authentication request. The fd contains the file descriptor to
+   transfer, or -1 if no fd is wanted to be transferred. Returns tag which can
+   be used to abort the request (ie. ignore the reply from master).
+   request->tag is ignored. */
+unsigned int master_auth_request(struct master_service *service, int fd,
+                                const struct master_auth_request *request,
+                                const unsigned char *data,
+                                master_auth_callback_t *callback,
+                                void *context);
+void master_auth_request_abort(struct master_service *service,
+                              unsigned int tag);
+
+void master_auth_init(struct master_service *service);
+void master_auth_deinit(struct master_service *service);
+
+#endif
diff --git a/src/lib-master/master-interface.h b/src/lib-master/master-interface.h
new file mode 100644 (file)
index 0000000..600173b
--- /dev/null
@@ -0,0 +1,104 @@
+#ifndef MASTER_INTERFACE_H
+#define MASTER_INTERFACE_H
+
+#include "network.h"
+
+/* We are attempting semi-compatibility with Postfix's master process here.
+   Whether this is useful or not remains to be seen. */
+
+/* Child processes should send status updates whenever they accept a new
+   connection (decrease available_count) and when they close existing
+   connection (increase available_count). */
+struct master_status {
+       pid_t pid;
+       /* uid is used to check for old/invalid status messages */
+       unsigned int uid;
+       /* number of new connections process is currently accepting */
+       unsigned int available_count;
+};
+
+/* When connecting to log service, send this handshake first */
+struct log_service_handshake {
+       /* If magic is invalid, assume the data is already what we want
+          to log */
+#define MASTER_LOG_MAGIC 0x02ff03fe
+       unsigned int log_magic;
+
+       /* If we're writing log lines more often than this, start throttling */
+       unsigned int max_lines_per_sec;
+
+       /* Add this previs to each logged line */
+       unsigned int prefix_len;
+       /* unsigned char prefix[]; */
+};
+
+/* This should be kept in sync with LOGIN_MAX_INBUF_SIZE. Multiply it by two
+   to make sure there's space to transfer the command tag  */
+#define MASTER_AUTH_MAX_DATA_SIZE (4096*2)
+
+/* Authentication request. File descriptor may be sent along with the
+   request. */
+struct master_auth_request {
+       /* Request tag. Reply is sent back using same tag. */
+       unsigned int tag;
+
+       /* Authentication process and authentication ID. */
+       pid_t auth_pid;
+       unsigned int auth_id;
+
+       /* Local and remote IPs of the connection. The file descriptor
+          itself may be a local socketpair. */
+       struct ip_addr local_ip, remote_ip;
+
+       /* request follows this many bytes of client input */
+       uint32_t data_size;
+       /* inode of the transferred fd. verified just to be sure that the
+          correct fd is mapped to the correct struct. */
+       ino_t ino;
+};
+
+enum master_auth_status {
+       MASTER_AUTH_STATUS_OK,
+       MASTER_AUTH_STATUS_INTERNAL_ERROR,
+       /* user reached max. simultaneous connections */
+       MASTER_AUTH_STATUS_MAX_CONNECTIONS
+};
+
+struct master_auth_reply {
+       unsigned int tag;
+       enum master_auth_status status;
+       /* PID of the post-login mail process handling this connection */
+       pid_t mail_pid;
+};
+
+/* getenv(MASTER_UID_ENV) provides master_status.uid value */
+#define MASTER_UID_ENV "GENERATION"
+
+/* getenv(MASTER_CLIENT_LIMIT_ENV) provides maximum
+   master_status.available_count as specified in configuration file */
+#define MASTER_CLIENT_LIMIT_ENV "CLIENT_LIMIT"
+
+/* getenv(MASTER_CONFIG_FILE_ENV) provides path to configuration file/socket */
+#define MASTER_CONFIG_FILE_ENV "CONFIG_FILE"
+
+/* 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
+
+/* Socket for sending master_auth_requests. Also used by auth server process
+   as a master socket. */
+#define MASTER_AUTH_FD 4
+
+/* Shared pipe to master, used to send master_status reports */
+#define MASTER_STATUS_FD 5
+/* First file descriptor where process is expected to be listening.
+   The file descriptor count is given in -s parameter, defaulting to 1.
+
+   master_status.available_count reports how many accept()s we're still
+   accepting. Once no children are listening, master will do it and create
+   new child processes when needed. */
+#define MASTER_LISTEN_FD_FIRST 6
+
+#endif
index ac500e6194b8e8cba237b20dafa3a30179c23acb..e64f7ad74658791342855255ba752900f41e25a4 100644 (file)
@@ -1,8 +1,15 @@
 #ifndef MASTER_SERVICE_PRIVATE_H
 #define MASTER_SERVICE_PRIVATE_H
 
+#include "master-interface.h"
 #include "master-service.h"
 
+struct master_service_listener {
+       struct master_service *service;
+       int fd;
+       struct io *io;
+};
+
 struct master_service {
        struct ioloop *ioloop;
 
@@ -16,12 +23,24 @@ struct master_service {
        const char *config_path;
        int syslog_facility;
 
+       unsigned int socket_count;
+        struct master_service_listener *listeners;
+
+       struct io *io_status_write, *io_status_error;
+       unsigned int service_count_left;
+       unsigned int total_available_count;
+       struct master_status master_status;
+
+        struct master_auth *auth;
+       master_service_connection_callback_t *callback;
+
        pool_t set_pool;
        const struct master_service_settings *set;
        struct setting_parser_context *set_parser;
 
        unsigned int keep_environment:1;
        unsigned int log_directly:1;
+       unsigned int initial_status_sent:1;
 };
 
 #endif
index 3ef84a65ac840ae3b521644e1f1a7598e4c07433..e21fbc831ed854729d6d1d7d3b3789ecc012d1ad 100644 (file)
@@ -2,6 +2,8 @@
 
 #include "lib.h"
 #include "array.h"
+#include "istream.h"
+#include "write-full.h"
 #include "settings-parser.h"
 #include "master-service-private.h"
 #include "master-service-settings.h"
@@ -9,9 +11,13 @@
 #include <stddef.h>
 #include <stdlib.h>
 #include <unistd.h>
+#include <sys/stat.h>
 
 #define DOVECOT_CONFIG_BIN_PATH BINDIR"/doveconf"
 
+#define CONFIG_HANDSHAKE "VERSION\t1\t0\n"
+#define CONFIG_REQUEST_SERVICE "REQ\tservice=%s\n"
+
 #undef DEF
 #define DEF(type, name) \
        { type, #name, offsetof(struct master_service_settings, name), NULL }
@@ -57,17 +63,49 @@ master_service_exec_config(struct master_service *service, bool preserve_home)
        /* @UNSAFE */
        conf_argv = t_new(const char *, 6 + (service->argc + 1) + 1);
        conf_argv[0] = DOVECOT_CONFIG_BIN_PATH;
-       conf_argv[1] = "-s";
+       conf_argv[1] = "-p";
        conf_argv[2] = service->name;
        conf_argv[3] = "-c";
        conf_argv[4] = service->config_path;
-       conf_argv[5] = "--exec";
+       conf_argv[5] = "-e";
        memcpy(conf_argv+6, service->argv,
               (service->argc+1) * sizeof(conf_argv[0]));
        execv(conf_argv[0], (char **)conf_argv);
        i_fatal("execv(%s) failed: %m", conf_argv[0]);
 }
 
+static int
+master_service_read_config(struct master_service *service, bool preserve_home,
+                          const char **error_r)
+{
+       const char *path, *str;
+       int fd;
+
+       path = master_service_get_config_path(service);
+       fd = net_connect_unix(path);
+       if (fd < 0) {
+               struct stat st;
+
+               *error_r = t_strdup_printf("net_connect_unix(%s) failed: %m",
+                                          path);
+
+               if (stat(path, &st) == 0 && !S_ISFIFO(st.st_mode)) {
+                       /* it's a file, not a socket */
+                       master_service_exec_config(service, preserve_home);
+               }
+               return -1;
+       }
+       net_set_nonblock(fd, FALSE);
+
+       str = t_strdup_printf(CONFIG_HANDSHAKE CONFIG_REQUEST_SERVICE,
+                             service->name);
+       if (write_full(fd, str, strlen(str)) < 0) {
+               *error_r = t_strdup_printf("write_full(%s) failed: %m", path);
+               return -1;
+       }
+       return fd;
+}
+
 int master_service_settings_read(struct master_service *service,
                                 const struct setting_parser_info *roots[],
                                 const struct dynamic_settings_parser *dyn_parsers,
@@ -76,12 +114,18 @@ int master_service_settings_read(struct master_service *service,
        ARRAY_DEFINE(all_roots, const struct setting_parser_info *);
        const struct setting_parser_info *tmp_root;
        struct setting_parser_context *parser;
+       struct istream *input;
        const char *error;
        void **sets;
        unsigned int i;
+       int ret, fd = -1;
 
-       if (getenv("DOVECONF_ENV") == NULL)
-               master_service_exec_config(service, preserve_home);
+       if (getenv("DOVECONF_ENV") == NULL) {
+               fd = master_service_read_config(service, preserve_home,
+                                               error_r);
+               if (fd == -1)
+                       return -1;
+       }
 
        if (service->set_pool != NULL)
                p_clear(service->set_pool);
@@ -96,16 +140,29 @@ int master_service_settings_read(struct master_service *service,
        p_array_init(&all_roots, service->set_pool, 8);
        tmp_root = &master_service_setting_parser_info;
        array_append(&all_roots, &tmp_root, 1);
-       for (i = 0; roots[i] != NULL; i++)
-               array_append(&all_roots, &roots[i], 1);
+       if (roots != NULL) {
+               for (i = 0; roots[i] != NULL; i++)
+                       array_append(&all_roots, &roots[i], 1);
+       }
 
        parser = settings_parser_init_list(service->set_pool,
                        array_idx(&all_roots, 0), array_count(&all_roots),
                        SETTINGS_PARSER_FLAG_IGNORE_UNKNOWN_KEYS);
 
-       if (settings_parse_environ(parser) < 0) {
-               *error_r = settings_parser_get_error(parser);
-               return -1;
+       if (fd != -1) {
+               input = i_stream_create_fd(fd, (size_t)-1, FALSE);
+               ret = settings_parse_stream_read(parser, input);
+               i_stream_unref(&input);
+               i_assert(ret <= 0);
+               if (ret < 0) {
+                       *error_r = settings_parser_get_error(parser);
+                       return -1;
+               }
+       } else {
+               if (settings_parse_environ(parser) < 0) {
+                       *error_r = settings_parser_get_error(parser);
+                       return -1;
+               }
        }
 
        if (settings_parser_check(parser, service->set_pool, &error) < 0) {
index 5ae49a2256f5bce6fef8913693d3d1d8e6b74e6a..871c504398d57b3b4f8f4e976149e3663fb15b95 100644 (file)
@@ -13,6 +13,7 @@
 
 #include <stdlib.h>
 #include <unistd.h>
+#include <sys/stat.h>
 #include <syslog.h>
 
 #define DEFAULT_CONFIG_FILE_PATH SYSCONFDIR"/dovecot.conf"
 /* getenv(MASTER_DOVECOT_VERSION_ENV) provides master's version number */
 #define MASTER_DOVECOT_VERSION_ENV "DOVECOT_VERSION"
 
+static void io_listeners_add(struct master_service *service);
+static void io_listeners_remove(struct master_service *service);
+static void master_status_update(struct master_service *service);
+
 const char *master_service_getopt_string(void)
 {
-       return "c:Lk";
+       return "c:ks:L";
 }
 
 static void sig_die(const siginfo_t *si, void *context)
@@ -69,7 +74,7 @@ master_service_init(const char *name, enum master_service_flags flags,
           is properly initialized */
        i_set_failure_prefix(t_strdup_printf("%s(init): ", name));
 
-       if (getenv("LOG_TO_MASTER") == NULL)
+       if (getenv(MASTER_UID_ENV) == NULL)
                flags |= MASTER_SERVICE_FLAG_STANDALONE;
 
        service = i_new(struct master_service, 1);
@@ -78,18 +83,22 @@ master_service_init(const char *name, enum master_service_flags flags,
        service->name = i_strdup(name);
        service->flags = flags;
        service->ioloop = io_loop_create();
+       service->service_count_left = (unsigned int)-1;
+
        service->config_path = getenv(MASTER_CONFIG_FILE_ENV);
        if (service->config_path == NULL)
                service->config_path = DEFAULT_CONFIG_FILE_PATH;
 
-       if ((flags & MASTER_SERVICE_FLAG_STANDALONE) == 0)
+       if ((flags & MASTER_SERVICE_FLAG_STANDALONE) == 0) {
                service->version_string = getenv(MASTER_DOVECOT_VERSION_ENV);
-       else
+               service->socket_count = 1;
+       } else {
                service->version_string = PACKAGE_VERSION;
+       }
 
        /* set up some kind of logging until we know exactly how and where
           we want to log */
-       if (getenv("LOG_TO_MASTER") != NULL)
+       if (getenv("LOG_SERVICE") != NULL)
                i_set_failure_internal();
        if (getenv("USER") != NULL) {
                i_set_failure_prefix(t_strdup_printf("%s(%s): ",
@@ -98,18 +107,12 @@ master_service_init(const char *name, enum master_service_flags flags,
                i_set_failure_prefix(t_strdup_printf("%s: ", name));
        }
 
-       /* set default signal handlers */
-       lib_signals_init();
-        lib_signals_ignore(SIGPIPE, TRUE);
-        lib_signals_ignore(SIGALRM, FALSE);
-        lib_signals_set_handler(SIGINT, TRUE, sig_die, service);
-       lib_signals_set_handler(SIGTERM, TRUE, sig_die, service);
-
        master_service_verify_version(service);
        return service;
 }
 
-void master_service_init_log(struct master_service *service, const char *prefix)
+void master_service_init_log(struct master_service *service, const char *prefix,
+                            unsigned int max_lines_per_sec)
 {
        const char *path;
 
@@ -118,13 +121,18 @@ void master_service_init_log(struct master_service *service, const char *prefix)
                return;
        }
 
-       if (getenv("LOG_TO_MASTER") != NULL && !service->log_directly) {
-               /* logging via master process */
+       if (getenv("LOG_SERVICE") != NULL && !service->log_directly) {
+               /* logging via log service */
                i_set_failure_internal();
                i_set_failure_prefix(prefix);
                return;
        }
 
+       if (service->set == NULL) {
+               i_set_failure_file("/dev/stderr", prefix);
+               return;
+       }
+
        if (*service->set->log_path == '\0') {
                /* log to syslog */
                int facility;
@@ -149,6 +157,8 @@ void master_service_init_log(struct master_service *service, const char *prefix)
 bool master_service_parse_option(struct master_service *service,
                                 int opt, const char *arg)
 {
+       int i;
+
        switch (opt) {
        case 'c':
                service->config_path = arg;
@@ -156,6 +166,11 @@ bool master_service_parse_option(struct master_service *service,
        case 'k':
                service->keep_environment = TRUE;
                break;
+       case 's':
+               if ((i = atoi(arg)) < 0)
+                       i_fatal("Invalid socket count: %s", arg);
+                service->socket_count = i;
+               break;
        case 'L':
                service->log_directly = TRUE;
                break;
@@ -165,6 +180,77 @@ bool master_service_parse_option(struct master_service *service,
        return TRUE;
 }
 
+static void master_status_error(void *context)
+{
+       struct master_service *service = context;
+
+       /* status fd is a write-only pipe, so if we're here it means the
+          master wants us to die (or died itself). don't die until all
+          service connections are finished. */
+       io_remove(&service->io_status_error);
+
+       /* the log fd may also be closed already, don't die when trying to
+          log later */
+       i_set_failure_ignore_errors(TRUE);
+
+       if (service->master_status.available_count ==
+           service->total_available_count)
+               master_service_stop(service);
+}
+
+void master_service_init_finish(struct master_service *service)
+{
+       struct stat st;
+       const char *value;
+       unsigned int count;
+
+       i_assert(service->total_available_count == 0);
+       i_assert(service->service_count_left == (unsigned int)-1);
+
+       /* set default signal handlers */
+       lib_signals_init();
+        lib_signals_ignore(SIGPIPE, TRUE);
+        lib_signals_ignore(SIGALRM, FALSE);
+        lib_signals_set_handler(SIGINT, TRUE, sig_die, service);
+       lib_signals_set_handler(SIGTERM, TRUE, sig_die, service);
+
+       if ((service->flags & MASTER_SERVICE_FLAG_STANDALONE) == 0) {
+               if (fstat(MASTER_STATUS_FD, &st) < 0 || !S_ISFIFO(st.st_mode))
+                       i_fatal("Must be started by dovecot master process");
+
+               /* initialize master_status structure */
+               value = getenv(MASTER_UID_ENV);
+               if (value == NULL)
+                       i_fatal(MASTER_UID_ENV" not set");
+               service->master_status.pid = getpid();
+               service->master_status.uid =
+                       (unsigned int)strtoul(value, NULL, 10);
+
+               /* set the default limit */
+               value = getenv(MASTER_CLIENT_LIMIT_ENV);
+               count = value == NULL ? 0 :
+                       (unsigned int)strtoul(value, NULL, 10);
+               if (count == 0)
+                       i_fatal(MASTER_CLIENT_LIMIT_ENV" not set");
+               master_service_set_client_limit(service, count);
+
+               /* start listening errors for status fd, it means master died */
+               service->io_status_error = io_add(MASTER_STATUS_FD, IO_ERROR,
+                                                 master_status_error, service);
+       } else {
+               master_service_set_client_limit(service, 1);
+               master_service_set_service_count(service, 1);
+       }
+
+       io_listeners_add(service);
+
+       if ((service->flags & MASTER_SERVICE_FLAG_STD_CLIENT) != 0) {
+               /* we already have a connection to be served */
+               service->master_status.available_count--;
+       }
+       master_status_update(service);
+}
+
 void master_service_env_clean(bool preserve_home)
 {
        const char *user, *tz, *home;
@@ -189,6 +275,47 @@ void master_service_env_clean(bool preserve_home)
        if (home != NULL) env_put(home);
 }
 
+void master_service_set_client_limit(struct master_service *service,
+                                    unsigned int client_limit)
+{
+       i_assert(service->master_status.available_count ==
+                service->total_available_count);
+
+       service->total_available_count = client_limit;
+       service->master_status.available_count = client_limit;
+}
+
+unsigned int master_service_get_client_limit(struct master_service *service)
+{
+       return service->total_available_count;
+}
+
+void master_service_set_service_count(struct master_service *service,
+                                     unsigned int count)
+{
+       unsigned int used;
+
+       used = service->total_available_count -
+               service->master_status.available_count;
+       i_assert(count >= used);
+
+       if (service->total_available_count > count) {
+               service->total_available_count = count;
+               service->master_status.available_count = count - used;
+       }
+       service->service_count_left = count;
+}
+
+unsigned int master_service_get_service_count(struct master_service *service)
+{
+       return service->service_count_left;
+}
+
+unsigned int master_service_get_socket_count(struct master_service *service)
+{
+       return service->socket_count;
+}
+
 const char *master_service_get_config_path(struct master_service *service)
 {
        return service->config_path;
@@ -199,9 +326,12 @@ const char *master_service_get_version_string(struct master_service *service)
        return service->version_string;
 }
 
-void master_service_run(struct master_service *service)
+void master_service_run(struct master_service *service,
+                       master_service_connection_callback_t *callback)
 {
+       service->callback = callback;
        io_loop_run(service->ioloop);
+       service->callback = NULL;
 }
 
 void master_service_stop(struct master_service *service)
@@ -209,11 +339,54 @@ void master_service_stop(struct master_service *service)
         io_loop_stop(service->ioloop);
 }
 
+void master_service_client_connection_destroyed(struct master_service *service)
+{
+       if (service->listeners == NULL) {
+               /* we can listen again */
+               io_listeners_add(service);
+       }
+
+       i_assert(service->total_available_count > 0);
+
+       if (service->service_count_left != service->total_available_count) {
+               i_assert(service->service_count_left == (unsigned int)-1);
+               service->master_status.available_count++;
+       } else {
+               /* we have only limited amount of service requests left */
+               i_assert(service->service_count_left > 0);
+                service->service_count_left--;
+               service->total_available_count--;
+
+               if (service->service_count_left == 0) {
+                       i_assert(service->master_status.available_count ==
+                                service->total_available_count);
+                       master_service_stop(service);
+               }
+       }
+       master_status_update(service);
+
+       if (service->io_status_error == NULL &&
+           service->master_status.available_count ==
+           service->total_available_count) {
+               /* master has closed the connection and we have nothing else
+                  to do anymore. */
+               master_service_stop(service);
+       }
+}
+
 void master_service_deinit(struct master_service **_service)
 {
        struct master_service *service = *_service;
 
        *_service = NULL;
+
+       io_listeners_remove(service);
+
+       if (service->io_status_error != NULL)
+               io_remove(&service->io_status_error);
+       if (service->io_status_write != NULL)
+               io_remove(&service->io_status_write);
+
        lib_signals_deinit();
        io_loop_destroy(&service->ioloop);
 
@@ -222,3 +395,115 @@ void master_service_deinit(struct master_service **_service)
 
        lib_deinit();
 }
+
+static void master_service_listen(struct master_service_listener *l)
+{
+       struct master_service_connection conn;
+
+       if (l->service->master_status.available_count == 0) {
+               /* we are full. stop listening for now. */
+               io_listeners_remove(l->service);
+               return;
+       }
+
+       memset(&conn, 0, sizeof(conn));
+       conn.listen_fd = l->fd;
+       conn.fd = net_accept(l->fd, &conn.remote_ip, &conn.remote_port);
+       if (conn.fd < 0) {
+               if (conn.fd == -1)
+                       return;
+
+               if (errno != ENOTSOCK) {
+                       i_error("net_accept() failed: %m");
+                       io_listeners_remove(l->service);
+                       return;
+               }
+               /* it's not a socket. probably a fifo. use the "listener"
+                  as the connection fd */
+               io_remove(&l->io);
+               conn.fd = l->fd;
+       }
+
+       l->service->master_status.available_count--;
+        master_status_update(l->service);
+
+        l->service->callback(&conn);
+}
+
+static void io_listeners_add(struct master_service *service)
+{
+       unsigned int i;
+
+       if (service->socket_count == 0)
+               return;
+
+       service->listeners =
+               i_new(struct master_service_listener, service->socket_count);
+
+       for (i = 0; i < service->socket_count; i++) {
+               struct master_service_listener *l = &service->listeners[i];
+
+               l->service = service;
+               l->fd = MASTER_LISTEN_FD_FIRST + i;
+               l->io = io_add(MASTER_LISTEN_FD_FIRST + i, IO_READ,
+                              master_service_listen, l);
+       }
+}
+
+static void io_listeners_remove(struct master_service *service)
+{
+       unsigned int i;
+
+       if (service->listeners != NULL) {
+               for (i = 0; i < service->socket_count; i++) {
+                       if (service->listeners[i].io != NULL)
+                               io_remove(&service->listeners[i].io);
+               }
+               i_free_and_null(service->listeners);
+       }
+}
+
+static bool master_status_update_is_important(struct master_service *service)
+{
+       if (service->master_status.available_count == 0)
+               return TRUE;
+       if (!service->initial_status_sent)
+               return TRUE;
+       return FALSE;
+}
+
+static void master_status_update(struct master_service *service)
+{
+       ssize_t ret;
+
+       if (service->master_status.pid == 0)
+               return; /* closed */
+
+       ret = write(MASTER_STATUS_FD, &service->master_status,
+                   sizeof(service->master_status));
+       if (ret > 0) {
+               /* success */
+               if (service->io_status_write != NULL) {
+                       /* delayed important update sent successfully */
+                       io_remove(&service->io_status_write);
+               }
+               service->initial_status_sent = TRUE;
+       } else if (ret == 0) {
+               /* shouldn't happen? */
+               i_error("write(master_status_fd) returned 0");
+               service->master_status.pid = 0;
+       } else if (errno != EAGAIN) {
+               /* failure */
+               if (errno != EPIPE)
+                       i_error("write(master_status_fd) failed: %m");
+               service->master_status.pid = 0;
+       } else if (master_status_update_is_important(service)) {
+               /* reader is busy, but it's important to get this notification
+                  through. send it when possible. */
+               if (service->io_status_write == NULL) {
+                       service->io_status_write =
+                               io_add(MASTER_STATUS_FD, IO_WRITE,
+                                      master_status_update, service);
+               }
+       }
+}
index d9326d9f37e78606765699f6b362e1a73610633a..130283d8fa43083f58eb9147abbee74d63c0f979 100644 (file)
@@ -12,6 +12,17 @@ enum master_service_flags {
        MASTER_SERVICE_FLAG_LOG_TO_STDERR       = 0x04
 };
 
+struct master_service_connection {
+       int fd;
+       int listen_fd;
+
+       struct ip_addr remote_ip;
+       unsigned int remote_port;
+};
+
+typedef void
+master_service_connection_callback_t(const struct master_service_connection *conn);
+
 struct master_service;
 
 const char *master_service_getopt_string(void);
@@ -23,13 +34,32 @@ master_service_init(const char *name, enum master_service_flags flags,
 /* Parser command line option. Returns TRUE if processed. */
 bool master_service_parse_option(struct master_service *service,
                                 int opt, const char *arg);
+/* Finish service initialization. The caller should drop privileges
+   before calling this. */
+void master_service_init_finish(struct master_service *service);
 
 /* Clean environment from everything except TZ, USER and optionally HOME. */
 void master_service_env_clean(bool preserve_home);
 
 /* Initialize logging. */
-void master_service_init_log(struct master_service *service,
-                            const char *prefix);
+void master_service_init_log(struct master_service *service, const char *prefix,
+                            unsigned int max_lines_per_sec);
+
+/* Set maximum number of clients we can handle. Default is given by master. */
+void master_service_set_client_limit(struct master_service *service,
+                                    unsigned int client_limit);
+/* Returns the maximum number of clients we can handle. */
+unsigned int master_service_get_client_limit(struct master_service *service);
+
+/* Set maximum number of client connections we will handle before shutting
+   down. */
+void master_service_set_service_count(struct master_service *service,
+                                     unsigned int count);
+/* Returns the number of client connections we will handle before shutting
+   down. The value is decreased only after connection has been closed. */
+unsigned int master_service_get_service_count(struct master_service *service);
+/* Return the number of listener sockets. */
+unsigned int master_service_get_socket_count(struct master_service *service);
 
 /* Returns configuration file path. */
 const char *master_service_get_config_path(struct master_service *service);
@@ -38,10 +68,14 @@ const char *master_service_get_config_path(struct master_service *service);
 const char *master_service_get_version_string(struct master_service *service);
 
 /* Start the service. Blocks until finished */
-void master_service_run(struct master_service *service);
+void master_service_run(struct master_service *service,
+                       master_service_connection_callback_t *callback);
 /* Stop a running service. */
 void master_service_stop(struct master_service *service);
 
+/* Call whenever a client connection is destroyed. */
+void master_service_client_connection_destroyed(struct master_service *service);
+
 /* Deinitialize the service. */
 void master_service_deinit(struct master_service **service);
 
index 5aff6e1fbb207802025afdf69db4456fdfe20568..fd1754ba69b5de460db677d051c92c20473a680e 100644 (file)
@@ -635,7 +635,7 @@ int settings_parse_exec(struct setting_parser_context *ctx,
                static const char *argv[] = {
                        NULL,
                        "-c", NULL,
-                       "-s", NULL,
+                       "-p", NULL,
                        NULL
                };
                argv[0] = bin_path;
index fc4279648ac612af0424a65936661e881afb736e..d3dd663c8eb0af03103331831b18f761cad4d497 100644 (file)
@@ -108,7 +108,7 @@ int settings_parse_line(struct setting_parser_context *ctx, const char *line);
 int settings_parse_stream(struct setting_parser_context *ctx,
                          struct istream *input);
 /* Read data from input stream and parser it. returns -1 = error,
-   0 = eof/stream error, 1 = not finished yet (stream is non-blocking) */
+   0 = done, 1 = not finished yet (stream is non-blocking) */
 int settings_parse_stream_read(struct setting_parser_context *ctx,
                               struct istream *input);
 /* Open file and parse it. */
index db0b9b8b9c7d1ec79f4289cfde13cb4370decb6a..e3f7829a11a615e2c2864a5965e86c15a084b816 100644 (file)
@@ -448,7 +448,8 @@ mail_storage_service_init_log(struct master_service *service,
                str = t_str_new(256);
                var_expand(str, user_set->mail_log_prefix,
                           get_var_expand_table(service, input));
-               master_service_init_log(service, str_c(str));
+               master_service_init_log(service, str_c(str),
+                                       user_set->mail_log_max_lines_per_sec);
        } T_END;
 }
 
@@ -474,9 +475,7 @@ mail_storage_service_init_user(struct master_service *service,
        if ((flags & MAIL_STORAGE_SERVICE_FLAG_DEBUG) != 0)
                set_keyval(service->set_parser, "mail_debug", "yes");
 
-       /* now that we've read settings, we can set up logging */
        mail_storage_service_init_log(service, &input);
-
        set = master_service_settings_get(service);
        sets = master_service_settings_get_others(service);
        user_set = sets[0];
@@ -563,16 +562,16 @@ mail_storage_service_multi_init(struct master_service *service,
 
        mail_storage_service_init_settings(service, set_roots, FALSE);
 
-       /* do all the global initialization. delay initializing plugins until
-          we drop privileges the first time. */
-       master_service_init_log(service,
-                               t_strdup_printf("%s: ", service->name));
-
        set = master_service_settings_get(service);
        sets = master_service_settings_get_others(service);
        user_set = sets[0];
        mail_set = mail_user_set_get_storage_set(user_set);
 
+       /* do all the global initialization. delay initializing plugins until
+          we drop privileges the first time. */
+       master_service_init_log(service, t_strconcat(service->name, ": ", NULL),
+                               user_set->mail_log_max_lines_per_sec);
+
        modules = *user_set->mail_plugins == '\0' ? NULL :
                module_dir_load(user_set->mail_plugin_dir,
                                user_set->mail_plugins, TRUE,
index e0eece37a584f01602bc482b2c6d4bc854842e2d..95a1231060dc6c55f87d3a264a5978cd128e4c65 100644 (file)
@@ -145,6 +145,7 @@ static struct setting_define mail_user_setting_defines[] = {
        DEF(SET_UINT, last_valid_uid),
        DEF(SET_UINT, first_valid_gid),
        DEF(SET_UINT, last_valid_gid),
+       DEF(SET_UINT, mail_log_max_lines_per_sec),
 
        DEF(SET_STR, mail_plugins),
        DEF(SET_STR, mail_plugin_dir),
@@ -174,6 +175,8 @@ static struct mail_user_settings mail_user_default_settings = {
        MEMBER(first_valid_gid) 1,
        MEMBER(last_valid_gid) 0,
 
+       MEMBER(mail_log_max_lines_per_sec) 10,
+
        MEMBER(mail_plugins) "",
        MEMBER(mail_plugin_dir) MODULEDIR,
 
index dacea133915701108ea79d444a0b7feecad3a993..62135bf7952bc8e6c256b77234f79d3a583caba8 100644 (file)
@@ -54,6 +54,8 @@ struct mail_user_settings {
        unsigned int first_valid_uid, last_valid_uid;
        unsigned int first_valid_gid, last_valid_gid;
 
+       unsigned int mail_log_max_lines_per_sec;
+
        const char *mail_plugins;
        const char *mail_plugin_dir;
 
index 776bbdfc952452e1b9b8fcf6bc02afc1dc1953ff..d73491d799332e945ba3138d64aec730956c671d 100644 (file)
@@ -3,6 +3,7 @@
 #include "lib.h"
 #include "ioloop.h"
 #include "str.h"
+#include "hostpid.h"
 #include "network.h"
 #include "backtrace-string.h"
 #include "printf-format-fix.h"
 #include <syslog.h>
 #include <time.h>
 
-const char *failure_log_type_prefixes[] = {
+const char *failure_log_type_prefixes[LOG_TYPE_COUNT] = {
        "Info: ",
        "Warning: ",
        "Error: ",
        "Fatal: ",
-       "Panic: "
-};
-static char log_type_internal_chars[] = {
-       'I', 'W', 'E', 'F', 'P'
+       "Panic: ",
+       "Error: "
 };
 
 /* Initialize working defaults */
@@ -37,6 +36,9 @@ static int log_fd = STDERR_FILENO, log_info_fd = STDERR_FILENO;
 static char *log_prefix = NULL, *log_stamp_format = NULL;
 static bool failure_ignore_errors = FALSE;
 
+static void ATTR_FORMAT(2, 0)
+i_internal_error_handler(enum log_type type, const char *fmt, va_list args);
+
 /* kludgy .. we want to trust log_stamp_format with -Wformat-nonliteral */
 static const char *get_log_stamp_format(const char *unused)
        ATTR_FORMAT_ARG(1);
@@ -59,9 +61,6 @@ static void log_prefix_add(string_t *str)
        char buf[256];
        time_t now;
 
-       if (log_prefix != NULL)
-               str_append(str, log_prefix);
-
        if (log_stamp_format != NULL) {
                now = time(NULL);
                tm = localtime(&now);
@@ -70,6 +69,8 @@ static void log_prefix_add(string_t *str)
                             get_log_stamp_format("unused"), tm) > 0)
                        str_append(str, buf);
        }
+       if (log_prefix != NULL)
+               str_append(str, log_prefix);
 }
 
 static void log_fd_flush_stop(struct ioloop *ioloop)
@@ -193,6 +194,23 @@ void i_log_type(enum log_type type, const char *format, ...)
        va_list args;
 
        va_start(args, format);
+
+       if (type == LOG_TYPE_ERROR_IGNORE_IF_SEEN_FATAL &&
+           error_handler != i_internal_error_handler) {
+               /* this is handled specially only by internal logging.
+                  skip over the pid. */
+               T_BEGIN {
+                       const char *str;
+
+                       str = t_strdup_vprintf(format, args);
+                       while (*str >= '0' && *str <= '9') str++;
+                       if (*str == ' ') str++;
+
+                       i_error("%s", str);
+               } T_END;
+               return;
+       }
+
        if (type == LOG_TYPE_INFO)
                info_handler(type, format, args);
        else
@@ -328,12 +346,16 @@ void i_syslog_error_handler(enum log_type type, const char *fmt, va_list args)
                level = LOG_WARNING;
                break;
        case LOG_TYPE_ERROR:
+       case LOG_TYPE_ERROR_IGNORE_IF_SEEN_FATAL:
                level = LOG_ERR;
                break;
        case LOG_TYPE_FATAL:
        case LOG_TYPE_PANIC:
                level = LOG_CRIT;
                break;
+       case LOG_TYPE_COUNT:
+       case LOG_TYPE_OPTION:
+               i_unreached();
        }
 
        if (syslog_handler(level, type, fmt, args) < 0)
@@ -399,8 +421,28 @@ void i_set_failure_prefix(const char *prefix)
        log_prefix = i_strdup(prefix);
 }
 
+static int internal_send_split(string_t *full_str, unsigned int prefix_len)
+{
+       string_t *str;
+       unsigned int max_text_len, pos = prefix_len;
+
+       str = t_str_new(PIPE_BUF);
+       str_append_n(str, str_c(full_str), prefix_len);
+       max_text_len = PIPE_BUF - prefix_len - 1;
+
+       while (pos < str_len(full_str)) {
+               str_truncate(str, prefix_len);
+               str_append_n(str, str_c(full_str) + pos, max_text_len);
+               str_append_c(str, '\n');
+               if (log_fd_write(2, str_data(str), str_len(str)) < 0)
+                       return -1;
+               pos += max_text_len;
+       }
+       return 0;
+}
+
 static int ATTR_FORMAT(2, 0)
-internal_handler(char log_type, const char *format, va_list args)
+internal_handler(enum log_type log_type, const char *format, va_list args)
 {
        static int recursed = 0;
        int ret;
@@ -414,13 +456,18 @@ internal_handler(char log_type, const char *format, va_list args)
        recursed++;
        T_BEGIN {
                string_t *str;
+               unsigned int prefix_len;
 
                str = t_str_new(512);
-               str_append_c(str, 1);
-               str_append_c(str, log_type);
+               str_printfa(str, "\001%c%s ", log_type + 1, my_pid);
+               prefix_len = str_len(str);
+
                str_vprintfa(str, format, args);
                str_append_c(str, '\n');
-               ret = write_full(2, str_data(str), str_len(str));
+               if (str_len(str) <= PIPE_BUF)
+                       ret = log_fd_write(2, str_data(str), str_len(str));
+               else
+                       ret = internal_send_split(str, prefix_len);
        } T_END;
 
        if (ret < 0 && failure_ignore_errors)
@@ -430,21 +477,62 @@ internal_handler(char log_type, const char *format, va_list args)
        return ret;
 }
 
+static bool line_is_ok(const char *line)
+{
+       if (*line != 1)
+               return FALSE;
+
+       if (line[1] == '\0') {
+               i_warning("Broken log line follows (type=NUL)");
+               return FALSE;
+       }
+
+       if (line[1]-1 > LOG_TYPE_OPTION) {
+               i_warning("Broken log line follows (type=%d)", line[1]-1);
+               return FALSE;
+       }
+       return TRUE;
+}
+
+void i_failure_parse_line(const char *line, struct failure_line *failure)
+{
+       memset(failure, 0, sizeof(*failure));
+       if (!line_is_ok(line)) {
+               failure->log_type = LOG_TYPE_ERROR;
+               failure->text = line;
+               return;
+       }
+       failure->log_type = line[1] - 1;
+
+       line += 2;
+       failure->text = line;
+       while (*line >= '0' && *line <= '9') {
+               failure->pid = failure->pid*10 + (*line - '0');
+               line++;
+       }
+       if (*line != ' ') {
+               /* some old protocol? */
+               failure->pid = 0;
+               return;
+       }
+       failure->text = line + 1;
+}
+
 static void ATTR_NORETURN ATTR_FORMAT(3, 0)
 i_internal_fatal_handler(enum log_type type, int status,
                         const char *fmt, va_list args)
 {
-       if (internal_handler(log_type_internal_chars[type], fmt, args) < 0 &&
+       if (internal_handler(type, fmt, args) < 0 &&
            status == FATAL_DEFAULT)
                status = FATAL_LOGERROR;
 
        default_fatal_finish(type, status);
 }
 
-static void ATTR_FORMAT(2, 0)
+static void
 i_internal_error_handler(enum log_type type, const char *fmt, va_list args)
 {
-       if (internal_handler(log_type_internal_chars[type], fmt, args) < 0)
+       if (internal_handler(type, fmt, args) < 0)
                failure_exit(FATAL_LOGERROR);
 }
 
@@ -480,7 +568,8 @@ void i_set_failure_ip(const struct ip_addr *ip)
        const char *str;
 
        if (error_handler == i_internal_error_handler) {
-               str = t_strdup_printf("\x01Oip=%s\n", net_ip2addr(ip));
+               str = t_strdup_printf("\001%c%s ip=%s\n",
+                                     LOG_TYPE_OPTION, my_pid, net_ip2addr(ip));
                (void)write_full(2, str, strlen(str));
        }
 }
index 1c7c3a3235829a74e9f89e5677013884bb0ba120..ebef298492acec44f266b8235c96aa110e902e28 100644 (file)
@@ -19,7 +19,21 @@ enum log_type {
        LOG_TYPE_WARNING,
        LOG_TYPE_ERROR,
        LOG_TYPE_FATAL,
-       LOG_TYPE_PANIC
+       LOG_TYPE_PANIC,
+
+       /* Special message from master to log process: Log message begins with
+          "<pid> " and if <pid> has already logged a fatal/panic, this message
+          shouldn't be written to the log. Otherwise log as an error. */
+       LOG_TYPE_ERROR_IGNORE_IF_SEEN_FATAL,
+
+       LOG_TYPE_COUNT,
+       LOG_TYPE_OPTION
+};
+
+struct failure_line {
+       pid_t pid;
+       enum log_type log_type;
+       const char *text;
 };
 
 #define DEFAULT_FAILURE_STAMP_FORMAT "%b %d %H:%M:%S "
@@ -95,6 +109,9 @@ void i_set_failure_exit_callback(void (*callback)(int *status));
 /* Call the exit callback and exit() */
 void failure_exit(int status) ATTR_NORETURN;
 
+/* Parse a line logged using internal failure handler */
+void i_failure_parse_line(const char *line, struct failure_line *failure);
+
 void failures_deinit(void);
 
 #endif
index b7700666e6b8f8fbd30a019d507f18fd3380faaf..a8ae2897462bef5d22a1a1dc989b60da887413c6 100644 (file)
@@ -20,6 +20,7 @@
 static gid_t process_primary_gid = (gid_t)-1;
 static gid_t process_privileged_gid = (gid_t)-1;
 static bool process_using_priv_gid = FALSE;
+static char *chroot_dir = NULL;
 
 void restrict_access_init(struct restrict_access_settings *set)
 {
@@ -273,6 +274,7 @@ void restrict_access(const struct restrict_access_settings *set,
 
                if (chroot(set->chroot_dir) != 0)
                        i_fatal("chroot(%s) failed: %m", set->chroot_dir);
+               chroot_dir = i_strdup(set->chroot_dir);
 
                if (home != NULL) {
                        if (chdir(home) < 0) {
@@ -407,6 +409,11 @@ void restrict_access_by_env(const char *home, bool disallow_root)
        env_remove("RESTRICT_CHROOT");
 }
 
+const char *restrict_access_get_current_chroot(void)
+{
+       return chroot_dir;
+}
+
 void restrict_access_allow_coredumps(bool allow ATTR_UNUSED)
 {
 #ifdef HAVE_PR_SET_DUMPABLE
index 7d1ab346e07f888ceb12a0200bfce60f27652ba9..a80560335e10340013828712a21adbd2ffcf612a 100644 (file)
@@ -37,6 +37,10 @@ void restrict_access_set_env(const struct restrict_access_settings *set);
    environment settings. */
 void restrict_access_by_env(const char *home, bool disallow_root);
 
+/* Return the chrooted directory if restrict_access*() chrooted,
+   otherwise NULL. */
+const char *restrict_access_get_current_chroot(void);
+
 /* Try to set up the process in a way that core dumps are still allowed
    after calling restrict_access_by_env(). */
 void restrict_access_allow_coredumps(bool allow);
index 4cb5b6c9419b677ce5c24911422a7442f6a0f146..7116b691e774791f8bec46633a94ef32df2a9084 100644 (file)
@@ -7,6 +7,7 @@
 #include "istream.h"
 #include "ostream.h"
 #include "hostpid.h"
+#include "master-service.h"
 #include "master-service-settings.h"
 #include "mail-namespace.h"
 #include "mail-storage.h"
@@ -195,7 +196,7 @@ void client_destroy(struct client *client, const char *prefix,
        pool_unref(&client->state_pool);
        i_free(client);
 
-       listener_client_destroyed();
+       master_service_client_connection_destroyed(service);
 }
 
 static const char *client_get_disconnect_reason(struct client *client)
index afe005e4cc8375fabb5a0fa1423feb527769dfb7..9e8ded8cd336c545be5ba7d8d160db933d3067df 100644 (file)
 #define IS_STANDALONE() \
         (getenv("MASTER_SERVICE") == NULL)
 
-struct lmtp_listener {
-       int fd;
-       struct io *io;
-};
-
 struct master_service *service;
 struct mail_storage_service_multi_ctx *multi_service;
 
-static struct io *log_io = NULL;
-static ARRAY_DEFINE(listeners, struct lmtp_listener *);
-
-static void log_error_callback(void *context ATTR_UNUSED)
-{
-       /* the log fd is closed, don't die when trying to log later */
-       i_set_failure_ignore_errors(TRUE);
-
-       master_service_stop(service);
-}
-
-static void listen_connected(struct lmtp_listener *l)
+static void client_connected(const struct master_service_connection *conn)
 {
        struct client *client;
        struct ip_addr remote_ip;
        unsigned int remote_port;
        int fd;
 
-       fd = net_accept(l->fd, &remote_ip, &remote_port);
+       fd = net_accept(conn->fd, &remote_ip, &remote_port);
        if (fd < 0) {
                if (fd < -1)
                        i_error("accept() failed: %m");
@@ -59,80 +43,15 @@ static void listen_connected(struct lmtp_listener *l)
        (void)net_getsockname(fd, &client->local_ip, &client->local_port);
 }
 
-static void listen_start(void)
-{
-       struct lmtp_listener *const *l;
-       unsigned int i, count;
-
-       l = array_get(&listeners, &count);
-       for (i = 0; i < count; i++) {
-               i_assert(l[i]->io == NULL);
-               l[i]->io = io_add(l[i]->fd, IO_READ, listen_connected, l[i]);
-       }
-}
-
-static void listen_stop(void)
-{
-       struct lmtp_listener *const *l;
-       unsigned int i, count;
-
-       l = array_get(&listeners, &count);
-       for (i = 0; i < count; i++) {
-               i_assert(l[i]->io != NULL);
-               io_remove(&l[i]->io);
-       }
-}
-
-static void listen_free(void)
-{
-       struct lmtp_listener **l;
-       unsigned int i, count;
-
-       l = array_get_modifiable(&listeners, &count);
-       for (i = 0; i < count; i++) {
-               if (l[i]->io != NULL)
-                       io_remove(&l[i]->io);
-               i_free(l[i]);
-       }
-       array_free(&listeners);
-}
-
-void listener_client_destroyed(void)
-{
-       if (array_count(&listeners) == 0)
-               master_service_stop(service);
-}
-
 static void main_init(void)
 {
-       struct lmtp_listener *l;
-       const char *value;
-       unsigned int i, count;
-
-       /* If master dies, the log fd gets closed and we'll quit */
-       log_io = io_add(STDERR_FILENO, IO_ERROR, log_error_callback, NULL);
-
-       value = getenv("LISTEN_FDS");
-       count = value == NULL ? 0 : atoi(value);
-       i_array_init(&listeners, count + 1);
-       for (i = 0; i < count; i++) {
-               l = i_new(struct lmtp_listener, 1);
-               l->fd = LMTP_MASTER_FIRST_LISTEN_FD + i;
-               array_append(&listeners, &l, 1);
-       }
-
-       if (count == 0)
+       if (IS_STANDALONE())
                (void)client_create(STDIN_FILENO, STDOUT_FILENO);
-       else
-               listen_start();
 }
 
 static void main_deinit(void)
 {
-       if (log_io != NULL)
-               io_remove(&log_io);
        clients_destroy();
-       listen_free();
 }
 
 int main(int argc, char *argv[], char *envp[])
@@ -157,13 +76,15 @@ int main(int argc, char *argv[], char *envp[])
        }
 #endif
 
-       if (IS_STANDALONE())
-               service_flags |= MASTER_SERVICE_FLAG_STANDALONE;
+       if (IS_STANDALONE()) {
+               service_flags |= MASTER_SERVICE_FLAG_STANDALONE |
+                       MASTER_SERVICE_FLAG_STD_CLIENT;
+       }
 
        service = master_service_init("lmtp", service_flags, argc, argv);
        while ((c = getopt(argc, argv, master_service_getopt_string())) > 0) {
                if (!master_service_parse_option(service, c, optarg))
-                       i_fatal("Unknown argument: %c", c);
+                       exit(FATAL_DEFAULT);
        }
 
        multi_service = mail_storage_service_multi_init(service, set_roots,
@@ -173,7 +94,7 @@ int main(int argc, char *argv[], char *envp[])
         process_title_init(argv, envp);
 
        main_init();
-       master_service_run(service);
+       master_service_run(service, client_connected);
 
        main_deinit();
        mail_storage_service_multi_deinit(&multi_service);
diff --git a/src/log/Makefile.am b/src/log/Makefile.am
new file mode 100644 (file)
index 0000000..6971437
--- /dev/null
@@ -0,0 +1,19 @@
+pkglibexecdir = $(libexecdir)/dovecot
+
+pkglibexec_PROGRAMS = log
+
+AM_CPPFLAGS = \
+       -I$(top_srcdir)/src/lib \
+       -I$(top_srcdir)/src/lib-settings \
+       -I$(top_srcdir)/src/lib-master
+
+log_LDADD = $(LIBDOVECOT)
+log_DEPENDENCIES = $(LIBDOVECOT)
+
+log_SOURCES = \
+       log-connection.c \
+       main.c
+
+noinst_HEADERS = \
+       common.h \
+       log-connection.h
diff --git a/src/log/common.h b/src/log/common.h
new file mode 100644 (file)
index 0000000..a5fa4f2
--- /dev/null
@@ -0,0 +1,9 @@
+#ifndef COMMON_H
+#define COMMON_H
+
+#include "lib.h"
+
+extern struct master_service *service;
+extern pid_t master_pid;
+
+#endif
diff --git a/src/log/log-connection.c b/src/log/log-connection.c
new file mode 100644 (file)
index 0000000..fb9beb9
--- /dev/null
@@ -0,0 +1,230 @@
+/* Copyright (c) 2005-2009 Dovecot authors, see the included COPYING file */
+
+#include "common.h"
+#include "ioloop.h"
+#include "llist.h"
+#include "hash.h"
+#include "master-interface.h"
+#include "master-service.h"
+#include "log-connection.h"
+
+#include <unistd.h>
+
+#define FATAL_QUEUE_TIMEOUT_MSECS 500
+
+struct log_client {
+       struct ip_addr ip;
+       unsigned int fatal_logged:1;
+};
+
+struct log_connection {
+       struct log_connection *prev, *next;
+
+       int fd;
+       struct io *io;
+
+       char *prefix;
+       struct hash_table *clients;
+
+       unsigned int handshaked:1;
+};
+
+static struct log_connection *log_connections = NULL;
+
+static struct log_client *log_client_get(struct log_connection *log, pid_t pid)
+{
+       struct log_client *client;
+
+       client = hash_table_lookup(log->clients, POINTER_CAST(pid));
+       if (client == NULL) {
+               client = i_new(struct log_client, 1);
+               hash_table_insert(log->clients, POINTER_CAST(pid), client);
+       }
+       return client;
+}
+
+static void log_parse_ip(struct log_connection *log,
+                        const struct failure_line *failure)
+{
+       struct log_client *client;
+
+       client = log_client_get(log, failure->pid);
+       (void)net_addr2ip(failure->text + 3, &client->ip);
+}
+
+static void log_remove_pid(struct log_connection *log, pid_t pid)
+{
+       struct log_client *client;
+
+       client = hash_table_lookup(log->clients, POINTER_CAST(pid));
+       if (client != NULL) {
+               hash_table_remove(log->clients, POINTER_CAST(pid));
+               i_free(client);
+       }
+}
+
+static void log_parse_option(struct log_connection *log,
+                            const struct failure_line *failure)
+{
+       if (strncmp(failure->text, "ip=", 3) == 0)
+               log_parse_ip(log, failure);
+       else if (strcmp(failure->text, "bye") == 0)
+               log_remove_pid(log, failure->pid);
+}
+
+static bool
+log_handle_seen_fatal(struct log_connection *log, const char **_text)
+{
+       const char *text = *_text;
+       struct log_client *client;
+       pid_t pid = 0;
+
+       while (*text >= '0' && *text <= '9') {
+               pid = pid*10 + (*text - '0');
+               text++;
+       }
+       if (*text != ' ' || pid == 0)
+               return FALSE;
+       *_text = text;
+
+       client = hash_table_lookup(log->clients, POINTER_CAST(pid));
+       if (client != NULL && client->fatal_logged) {
+               log_remove_pid(log, pid);
+               return TRUE;
+       }
+       return FALSE;
+}
+
+static void log_it(struct log_connection *log, const char *line)
+{
+       struct failure_line failure;
+       struct log_client *client;
+
+       i_failure_parse_line(line, &failure);
+       switch (failure.log_type) {
+       case LOG_TYPE_FATAL:
+       case LOG_TYPE_PANIC:
+               client = log_client_get(log, failure.pid);
+               client->fatal_logged = TRUE;
+               break;
+       case LOG_TYPE_ERROR_IGNORE_IF_SEEN_FATAL:
+               /* Special case for master connection. If the following PID
+                  has logged a fatal/panic, don't log this message. */
+               failure.log_type = LOG_TYPE_ERROR;
+               if (failure.pid != master_pid) {
+                       i_error("Non-master process %s "
+                               "sent LOG_TYPE_ERROR_IGNORE_IF_SEEN_FATAL",
+                               dec2str(failure.pid));
+                       break;
+               }
+
+               if (log_handle_seen_fatal(log, &failure.text))
+                       return;
+               break;
+       case LOG_TYPE_OPTION:
+               log_parse_option(log, &failure);
+               return;
+       default:
+               break;
+       }
+       i_assert(failure.log_type < LOG_TYPE_COUNT);
+
+       i_set_failure_prefix(log->prefix);
+       i_log_type(failure.log_type, "%s", failure.text);
+       i_set_failure_prefix("log: ");
+}
+
+static bool log_connection_handshake(struct log_connection *log,
+                                    char **data, size_t size)
+{
+       struct log_service_handshake handshake;
+
+       if (size < sizeof(handshake))
+               return FALSE;
+
+       memcpy(&handshake, *data, sizeof(handshake));
+       if (handshake.log_magic != MASTER_LOG_MAGIC)
+               return FALSE;
+
+       if (handshake.prefix_len <= size - sizeof(handshake)) {
+               log->prefix = i_strndup(*data + sizeof(handshake),
+                                       handshake.prefix_len);
+               *data += sizeof(handshake) + handshake.prefix_len;
+       }
+       log->handshaked = TRUE;
+       return TRUE;
+}
+
+static void log_connection_input(struct log_connection *log)
+{
+       char data[PIPE_BUF+1], *p, *line;
+       ssize_t ret;
+
+       ret = read(log->fd, data, sizeof(data)-1);
+       if (ret <= 0) {
+               if (ret < 0)
+                       i_error("read(log pipe) failed: %m");
+               log_connection_destroy(log);
+               return;
+       }
+       data[ret] = '\0';
+
+       line = data;
+       if (!log->handshaked)
+               log_connection_handshake(log, &line, ret);
+
+       p = line;
+       while ((p = strchr(line, '\n')) != NULL) {
+               *p = '\0';
+               log_it(log, line);
+               line = p + 1;
+       }
+       if (line - data != ret) {
+               i_error("Invalid log line follows: Missing LF");
+               log_it(log, line);
+       }
+}
+
+struct log_connection *log_connection_create(int fd)
+{
+       struct log_connection *log;
+
+       log = i_new(struct log_connection, 1);
+       log->fd = fd;
+       log->io = io_add(fd, IO_READ, log_connection_input, log);
+       log->clients = hash_table_create(default_pool, default_pool, 0,
+                                        NULL, NULL);
+
+       DLLIST_PREPEND(&log_connections, log);
+       log_connection_input(log);
+       return log;
+}
+
+void log_connection_destroy(struct log_connection *log)
+{
+       struct hash_iterate_context *iter;
+       void *key, *value;
+
+       DLLIST_REMOVE(&log_connections, log);
+
+       iter = hash_table_iterate_init(log->clients);
+       while (hash_table_iterate(iter, &key, &value))
+               i_free(value);
+       hash_table_iterate_deinit(&iter);
+       hash_table_destroy(&log->clients);
+
+       if (log->io != NULL)
+               io_remove(&log->io);
+       i_free(log->prefix);
+       i_free(log);
+
+        master_service_client_connection_destroyed(service);
+}
+
+void log_connections_deinit(void)
+{
+       /* normally we don't exit until all log connections are gone,
+          but we could get here when we're being killed by a signal */
+       while (log_connections != NULL)
+               log_connection_destroy(log_connections);
+}
diff --git a/src/log/log-connection.h b/src/log/log-connection.h
new file mode 100644 (file)
index 0000000..819bb20
--- /dev/null
@@ -0,0 +1,9 @@
+#ifndef LOG_CONNECTION_H
+#define LOG_CONNECTION_H
+
+struct log_connection *log_connection_create(int fd);
+void log_connection_destroy(struct log_connection *log);
+
+void log_connections_deinit(void);
+
+#endif
diff --git a/src/log/main.c b/src/log/main.c
new file mode 100644 (file)
index 0000000..7434382
--- /dev/null
@@ -0,0 +1,73 @@
+/* Copyright (c) 2005-2009 Dovecot authors, see the included COPYING file */
+
+#include "common.h"
+#include "lib-signals.h"
+#include "master-interface.h"
+#include "master-service.h"
+#include "master-service-settings.h"
+#include "log-connection.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+
+struct master_service *service;
+pid_t master_pid;
+
+static void
+sig_reread_config(const siginfo_t *si ATTR_UNUSED, void *context ATTR_UNUSED)
+{
+       // FIXME
+}
+
+static void
+sig_reopen_logs(const siginfo_t *si ATTR_UNUSED, void *context ATTR_UNUSED)
+{
+       // FIXME
+}
+
+static void main_init(void)
+{
+        lib_signals_set_handler(SIGHUP, TRUE, sig_reread_config, NULL);
+       lib_signals_set_handler(SIGUSR1, TRUE, sig_reopen_logs, NULL);
+
+       master_pid = getppid();
+}
+
+static void main_deinit(void)
+{
+       log_connections_deinit();
+}
+
+static void client_connected(const struct master_service_connection *conn)
+{
+       log_connection_create(conn->fd);
+}
+
+int main(int argc, char *argv[])
+{
+       const char *error;
+       int c;
+
+       service = master_service_init("log", 0, argc, argv);
+
+       /* use log prefix and log to stderr until we've configured the real
+          logging */
+       i_set_failure_file("/dev/stderr", "log: ");
+
+       while ((c = getopt(argc, argv, master_service_getopt_string())) > 0) {
+               if (!master_service_parse_option(service, c, optarg))
+                       exit(FATAL_DEFAULT);
+       }
+
+       if (master_service_settings_read(service, NULL, NULL, FALSE,
+                                        &error) < 0)
+               i_fatal("Error reading configuration: %s", error);
+
+       master_service_init_log(service, "log: ", 0);
+       master_service_init_finish(service);
+       main_init();
+       master_service_run(service, client_connected);
+       main_deinit();
+       master_service_deinit(&service);
+        return 0;
+}
index 2895df9d2f782bede02c25803487c0e404ebaaf2..80f655e5e124aebc42d28021928aa8f8fac16175 100644 (file)
@@ -4,6 +4,7 @@ AM_CPPFLAGS = \
        -I$(top_srcdir)/src/lib \
        -I$(top_srcdir)/src/lib-settings \
        -I$(top_srcdir)/src/lib-auth \
+       -I$(top_srcdir)/src/lib-master \
        -DPKG_RUNDIR=\""$(rundir)"\" \
        -DPKG_STATEDIR=\""$(statedir)"\" \
        -DSBINDIR=\""$(sbindir)"\" \
@@ -14,7 +15,6 @@ liblogin_la_SOURCES = \
        login-proxy.c \
        login-settings.c \
        main.c \
-       master.c \
        sasl-server.c \
        ssl-proxy.c \
        ssl-proxy-gnutls.c \
@@ -28,7 +28,6 @@ noinst_HEADERS = \
        login-proxy.h \
        login-settings.h \
        common.h \
-       master.h \
        sasl-server.h \
        ssl-proxy.h
 
index 90e08c60a4cf9e32d5eeed5ae7513f1eb2b472b6..02337c292607109934c48bc5e17cd76413baed74 100644 (file)
@@ -2,7 +2,6 @@
 #define CLIENT_COMMON_H
 
 #include "network.h"
-#include "master.h"
 #include "sasl-server.h"
 
 /* max. size of input buffer. this means:
@@ -29,7 +28,6 @@ struct client {
        struct auth_request *auth_request;
 
        unsigned int master_tag;
-       master_callback_t *master_callback;
        sasl_server_callback_t *sasl_callback;
 
        unsigned int auth_attempts;
@@ -39,6 +37,7 @@ struct client {
        unsigned int tls:1;
        unsigned int secured:1;
        unsigned int trusted:1;
+       unsigned int proxying:1;
        unsigned int authenticating:1;
        unsigned int auth_tried_disabled_plaintext:1;
        /* ... */
index 5c92dc9a6db2ac7b3dbaedb4c4487bae84e2ab0e..4e26e3e280373d4270a12b161611de9d5eb02781 100644 (file)
 #define AUTH_PLAINTEXT_DISABLED_MSG \
        "Plaintext authentication disallowed on non-secure (SSL/TLS) connections."
 
-extern const char *login_protocol;
+extern const char *login_protocol, *login_process_name;
 
 extern struct auth_client *auth_client;
 extern bool closing_down;
-extern unsigned int login_process_uid;
 
+extern struct master_service *service;
 extern struct login_settings *login_settings;
 
-void main_ref(void);
-void main_unref(void);
-
-void main_listen_start(void);
-void main_listen_stop(void);
-
-void connection_queue_add(unsigned int connection_count);
-
 #endif
index 56cf98f5a1ed4f7d82bdcfa758da487ac5036d65..9941ff24d4bd3f9a6597482242360c35b1af5a92 100644 (file)
@@ -6,6 +6,7 @@
 #include "ostream.h"
 #include "llist.h"
 #include "str-sanitize.h"
+#include "master-service.h"
 #include "client-common.h"
 #include "login-proxy.h"
 
@@ -31,7 +32,6 @@ struct login_proxy {
 };
 
 static struct login_proxy *login_proxies = NULL;
-static unsigned int login_proxy_count = 0;
 
 static void server_input(struct login_proxy *proxy)
 {
@@ -191,9 +191,7 @@ void login_proxy_free(struct login_proxy **_proxy)
 
        if (proxy->client_fd != -1) {
                /* detached proxy */
-               main_unref();
                DLLIST_REMOVE(&login_proxies, proxy);
-               login_proxy_count--;
 
                ipstr = net_ip2addr(&proxy->ip);
                i_info("proxy(%s): disconnecting %s",
@@ -224,7 +222,7 @@ void login_proxy_free(struct login_proxy **_proxy)
        i_free(proxy->user);
        i_free(proxy);
 
-       main_listen_start();
+       master_service_client_connection_destroyed(service);
 }
 
 bool login_proxy_is_ourself(const struct client *client, const char *host,
@@ -253,11 +251,6 @@ unsigned int login_proxy_get_port(const struct login_proxy *proxy)
        return proxy->port;
 }
 
-unsigned int login_proxy_get_count(void)
-{
-       return login_proxy_count;
-}
-
 void login_proxy_detach(struct login_proxy *proxy, struct istream *client_input,
                        struct ostream *client_output)
 {
@@ -291,9 +284,7 @@ void login_proxy_detach(struct login_proxy *proxy, struct istream *client_input,
        proxy->callback = NULL;
        proxy->context = NULL;
 
-       login_proxy_count++;
        DLLIST_PREPEND(&login_proxies, proxy);
-       main_ref();
 }
 
 void login_proxy_deinit(void)
index 74d5d5e993569546a9790b4ee4cc78267876f334..556ac42d096cfd950280dc8659adbfab735ad3eb 100644 (file)
@@ -39,9 +39,6 @@ void login_proxy_detach(struct login_proxy *proxy, struct istream *client_input,
 const char *login_proxy_get_host(const struct login_proxy *proxy) ATTR_PURE;
 unsigned int login_proxy_get_port(const struct login_proxy *proxy) ATTR_PURE;
 
-/* Return number of active detached login proxies */
-unsigned int login_proxy_get_count(void) ATTR_PURE;
-
 void login_proxy_deinit(void);
 
 #endif
index c1dae022b896441b2171e768e0f3b3b11b4c28d7..58406f0a291d9ec99186b0809a4a4cefa2f62779 100644 (file)
@@ -2,6 +2,7 @@
 
 #include "lib.h"
 #include "settings-parser.h"
+#include "master-service-settings.h"
 #include "login-settings.h"
 
 #include <stddef.h>
@@ -92,8 +93,6 @@ struct setting_parser_info login_setting_parser_info = {
        MEMBER(check_func) login_settings_check
 };
 
-static pool_t settings_pool = NULL;
-
 /* <settings checks> */
 static int ssl_settings_check(void *_set ATTR_UNUSED, const char **error_r)
 {
@@ -173,30 +172,19 @@ static bool login_settings_check(void *_set, pool_t pool ATTR_UNUSED,
 }
 /* </settings checks> */
 
-struct login_settings *login_settings_read(void)
+struct login_settings *login_settings_read(struct master_service *service)
 {
-       struct setting_parser_context *parser;
-        struct login_settings *set;
+       static const struct setting_parser_info *set_roots[] = {
+               &login_setting_parser_info,
+               NULL
+       };
        const char *error;
+       void **sets;
 
-       if (settings_pool == NULL)
-               settings_pool = pool_alloconly_create("settings pool", 512);
-       else
-               p_clear(settings_pool);
-
-       parser = settings_parser_init(settings_pool,
-                                     &login_setting_parser_info,
-                                     SETTINGS_PARSER_FLAG_IGNORE_UNKNOWN_KEYS);
-
-       if (settings_parse_environ(parser) < 0) {
-               i_fatal("Error reading configuration: %s",
-                       settings_parser_get_error(parser));
-       }
-
-       if (settings_parser_check(parser, settings_pool, &error) < 0)
-               i_fatal("Invalid settings: %s", error);
+       if (master_service_settings_read(service, set_roots, NULL, FALSE,
+                                        &error) < 0)
+               i_fatal("Error reading configuration: %s", error);
 
-       set = settings_parser_get(parser);
-       settings_parser_deinit(&parser);
-       return set;
+       sets = master_service_settings_get_others(service);
+       return sets[0];
 }
index 5f48f03bc5758526eb0dfc32ecb6c5a24dc8edf9..2f4a91aca2337049bae142b9dd248594b6361d69 100644 (file)
@@ -1,6 +1,8 @@
 #ifndef LOGIN_SETTINGS_H
 #define LOGIN_SETTINGS_H
 
+struct master_service;
+
 struct login_settings {
        const char *login_dir;
        bool login_chroot;
@@ -35,6 +37,6 @@ struct login_settings {
        const char *const *log_format_elements_split;
 };
 
-struct login_settings *login_settings_read(void);
+struct login_settings *login_settings_read(struct master_service *service);
 
 #endif
index 39667b8cf53743e395ee8e53bb96274bd1f652ae..2aabec1800d4494e672c1e0e626c496b31c8ab29 100644 (file)
@@ -2,14 +2,13 @@
 
 #include "common.h"
 #include "ioloop.h"
-#include "array.h"
-#include "lib-signals.h"
 #include "randgen.h"
 #include "restrict-access.h"
 #include "restrict-process-size.h"
 #include "process-title.h"
-#include "fd-close-on-exec.h"
-#include "master.h"
+#include "master-auth.h"
+#include "master-service.h"
+#include "master-interface.h"
 #include "client-common.h"
 #include "auth-client.h"
 #include "ssl-proxy.h"
 #include <unistd.h>
 #include <syslog.h>
 
-struct login_settings *login_settings;
-unsigned int login_process_uid;
 struct auth_client *auth_client;
 bool closing_down;
 
-static const char *process_name;
-static struct ioloop *ioloop;
-static int main_refcount;
-static bool is_inetd, listening;
-
-static ARRAY_DEFINE(listen_ios, struct io *);
-static unsigned int listen_count, ssl_listen_count;
-
-void main_ref(void)
-{
-       main_refcount++;
-}
-
-void main_unref(void)
-{
-       if (--main_refcount == 0) {
-               /* nothing to do, quit */
-               io_loop_stop(ioloop);
-       } else if (closing_down && clients_get_count() == 0) {
-               /* last login finished, close all communications
-                  to master process */
-               master_close();
-               /* we might still be proxying. close the connection to
-                  dovecot-auth, since it's not needed anymore. */
-               if (auth_client != NULL)
-                       auth_client_free(&auth_client);
-       } else if (clients_get_count() == 0) {
-               /* make sure we clear all the memory used by the
-                  authentication connections. also this makes sure that if
-                  this connection's authentication was finished but the master
-                  login wasn't, the next connection won't be able to log in
-                  as this user by finishing the master login. */
-               if (auth_client != NULL)
-                       auth_client_reconnect(auth_client);
-       }
-}
-
-static void sig_die(const siginfo_t *si, void *context ATTR_UNUSED)
-{
-       /* warn about being killed because of some signal, except SIGINT (^C)
-          which is too common at least while testing :) */
-       if (si->si_signo != SIGINT) {
-               i_warning("Killed with signal %d (by pid=%s uid=%s code=%s)",
-                         si->si_signo, dec2str(si->si_pid),
-                         dec2str(si->si_uid),
-                         lib_signal_code_to_str(si->si_signo, si->si_code));
-       }
-       io_loop_stop(ioloop);
-}
-
-static void login_accept(void *context)
-{
-       int listen_fd = POINTER_CAST_TO(context, int);
-       struct ip_addr remote_ip, local_ip;
-       unsigned int remote_port, local_port;
-       struct client *client;
-       int fd;
-
-       fd = net_accept(listen_fd, &remote_ip, &remote_port);
-       if (fd < 0) {
-               if (fd < -1)
-                       i_error("accept() failed: %m");
-               return;
-       }
-       i_set_failure_ip(&remote_ip);
-
-       if (net_getsockname(fd, &local_ip, &local_port) < 0) {
-               memset(&local_ip, 0, sizeof(local_ip));
-               local_port = 0;
-       }
+struct master_service *service;
+struct login_settings *login_settings;
 
-       client = client_create(fd, FALSE, &local_ip, &remote_ip);
-       client->remote_port = remote_port;
-       client->local_port = local_port;
+static bool ssl_connections = FALSE;
 
-       if (login_settings->login_process_per_connection) {
-               closing_down = TRUE;
-               main_listen_stop();
-       }
-}
-
-static void login_accept_ssl(void *context)
+static void client_connected(const struct master_service_connection *conn)
 {
-       int listen_fd = POINTER_CAST_TO(context, int);
-       struct ip_addr remote_ip, local_ip;
-       unsigned int remote_port, local_port;
        struct client *client;
        struct ssl_proxy *proxy;
-       int fd, fd_ssl;
-
-       fd = net_accept(listen_fd, &remote_ip, &remote_port);
-       if (fd < 0) {
-               if (fd < -1)
-                       i_error("accept() failed: %m");
-               return;
-       }
-       i_set_failure_ip(&remote_ip);
+       struct ip_addr local_ip;
+       unsigned int local_port;
+       int fd_ssl;
 
-       if (net_getsockname(fd, &local_ip, &local_port) < 0) {
+       if (net_getsockname(conn->fd, &local_ip, &local_port) < 0) {
                memset(&local_ip, 0, sizeof(local_ip));
                local_port = 0;
        }
 
-       connection_queue_add(1);
-
-       fd_ssl = ssl_proxy_new(fd, &remote_ip, &proxy);
-       if (fd_ssl == -1)
-               net_disconnect(fd);
-       else {
-               client = client_create(fd_ssl, TRUE, &local_ip, &remote_ip);
-               client->proxy = proxy;
-               client->remote_port = remote_port;
-               client->local_port = local_port;
-       }
-
-       if (login_settings->login_process_per_connection) {
-               closing_down = TRUE;
-               main_listen_stop();
-       }
-}
-
-void main_listen_start(void)
-{
-       struct io *io;
-       unsigned int i, current_count;
-       int cur_fd;
-
-       if (listening)
-               return;
-       if (closing_down) {
-               /* typically happens only with
-                  login_process_per_connection=yes after client logs in */
-               master_notify_state_change(LOGIN_STATE_FULL_LOGINS);
-               return;
-       }
-
-       current_count = ssl_proxy_get_count() + login_proxy_get_count();
-       if (current_count >= login_settings->login_max_connections) {
-               /* can't accept any more connections until existing proxies
-                  get destroyed */
-               return;
-       }
-
-       cur_fd = LOGIN_MASTER_SOCKET_FD + 1;
-       i_array_init(&listen_ios, listen_count + ssl_listen_count);
-       for (i = 0; i < listen_count; i++, cur_fd++) {
-               io = io_add(cur_fd, IO_READ, login_accept,
-                           POINTER_CAST(cur_fd));
-               array_append(&listen_ios, &io, 1);
-       }
-       for (i = 0; i < ssl_listen_count; i++, cur_fd++) {
-               io = io_add(cur_fd, IO_READ, login_accept_ssl,
-                           POINTER_CAST(cur_fd));
-               array_append(&listen_ios, &io, 1);
-       }
-       listening = TRUE;
-
-       /* the initial notification tells master that we're ok. if we die
-          before sending it, the master should shutdown itself. */
-       master_notify_state_change(LOGIN_STATE_LISTENING);
-}
-
-void main_listen_stop(void)
-{
-       struct io **ios;
-       unsigned int i, count;
-       int cur_fd;
-
-       if (!listening)
-               return;
-
-       ios = array_get_modifiable(&listen_ios, &count);
-       for (i = 0; i < count; i++)
-               io_remove(&ios[i]);
-       array_free(&listen_ios);
-
-       if (closing_down) {
-               cur_fd = LOGIN_MASTER_SOCKET_FD + 1;
-               for (i = 0; i < count; i++, cur_fd++) {
-                       if (close(cur_fd) < 0) {
-                               i_fatal("close(listener %d) failed: %m",
-                                       cur_fd);
-                       }
+       // FIXME: a global ssl_connections isn't enough!
+       if (!ssl_connections) {
+               client = client_create(conn->fd, FALSE, &local_ip,
+                                      &conn->remote_ip);
+       } else {
+               fd_ssl = ssl_proxy_new(conn->fd, &conn->remote_ip, &proxy);
+               if (fd_ssl == -1) {
+                       net_disconnect(conn->fd);
+                       return;
                }
-       }
-
-       listening = FALSE;
-       if (io_loop_is_running(ioloop)) {
-               master_notify_state_change(clients_get_count() == 0 ?
-                                          LOGIN_STATE_FULL_LOGINS :
-                                          LOGIN_STATE_FULL_PRELOGINS);
-       }
-}
-
-void connection_queue_add(unsigned int connection_count)
-{
-       unsigned int max_connections = login_settings->login_max_connections;
-       unsigned int current_count;
-
-       if (login_settings->login_process_per_connection)
-               return;
 
-       current_count = clients_get_count() + ssl_proxy_get_count() +
-               login_proxy_get_count();
-       if (current_count + connection_count + 2 >= max_connections) {
-               /* after this client we've reached max users count,
-                  so stop listening for more. reserve +2 extra for SSL with
-                  login proxy connections. */
-               main_listen_stop();
-
-               if (current_count >= max_connections) {
-                       /* already reached max. users count, kill few of the
-                          oldest connections.
-
-                          this happens when we've maxed out the login process
-                          count and master has told us to start listening for
-                          new connections even though we're full. */
-                       client_destroy_oldest();
-               }
+               client = client_create(fd_ssl, TRUE,
+                                      &local_ip, &conn->remote_ip);
+               client->proxying = TRUE;
+               client->proxy = proxy;
        }
+       client->remote_port = conn->remote_port;
+       client->local_port = local_port;
 }
 
 static void auth_connect_notify(struct auth_client *client ATTR_UNUSED,
@@ -253,211 +66,101 @@ static void auth_connect_notify(struct auth_client *client ATTR_UNUSED,
                 clients_notify_auth_connected();
 }
 
-static void drop_privileges(unsigned int *max_fds_r)
+static void main_preinit(void)
 {
-       const char *value;
-
-        login_settings = login_settings_read();
-
-       if (!is_inetd)
-               i_set_failure_internal();
-       else {
-               /* log to syslog */
-               value = getenv("SYSLOG_FACILITY");
-               i_set_failure_syslog(process_name, LOG_NDELAY,
-                                    value == NULL ? LOG_MAIL : atoi(value));
-       }
-
-       value = getenv("DOVECOT_VERSION");
-       if (value != NULL && strcmp(value, PACKAGE_VERSION) != 0) {
-               i_fatal("Dovecot version mismatch: "
-                       "Master is v%s, login is v"PACKAGE_VERSION" "
-                       "(if you don't care, set version_ignore=yes)", value);
-       }
-
-       value = getenv("LOGIN_DIR");
-       if (value == NULL)
-               i_fatal("LOGIN_DIR environment missing");
-       if (chdir(value) < 0)
-               i_error("chdir(%s) failed: %m", value);
+       unsigned int max_fds;
 
+       random_init();
        /* Initialize SSL proxy so it can read certificate and private
           key file. */
-       random_init();
        ssl_proxy_init();
 
-       value = getenv("LISTEN_FDS");
-       listen_count = value == NULL ? 0 : atoi(value);
-       value = getenv("SSL_LISTEN_FDS");
-       ssl_listen_count = value == NULL ? 0 : atoi(value);
-
        /* set the number of fds we want to use. it may get increased or
           decreased. leave a couple of extra fds for auth sockets and such.
           normal connections each use one fd, but SSL connections use two */
-       *max_fds_r = LOGIN_MASTER_SOCKET_FD + 16 +
-               listen_count + ssl_listen_count +
+       max_fds = MASTER_LISTEN_FD_FIRST + 16 +
+               master_service_get_socket_count(service) +
                login_settings->login_max_connections*2;
-       restrict_fd_limit(*max_fds_r);
+       restrict_fd_limit(max_fds);
+       io_loop_set_max_fd_count(current_ioloop, max_fds);
 
-       /* Refuse to run as root - we should never need it and it's
-          dangerous with SSL. */
-       restrict_access_by_env(NULL, TRUE);
+       i_assert(strcmp(login_settings->ssl, "no") == 0 || ssl_initialized);
 
-       /* make sure we can't fork() */
-       restrict_process_size((unsigned int)-1, 1);
+       restrict_access_by_env(NULL, TRUE);
 }
 
 static void main_init(void)
 {
-       const char *value;
-
-       lib_signals_init();
-        lib_signals_set_handler(SIGINT, TRUE, sig_die, NULL);
-        lib_signals_set_handler(SIGTERM, TRUE, sig_die, NULL);
-        lib_signals_ignore(SIGPIPE, TRUE);
-
-       value = getenv("PROCESS_UID");
-       if (value == NULL)
-               i_fatal("BUG: PROCESS_UID environment not given");
-        login_process_uid = strtoul(value, NULL, 10);
-       if (login_process_uid == 0)
-               i_fatal("BUG: PROCESS_UID environment is 0");
+       /* make sure we can't fork() */
+       restrict_process_size((unsigned int)-1, 1);
 
-        closing_down = FALSE;
-       main_refcount = 0;
+       if (restrict_access_get_current_chroot() == NULL) {
+               if (chdir("login") < 0)
+                       i_fatal("chdir(login) failed: %m");
+       }
 
-       auth_client = auth_client_new(login_process_uid);
+       auth_client = auth_client_new((unsigned int)getpid());
         auth_client_set_connect_notify(auth_client, auth_connect_notify, NULL);
-       clients_init();
-
-       if (!ssl_initialized && ssl_listen_count > 0) {
-               /* this shouldn't happen, master should have
-                  disabled the ssl socket.. */
-               i_fatal("BUG: SSL initialization parameters not given "
-                       "while they should have been");
-       }
 
-       if (!is_inetd) {
-               master_init(LOGIN_MASTER_SOCKET_FD);
-               main_listen_start();
-       }
+       clients_init();
+       master_auth_init(service);
 }
 
 static void main_deinit(void)
 {
-       closing_down = TRUE;
-       main_listen_stop();
-
        ssl_proxy_deinit();
        login_proxy_deinit();
 
        if (auth_client != NULL)
                auth_client_free(&auth_client);
        clients_deinit();
-       master_deinit();
-
-       lib_signals_deinit();
-       closelog();
+       master_auth_deinit(service);
 }
 
-int main(int argc ATTR_UNUSED, char *argv[], char *envp[])
+int main(int argc, char *argv[], char *envp[])
 {
-       const char *group_name;
-       struct ip_addr remote_ip, local_ip;
-       unsigned int remote_port, local_port, max_fds;
-       struct ssl_proxy *proxy = NULL;
-       struct client *client;
-       int i, fd = -1, master_fd = -1;
-       bool ssl = FALSE;
-
-       is_inetd = getenv("DOVECOT_MASTER") == NULL;
-
-#ifdef DEBUG
-       if (!is_inetd && getenv("GDB") == NULL) {
-               const char *env;
-
-               i = LOGIN_MASTER_SOCKET_FD + 1;
-               env = getenv("LISTEN_FDS");
-               if (env != NULL) i += atoi(env);
-               env = getenv("SSL_LISTEN_FDS");
-               if (env != NULL) i += atoi(env);
-
-               fd_debug_verify_leaks(i, 1024);
+       const char *getopt_str;
+       int c;
+
+       //FIXME:is_inetd = getenv("DOVECOT_MASTER") == NULL;
+
+       service = master_service_init(login_process_name, 0, argc, argv);
+       master_service_init_log(service, t_strconcat(login_process_name, ": ",
+                                                    NULL), 0);
+
+        getopt_str = t_strconcat("DS", master_service_getopt_string(), NULL);
+       while ((c = getopt(argc, argv, getopt_str)) > 0) {
+               switch (c) {
+               case 'D':
+                       restrict_access_allow_coredumps(TRUE);
+                       break;
+               case 'S':
+                       ssl_connections = TRUE;
+                       break;
+               default:
+                       if (!master_service_parse_option(service, c, optarg))
+                               exit(FATAL_DEFAULT);
+                       break;
+               }
        }
-#endif
-       /* NOTE: we start rooted, so keep the code minimal until
-          restrict_access_by_env() is called */
-       lib_init();
 
+#if 0
        if (is_inetd) {
                /* running from inetd. create master process before
                   dropping privileges. */
-               process_name = strrchr(argv[0], '/');
-               process_name = process_name == NULL ? argv[0] : process_name+1;
-               group_name = t_strcut(process_name, '-');
-
-               for (i = 1; i < argc; i++) {
-                       if (strncmp(argv[i], "--group=", 8) == 0) {
-                               group_name = argv[1]+8;
-                               break;
-                       }
-               }
-
-               master_fd = master_connect(group_name);
+               master_fd = master_connect(t_strcut(login_process_name, '-'));
        }
-
-       drop_privileges(&max_fds);
-
-       if (argv[1] != NULL && strcmp(argv[1], "-D") == 0)
-               restrict_access_allow_coredumps(TRUE);
+#endif
 
        process_title_init(argv, envp);
-       ioloop = io_loop_create();
-       io_loop_set_max_fd_count(ioloop, max_fds);
-       main_init();
+        login_settings = login_settings_read(service);
 
-       if (is_inetd) {
-               if (net_getpeername(1, &remote_ip, &remote_port) < 0) {
-                       i_fatal("%s can be started only through dovecot "
-                               "master process, inetd or equivalent", argv[0]);
-               }
-               if (net_getsockname(1, &local_ip, &local_port) < 0) {
-                       memset(&local_ip, 0, sizeof(local_ip));
-                       local_port = 0;
-               }
-
-               fd = 1;
-               for (i = 1; i < argc; i++) {
-                       if (strcmp(argv[i], "--ssl") == 0)
-                               ssl = TRUE;
-                       else if (strncmp(argv[i], "--group=", 8) != 0)
-                               i_fatal("Unknown parameter: %s", argv[i]);
-               }
-
-               /* hardcoded imaps and pop3s ports to be SSL by default */
-               if (local_port == 993 || local_port == 995 || ssl) {
-                       ssl = TRUE;
-                       fd = ssl_proxy_new(fd, &remote_ip, &proxy);
-                       if (fd == -1)
-                               return 1;
-               }
-
-               master_init(master_fd);
-               closing_down = TRUE;
-
-               if (fd != -1) {
-                       client = client_create(fd, ssl, &local_ip, &remote_ip);
-                       client->proxy = proxy;
-                       client->remote_port = remote_port;
-                       client->local_port = local_port;
-               }
-       }
+       main_preinit();
+       master_service_init_finish(service);
+       main_init();
 
-       io_loop_run(ioloop);
+       master_service_run(service, client_connected);
        main_deinit();
-
-       io_loop_destroy(&ioloop);
-       lib_deinit();
-
+       master_service_deinit(&service);
         return 0;
 }
diff --git a/src/login-common/master.c b/src/login-common/master.c
deleted file mode 100644 (file)
index cf26635..0000000
+++ /dev/null
@@ -1,311 +0,0 @@
-/* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
-
-#include "common.h"
-#include "hash.h"
-#include "buffer.h"
-#include "ioloop.h"
-#include "network.h"
-#include "fdpass.h"
-#include "istream.h"
-#include "env-util.h"
-#include "write-full.h"
-#include "master.h"
-#include "client-common.h"
-
-#include <unistd.h>
-
-static int master_fd;
-static struct io *io_master;
-static struct hash_table *master_requests;
-static unsigned int master_tag_counter;
-
-static unsigned int master_pos;
-static char master_buf[sizeof(struct master_login_reply)];
-static struct client destroyed_client;
-
-static void client_call_master_callback(struct client *client,
-                                       const struct master_login_reply *reply)
-{
-       master_callback_t *master_callback;
-
-       master_callback = client->master_callback;
-       client->master_tag = 0;
-       client->master_callback = NULL;
-
-       master_callback(client, reply);
-}
-
-static void request_handle(struct master_login_reply *reply)
-{
-       struct client *client;
-
-       if (reply->tag == 0 && !login_settings->login_process_per_connection) {
-               /* this means we have to start listening again.
-                  we've reached maximum number of login processes. */
-               main_listen_start();
-               return;
-       }
-
-       client = hash_table_lookup(master_requests, POINTER_CAST(reply->tag));
-       if (client == NULL)
-               i_fatal("Master sent reply with unknown tag %u", reply->tag);
-
-       hash_table_remove(master_requests, POINTER_CAST(reply->tag));
-       if (client != &destroyed_client) {
-               client_call_master_callback(client, reply);
-               /* NOTE: client may be destroyed now */
-       }
-}
-
-void master_request_login(struct client *client, master_callback_t *callback,
-                         unsigned int auth_pid, unsigned int auth_id)
-{
-       buffer_t *buf;
-       struct master_login_request *req;
-       struct stat st;
-       const unsigned char *data;
-       size_t size;
-       ssize_t ret;
-       unsigned int cmd_tag_size;
-
-       i_assert(auth_pid != 0);
-
-       if (master_fd == -1) {
-               struct master_login_reply reply;
-
-               i_assert(closing_down);
-               memset(&reply, 0, sizeof(reply));
-               reply.status = MASTER_LOGIN_STATUS_INTERNAL_ERROR;
-               callback(client, &reply);
-               return;
-       }
-
-       data = i_stream_get_data(client->input, &size);
-       cmd_tag_size = client->auth_command_tag == NULL ? 0 :
-               strlen(client->auth_command_tag);
-
-       buf = buffer_create_dynamic(pool_datastack_create(),
-                                   sizeof(*req) + size + cmd_tag_size);
-       buffer_write(buf, sizeof(*req), client->auth_command_tag, cmd_tag_size);
-       buffer_write(buf, sizeof(*req) + cmd_tag_size, data, size);
-       req = buffer_get_space_unsafe(buf, 0, sizeof(*req));
-       req->version = MASTER_LOGIN_PROTOCOL_VERSION;
-       req->tag = ++master_tag_counter;
-       if (req->tag == 0)
-               req->tag = ++master_tag_counter;
-       req->auth_pid = auth_pid;
-       req->auth_id = auth_id;
-       req->local_ip = client->local_ip;
-       req->remote_ip = client->ip;
-       req->cmd_tag_size =  cmd_tag_size;
-       req->data_size = req->cmd_tag_size + size;
-#if (LOGIN_MAX_INBUF_SIZE*2) != MASTER_LOGIN_MAX_DATA_SIZE
-#  error buffer max sizes unsynced
-#endif
-       i_assert(req->data_size <= LOGIN_MAX_INBUF_SIZE);
-
-       if (fstat(client->fd, &st) < 0)
-               i_fatal("fstat(client) failed: %m");
-       req->ino = st.st_ino;
-
-       ret = fd_send(master_fd, client->fd, buf->data, buf->used);
-       if (ret < 0)
-               i_fatal("fd_send(%d) failed: %m", client->fd);
-       if ((size_t)ret != buf->used) {
-               i_fatal("fd_send() sent only %d of %d bytes",
-                       (int)ret, (int)buf->used);
-       }
-
-       client->master_tag = req->tag;
-       client->master_callback = callback;
-
-       hash_table_insert(master_requests, POINTER_CAST(req->tag), client);
-}
-
-void master_request_abort(struct client *client)
-{
-       struct master_login_reply reply;
-
-       /* we're still going to get the reply from the master, so just
-          remember that we want to ignore it */
-       hash_table_update(master_requests, POINTER_CAST(client->master_tag),
-                         &destroyed_client);
-
-       memset(&reply, 0, sizeof(reply));
-       reply.status = MASTER_LOGIN_STATUS_INTERNAL_ERROR;
-       client_call_master_callback(client, &reply);
-}
-
-void master_notify_state_change(enum master_login_state state)
-{
-       struct master_login_request req;
-
-       if (io_master == NULL)
-               return;
-
-       memset(&req, 0, sizeof(req));
-       req.version = MASTER_LOGIN_PROTOCOL_VERSION;
-       req.tag = state;
-       req.ino = (ino_t)-1;
-
-       /* sending -1 as fd does the notification */
-       if (fd_send(master_fd, -1, &req, sizeof(req)) != sizeof(req))
-               i_fatal("fd_send(-1) failed: %m");
-}
-
-void master_close(void)
-{
-       if (io_master == NULL)
-               return;
-
-       io_remove(&io_master);
-       if (close(master_fd) < 0)
-               i_fatal("close(master) failed: %m");
-       master_fd = -1;
-
-       closing_down = TRUE;
-        main_listen_stop();
-       main_unref();
-
-        /* may call this function again through main_unref() */
-       clients_destroy_all();
-}
-
-static void master_exec(int fd)
-{
-       static char dovecot[] = "dovecot";
-       char *argv[] = { dovecot, NULL };
-
-       switch (fork()) {
-       case -1:
-               i_fatal("fork() failed: %m");
-       case 0:
-               if (dup2(fd, 0) < 0)
-                       i_fatal("master_exec: dup2(%d, 0) failed: %m", fd);
-               (void)close(fd);
-
-               if (setsid() < 0)
-                       i_fatal("setsid() failed: %m");
-
-               env_put("DOVECOT_INETD=1");
-               execv(SBINDIR"/dovecot", argv);
-               i_fatal_status(FATAL_EXEC, "execv(%s) failed: %m",
-                              SBINDIR"/dovecot");
-       default:
-               (void)close(fd);
-       }
-}
-
-static void master_read_env(int fd)
-{
-       struct istream *input;
-       const char *line;
-
-       env_clean();
-
-       /* read environment variable lines until empty line comes */
-       input = i_stream_create_fd(fd, 8192, FALSE);
-       do {
-               switch (i_stream_read(input)) {
-               case -1:
-                       i_fatal("EOF while reading environment from master");
-               case -2:
-                       i_fatal("Too large environment line from master");
-               }
-
-               while ((line = i_stream_next_line(input)) != NULL &&
-                      *line != '\0')
-                       env_put(line);
-       } while (line == NULL);
-
-       i_stream_destroy(&input);
-}
-
-int master_connect(const char *group_name)
-{
-       const char *path = PKG_RUNDIR"/master";
-       int i, fd = -1;
-
-       for (i = 0; i < 5 && fd == -1; i++) {
-               fd = net_connect_unix(path);
-               if (fd != -1)
-                       break;
-
-               if (errno == ECONNREFUSED) {
-                       if (unlink(path) < 0)
-                               i_error("unlink(%s) failed: %m", path);
-               } else if (errno != ENOENT) {
-                       i_fatal("Can't connect to master UNIX socket %s: %m",
-                               path);
-               }
-
-               /* need to create it */
-               fd = net_listen_unix(path, 16);
-               if (fd != -1) {
-                       master_exec(fd);
-                       fd = -1;
-               } else if (errno != EADDRINUSE) {
-                       i_fatal("Can't create master UNIX socket %s: %m", path);
-               }
-       }
-
-       if (fd == -1)
-               i_fatal("Couldn't use/create UNIX socket %s", path);
-
-       if (group_name[0] == '\0')
-               i_fatal("No login group name set");
-
-       if (strlen(group_name) >= 256)
-               i_fatal("Login group name too large: %s", group_name);
-
-       /* group_name length is now guaranteed to be in range of 1..255 so we
-          can send <length byte><name> */
-       group_name = t_strdup_printf("%c%s", (unsigned char)strlen(group_name),
-                                    group_name);
-       if (write_full(fd, group_name, strlen(group_name)) < 0)
-               i_fatal("write_full(master_fd) failed: %m");
-
-       master_read_env(fd);
-       return fd;
-}
-
-static void master_input(void *context ATTR_UNUSED)
-{
-       int ret;
-
-       ret = net_receive(master_fd, master_buf + master_pos,
-                         sizeof(master_buf) - master_pos);
-       if (ret < 0) {
-               /* master died, kill all clients logging in */
-               master_close();
-               return;
-       }
-
-       master_pos += ret;
-       if (master_pos < sizeof(master_buf))
-               return;
-
-       /* reply is now read */
-       request_handle((void *)master_buf);
-       master_pos = 0;
-}
-
-void master_init(int fd)
-{
-       main_ref();
-
-       master_fd = fd;
-       master_requests = hash_table_create(system_pool, system_pool,
-                                           0, NULL, NULL);
-
-        master_pos = 0;
-       io_master = io_add(master_fd, IO_READ, master_input, NULL);
-}
-
-void master_deinit(void)
-{
-       hash_table_destroy(&master_requests);
-
-       if (io_master != NULL)
-               io_remove(&io_master);
-}
diff --git a/src/login-common/master.h b/src/login-common/master.h
deleted file mode 100644 (file)
index 2c990a0..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-#ifndef MASTER_H
-#define MASTER_H
-
-struct client;
-
-#include "../master/master-login-interface.h"
-
-typedef void master_callback_t(struct client *client,
-                              const struct master_login_reply *reply);
-
-void master_request_login(struct client *client, master_callback_t *callback,
-                         unsigned int auth_pid, unsigned int auth_id);
-void master_request_abort(struct client *client);
-
-/* Notify master of a change in our state */
-void master_notify_state_change(enum master_login_state state);
-
-/* Close connection to master process */
-void master_close(void);
-
-/* inetd: Connect to existing master process, or create new one. */
-int master_connect(const char *group_name);
-
-void master_init(int fd);
-void master_deinit(void);
-
-#endif
index 45c8acb48bab0d231cfb1e536f05c4a4c58faca5..daf8555e61b47a11e4df8eafcf846b5d61f95d0e 100644 (file)
@@ -3,11 +3,13 @@
 #include "common.h"
 #include "base64.h"
 #include "buffer.h"
+#include "istream.h"
 #include "str-sanitize.h"
 #include "auth-client.h"
 #include "ssl-proxy.h"
+#include "master-interface.h"
+#include "master-auth.h"
 #include "client-common.h"
-#include "master.h"
 
 static enum auth_request_flags
 client_get_auth_flags(struct client *client)
@@ -38,19 +40,21 @@ call_client_callback(struct client *client, enum sasl_server_reply reply,
 }
 
 static void
-master_callback(struct client *client, const struct master_login_reply *reply)
+master_auth_callback(const struct master_auth_reply *reply, void *context)
 {
+       struct client *client = context;
        enum sasl_server_reply sasl_reply = SASL_SERVER_REPLY_MASTER_FAILED;
        const char *data = NULL;
 
+       client->master_tag = 0;
        client->authenticating = FALSE;
        switch (reply->status) {
-       case MASTER_LOGIN_STATUS_OK:
+       case MASTER_AUTH_STATUS_OK:
                sasl_reply = SASL_SERVER_REPLY_SUCCESS;
                break;
-       case MASTER_LOGIN_STATUS_INTERNAL_ERROR:
+       case MASTER_AUTH_STATUS_INTERNAL_ERROR:
                break;
-       case MASTER_LOGIN_STATUS_MAX_CONNECTIONS:
+       case MASTER_AUTH_STATUS_MAX_CONNECTIONS:
                data = "Maximum number of connections from user+IP exceeded "
                        "(mail_max_userip_connections)";
                break;
@@ -59,6 +63,35 @@ master_callback(struct client *client, const struct master_login_reply *reply)
        call_client_callback(client, sasl_reply, data, NULL);
 }
 
+static void
+master_send_request(struct client *client, struct auth_request *request)
+{
+       struct master_auth_request req;
+       const unsigned char *data;
+       size_t size;
+       buffer_t *buf;
+
+       memset(&req, 0, sizeof(req));
+       req.auth_pid = auth_client_request_get_server_pid(request);
+       req.auth_id = auth_client_request_get_id(request);
+       req.local_ip = client->local_ip;
+       req.remote_ip = client->ip;
+
+       buf = buffer_create_dynamic(pool_datastack_create(), 256);
+       if (client->auth_command_tag != NULL) {
+               buffer_append(buf, client->auth_command_tag,
+                             strlen(client->auth_command_tag)+1);
+       }
+
+       data = i_stream_get_data(client->input, &size);
+       buffer_append(buf, data, size);
+       req.data_size = buf->used;
+
+       client->master_tag =
+               master_auth_request(service, client->fd, &req, buf->data,
+                                   master_auth_callback, client);
+}
+
 static void authenticate_callback(struct auth_request *request, int status,
                                  const char *data_base64,
                                  const char *const *args, void *context)
@@ -101,9 +134,7 @@ static void authenticate_callback(struct auth_request *request, int status,
                        call_client_callback(client, SASL_SERVER_REPLY_SUCCESS,
                                             NULL, args);
                } else {
-                       master_request_login(client, master_callback,
-                               auth_client_request_get_server_pid(request),
-                               auth_client_request_get_id(request));
+                       master_send_request(client, request);
                }
                break;
        case -1:
index 35ffec1581283c3cd50660d492c312f37dcdc35a..3999d463d03a2fcbe1104a73bb702d6c75f5a9b8 100644 (file)
@@ -1,6 +1,8 @@
 #ifndef SASL_SERVER_H
 #define SASL_SERVER_H
 
+struct client;
+
 enum sasl_server_reply {
        SASL_SERVER_REPLY_SUCCESS,
        SASL_SERVER_REPLY_AUTH_FAILED,
index 4cc13ef5ad9b8e3ec109b213b45d45aba5931bd4..dd81722893ff30786fdf4d0f036233ba315c6df0 100644 (file)
@@ -8,6 +8,7 @@
 #include "read-full.h"
 #include "safe-memset.h"
 #include "llist.h"
+#include "master-service.h"
 #include "ssl-proxy.h"
 
 #include <fcntl.h>
@@ -473,7 +474,7 @@ static void ssl_step(struct ssl_proxy *proxy)
        ssl_proxy_unref(proxy);
 }
 
-int ssl_proxy_new(int fd, struct ip_addr *ip, struct ssl_proxy **proxy_r)
+int ssl_proxy_new(int fd, const struct ip_addr *ip, struct ssl_proxy **proxy_r)
 {
        struct ssl_proxy *proxy;
        SSL *ssl;
@@ -524,7 +525,6 @@ int ssl_proxy_new(int fd, struct ip_addr *ip, struct ssl_proxy **proxy_r)
        DLLIST_PREPEND(&ssl_proxies, proxy);
 
        ssl_step(proxy);
-       main_ref();
 
        *proxy_r = proxy;
        return sfd[1];
@@ -602,8 +602,6 @@ static void ssl_proxy_unref(struct ssl_proxy *proxy)
 
        SSL_free(proxy->ssl);
        i_free(proxy);
-
-       main_unref();
 }
 
 static void ssl_proxy_destroy(struct ssl_proxy *proxy)
@@ -631,7 +629,7 @@ static void ssl_proxy_destroy(struct ssl_proxy *proxy)
 
        ssl_proxy_unref(proxy);
 
-       main_listen_start();
+        master_service_client_connection_destroyed(service);
 }
 
 static RSA *ssl_gen_rsa_key(SSL *ssl ATTR_UNUSED,
index 00c2ef0ca819521577b1aa142fce9370b178d124..fa58d58b96e5d54b8d77d2a98222456f722e9268 100644 (file)
@@ -9,7 +9,7 @@ bool ssl_initialized = FALSE;
 
 /* no SSL support */
 
-int ssl_proxy_new(int fd ATTR_UNUSED, struct ip_addr *ip ATTR_UNUSED,
+int ssl_proxy_new(int fd ATTR_UNUSED, const struct ip_addr *ip ATTR_UNUSED,
                  struct ssl_proxy **proxy_r ATTR_UNUSED)
 {
        i_error("Dovecot wasn't built with SSL support");
index d8422d2b917f140ccee868a897d65981325ab872..5dbe42bcd57ddf9ab55ffe33010fccb9117d1c88 100644 (file)
@@ -9,7 +9,7 @@ extern bool ssl_initialized;
 /* establish SSL connection with the given fd, returns a new fd which you
    must use from now on, or -1 if error occurred. Unless -1 is returned,
    the given fd must be simply forgotten. */
-int ssl_proxy_new(int fd, struct ip_addr *ip, struct ssl_proxy **proxy_r);
+int ssl_proxy_new(int fd, const struct ip_addr *ip, struct ssl_proxy **proxy_r);
 bool ssl_proxy_has_valid_client_cert(const struct ssl_proxy *proxy) ATTR_PURE;
 bool ssl_proxy_has_broken_client_cert(struct ssl_proxy *proxy);
 const char *ssl_proxy_get_peer_name(struct ssl_proxy *proxy);
index 33390273e630cade469695d14a7f154e4f2e93f0..1f462de68d347e9fdc46dea25eeb70107b6ce5f9 100644 (file)
@@ -1,7 +1,7 @@
 pkglibexecdir = $(libexecdir)/dovecot
 
 sbin_PROGRAMS = dovecot
-pkglibexec_PROGRAMS = ssl-build-param
+#pkglibexec_PROGRAMS = ssl-build-param
 
 AM_CPPFLAGS = \
        -I$(top_srcdir)/src/lib \
@@ -11,52 +11,37 @@ AM_CPPFLAGS = \
        -DPKG_RUNDIR=\""$(rundir)"\" \
        -DPKG_STATEDIR=\""$(statedir)"\" \
        -DPKG_LIBEXECDIR=\""$(pkglibexecdir)"\" \
-       -DBINDIR=\""$(bindir)"\" \
-       -DSSLDIR=\""$(ssldir)\""
+       -DBINDIR=\""$(bindir)"\"
 
-dovecot_LDADD = \
+libs = \
        $(LIBCAP) \
        $(LIBDOVECOT)
 
+dovecot_LDADD = $(libs)
+dovecot_DEPENDENCIES = $(libs)
+
 dovecot_SOURCES = \
-       auth-process.c \
-       askpass.c \
        capabilities-posix.c \
-       child-process.c \
-       dict-process.c \
        dup2-array.c \
-       listener.c \
-       log.c \
-       login-process.c \
-       mail-process.c \
-       master-settings.c \
        main.c \
-       ssl-init.c \
-       sysinfo-get.c
+       master-settings.c \
+       service-auth-server.c \
+       service-auth-source.c \
+       service-listen.c \
+       service-log.c \
+       service-monitor.c \
+       service-process.c \
+       service.c
 
 noinst_HEADERS = \
-       auth-process.h \
-       askpass.h \
        capabilities.h \
-       child-process.h \
-       dict-process.h \
-       dup2-array.h \
-       listener.h \
        common.h \
-       log.h \
-       login-process.h \
-       mail-process.h \
-       master-login-interface.h \
+       dup2-array.h \
        master-settings.h \
-       ssl-init.h \
-       sysinfo-get.h
-
-ssl_build_param_SOURCES = \
-       ssl-init-main.c \
-       ssl-init-openssl.c \
-       ssl-init-gnutls.c
-
-ssl_build_param_LDADD = \
-       $(LIBDOVECOT) \
-       $(SSL_LIBS)
-ssl_build_param_DEPENDENCIES = $(LIBDOVECOT)
+       service-auth-server.h \
+       service-auth-source.h \
+       service-listen.h \
+       service-log.h \
+       service-monitor.h \
+       service-process.h \
+       service.h
diff --git a/src/master/auth-process.c b/src/master/auth-process.c
deleted file mode 100644 (file)
index d2ca6f2..0000000
+++ /dev/null
@@ -1,760 +0,0 @@
-/* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
-
-#include "common.h"
-#include "array.h"
-#include "hash.h"
-#include "ioloop.h"
-#include "env-util.h"
-#include "fd-close-on-exec.h"
-#include "unix-socket-create.h"
-#include "network.h"
-#include "istream.h"
-#include "ostream.h"
-#include "str.h"
-#include "restrict-access.h"
-#include "restrict-process-size.h"
-#include "auth-process.h"
-#include "child-process.h"
-#include "../auth/auth-master-interface.h"
-#include "log.h"
-
-#include <stdlib.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <pwd.h>
-#include <syslog.h>
-
-#define MAX_INBUF_SIZE 8192
-#define MAX_OUTBUF_SIZE 65536
-
-struct auth_process_group {
-       struct auth_process_group *next;
-
-       int listen_fd;
-       const struct master_settings *master_set;
-       const struct master_auth_settings *set;
-
-       unsigned int process_count;
-       struct auth_process *processes;
-};
-
-struct auth_process {
-       struct auth_process *next;
-
-        struct auth_process_group *group;
-       pid_t pid;
-       int fd;
-       struct io *io;
-       struct istream *input;
-       struct ostream *output;
-
-       int worker_listen_fd;
-       struct io *worker_io;
-
-       struct hash_table *requests;
-
-       unsigned int external:1;
-       unsigned int version_received:1;
-       unsigned int initialized:1;
-       unsigned int in_auth_reply:1;
-};
-
-bool have_initialized_auth_processes = FALSE;
-
-static struct child_process auth_child_process =
-       { MEMBER(type) PROCESS_TYPE_AUTH };
-static struct child_process auth_worker_child_process =
-       { MEMBER(type) PROCESS_TYPE_AUTH_WORKER };
-
-static struct timeout *to;
-static unsigned int auth_tag;
-static struct auth_process_group *process_groups;
-static bool auth_stalled = FALSE;
-
-static void auth_process_destroy(struct auth_process *p);
-static int create_auth_worker(struct auth_process *process, int fd);
-static void auth_processes_start_missing(void *context);
-
-void auth_process_request(struct auth_process *process, unsigned int login_pid,
-                         unsigned int login_id,
-                         struct login_auth_request *request)
-{
-       string_t *str;
-       ssize_t ret;
-
-       str = t_str_new(256);
-       str_printfa(str, "REQUEST\t%u\t%u\t%u\n",
-                   ++auth_tag, login_pid, login_id);
-
-       ret = o_stream_send(process->output, str_data(str), str_len(str));
-       if (ret != (ssize_t)str_len(str)) {
-               if (ret >= 0) {
-                       /* FIXME: well .. I'm not sure if it'd be better to
-                          just block here. I don't think this condition should
-                          happen often, so this could mean that the auth
-                          process is stuck. Or that the computer is just
-                          too heavily loaded. Possibility to block infinitely
-                          is annoying though, so for now don't do it. */
-                       i_warning("Auth process %s transmit buffer full, "
-                                 "killing..", dec2str(process->pid));
-               }
-               auth_process_destroy(process);
-       } else {
-               hash_table_insert(process->requests,
-                                 POINTER_CAST(auth_tag), request);
-       }
-}
-
-static bool
-auth_process_input_user(struct auth_process *process, const char *args)
-{
-       struct login_auth_request *request;
-       const char *const *list;
-       unsigned int id;
-
-       /* <id> <userid> [..] */
-
-       list = t_strsplit(args, "\t");
-       if (list[0] == NULL || list[1] == NULL) {
-               i_error("BUG: Auth process %s sent corrupted USER line",
-                       dec2str(process->pid));
-               return FALSE;
-       }
-       id = (unsigned int)strtoul(list[0], NULL, 10);
-
-       request = hash_table_lookup(process->requests, POINTER_CAST(id));
-       if (request == NULL) {
-               i_error("BUG: Auth process %s sent unrequested reply with ID "
-                       "%u", dec2str(process->pid), id);
-               return FALSE;
-       }
-
-       if (!auth_success_written) {
-               int fd;
-
-               fd = creat(AUTH_SUCCESS_PATH, 0666);
-               if (fd == -1)
-                       i_error("creat(%s) failed: %m", AUTH_SUCCESS_PATH);
-               else
-                       (void)close(fd);
-               auth_success_written = TRUE;
-       }
-
-       auth_master_callback(list[1], list + 2, request);
-       hash_table_remove(process->requests, POINTER_CAST(id));
-       return TRUE;
-}
-
-static bool
-auth_process_input_notfound(struct auth_process *process, const char *args)
-{
-       struct login_auth_request *request;
-       unsigned int id;
-
-       id = (unsigned int)strtoul(args, NULL, 10);
-
-       request = hash_table_lookup(process->requests, POINTER_CAST(id));
-       if (request == NULL) {
-               i_error("BUG: Auth process %s sent unrequested reply with ID "
-                       "%u", dec2str(process->pid), id);
-               return FALSE;
-       }
-
-       auth_master_callback(NULL, NULL, request);
-       hash_table_remove(process->requests, POINTER_CAST(id));
-       return TRUE;
-}
-
-static bool
-auth_process_input_spid(struct auth_process *process, const char *args)
-{
-       unsigned int pid;
-
-       if (process->initialized) {
-               i_error("BUG: Authentication server re-handshaking");
-               return FALSE;
-       }
-
-       pid = (unsigned int)strtoul(args, NULL, 10);
-       if (pid == 0) {
-               i_error("BUG: Authentication server said it's PID 0");
-               return FALSE;
-       }
-
-       if (process->pid != 0 && process->pid != (pid_t)pid) {
-               i_error("BUG: Authentication server sent invalid SPID "
-                       "(%u != %s)", pid, dec2str(process->pid));
-               return FALSE;
-       }
-
-       process->pid = pid;
-        process->initialized = TRUE;
-
-       have_initialized_auth_processes = TRUE;
-       return TRUE;
-}
-
-static bool
-auth_process_input_fail(struct auth_process *process, const char *args)
-{
-       struct login_auth_request *request;
-       const char *error;
-       unsigned int id;
-
-       error = strchr(args, '\t');
-       if (error != NULL)
-               error++;
-
-       id = (unsigned int)strtoul(args, NULL, 10);
-
-       request = hash_table_lookup(process->requests, POINTER_CAST(id));
-       if (request == NULL) {
-               i_error("BUG: Auth process %s sent unrequested reply with ID "
-                       "%u", dec2str(process->pid), id);
-               return FALSE;
-       }
-
-       auth_master_callback(NULL, NULL, request);
-       hash_table_remove(process->requests, POINTER_CAST(id));
-       return TRUE;
-}
-
-static bool
-auth_process_input_line(struct auth_process *process, const char *line)
-{
-       if (strncmp(line, "USER\t", 5) == 0)
-               return auth_process_input_user(process, line + 5);
-       else if (strncmp(line, "NOTFOUND\t", 9) == 0)
-               return auth_process_input_notfound(process, line + 9);
-       else if (strncmp(line, "FAIL\t", 5) == 0)
-               return auth_process_input_fail(process, line + 5);
-       else if (strncmp(line, "SPID\t", 5) == 0)
-               return auth_process_input_spid(process, line + 5);
-       else
-               return TRUE;
-}
-
-static void auth_process_input(struct auth_process *process)
-{
-       const char *line;
-       bool ret;
-
-       switch (i_stream_read(process->input)) {
-       case 0:
-               return;
-       case -1:
-               /* disconnected */
-               auth_process_destroy(process);
-               return;
-       case -2:
-               /* buffer full */
-               i_error("BUG: Auth process %s sent us more than %d "
-                       "bytes of data", dec2str(process->pid),
-                       (int)MAX_INBUF_SIZE);
-               auth_process_destroy(process);
-               return;
-       }
-
-       if (!process->version_received) {
-               line = i_stream_next_line(process->input);
-               if (line == NULL)
-                       return;
-
-               /* make sure the major version matches */
-               if (strncmp(line, "VERSION\t", 8) != 0 ||
-                   atoi(t_strcut(line + 8, '\t')) !=
-                   AUTH_MASTER_PROTOCOL_MAJOR_VERSION) {
-                       i_error("Auth process %s not compatible with master "
-                               "process (mixed old and new binaries?)",
-                               dec2str(process->pid));
-                       auth_process_destroy(process);
-                       return;
-               }
-               process->version_received = TRUE;
-       }
-
-       while ((line = i_stream_next_line(process->input)) != NULL) {
-               T_BEGIN {
-                       ret = auth_process_input_line(process, line);
-               } T_END;
-               if (!ret) {
-                       auth_process_destroy(process);
-                       break;
-               }
-       }
-}
-
-static void auth_worker_input(struct auth_process *p)
-{
-       int fd;
-
-       fd = net_accept(p->worker_listen_fd, NULL, NULL);
-       if (fd < 0) {
-               if (fd == -2)
-                       i_error("accept(worker) failed: %m");
-               return;
-       }
-
-       net_set_nonblock(fd, TRUE);
-       fd_close_on_exec(fd, TRUE);
-
-       create_auth_worker(p, fd);
-}
-
-static struct auth_process *
-auth_process_new(pid_t pid, int fd, struct auth_process_group *group)
-{
-       struct auth_process *p;
-       const char *path, *handshake;
-
-       if (pid != 0)
-               child_process_add(pid, &auth_child_process);
-
-       p = i_new(struct auth_process, 1);
-       p->group = group;
-       p->pid = pid;
-       p->fd = fd;
-       p->io = io_add(fd, IO_READ, auth_process_input, p);
-       p->input = i_stream_create_fd(fd, MAX_INBUF_SIZE, FALSE);
-       p->output = o_stream_create_fd(fd, MAX_OUTBUF_SIZE, FALSE);
-       p->requests = hash_table_create(default_pool, default_pool, 0,
-                                       NULL, NULL);
-
-       group->process_count++;
-
-       path = t_strdup_printf("%s/auth-worker.%s",
-                              *group->set->chroot != '\0' ?
-                              group->set->chroot :
-                              group->master_set->base_dir,
-                              dec2str(pid));
-       p->worker_listen_fd =
-               unix_socket_create(path, 0600, group->set->uid,
-                                  group->set->gid, 128);
-       if (p->worker_listen_fd == -1)
-               i_fatal("Couldn't create auth worker listener");
-
-       net_set_nonblock(p->worker_listen_fd, TRUE);
-       fd_close_on_exec(p->worker_listen_fd, TRUE);
-       p->worker_io = io_add(p->worker_listen_fd, IO_READ,
-                             auth_worker_input, p);
-
-       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);
-
-       p->next = group->processes;
-       group->processes = p;
-       return p;
-}
-
-static void auth_process_destroy(struct auth_process *p)
-{
-       struct hash_iterate_context *iter;
-       void *key, *value;
-       struct auth_process **pos;
-       const char *path;
-
-       if (!p->initialized && io_loop_is_running(ioloop) && !p->external) {
-               /* log the process exit and kill ourself */
-               child_processes_flush();
-               log_deinit();
-               i_fatal("Auth process died too early - shutting down");
-       }
-
-       for (pos = &p->group->processes; *pos != NULL; pos = &(*pos)->next) {
-               if (*pos == p) {
-                       *pos = p->next;
-                       break;
-               }
-       }
-       p->group->process_count--;
-
-       path = t_strdup_printf("%s/auth-worker.%s",
-                              *p->group->set->chroot != '\0' ?
-                              p->group->set->chroot :
-                              p->group->master_set->base_dir,
-                              dec2str(p->pid));
-       (void)unlink(path);
-
-       io_remove(&p->worker_io);
-       if (close(p->worker_listen_fd) < 0)
-               i_error("close(worker_listen) failed: %m");
-
-       iter = hash_table_iterate_init(p->requests);
-       while (hash_table_iterate(iter, &key, &value))
-               auth_master_callback(NULL, NULL, value);
-       hash_table_iterate_deinit(&iter);
-       hash_table_destroy(&p->requests);
-
-       i_stream_destroy(&p->input);
-       o_stream_destroy(&p->output);
-       io_remove(&p->io);
-       if (close(p->fd) < 0)
-               i_error("close(auth) failed: %m");
-       i_free(p);
-}
-
-static int connect_auth_socket(struct auth_process_group *group,
-                              const char *path)
-{
-       struct auth_process *auth;
-       int fd;
-
-       fd = net_connect_unix(path);
-       if (fd == -1) {
-               i_error("net_connect_unix(%s) failed: %m", path);
-               return -1;
-       }
-
-       net_set_nonblock(fd, TRUE);
-       fd_close_on_exec(fd, TRUE);
-       auth = auth_process_new(0, fd, group);
-       auth->external = TRUE;
-       return 0;
-}
-
-static void auth_set_environment(const struct master_settings *master_set,
-                                const struct master_auth_settings *set)
-{
-       struct restrict_access_settings rset;
-
-       master_settings_export_to_env(master_set);
-
-       /* setup access environment */
-       restrict_access_init(&rset);
-       rset.system_groups_user = set->user;
-       rset.uid = set->uid;
-       rset.gid = set->gid;
-       rset.chroot_dir = set->chroot;
-       restrict_access_set_env(&rset);
-
-       /* set other environment */
-       env_put("DOVECOT_MASTER=1");
-       env_put(t_strconcat("AUTH_NAME=", set->name, NULL));
-       restrict_process_size(set->process_size, (unsigned int)-1);
-}
-
-static const struct master_auth_socket_settings *
-get_connect_socket(const struct master_auth_settings *auth_set)
-{
-       struct master_auth_socket_settings *const *as;
-       unsigned int count;
-
-       if (!array_is_created(&auth_set->sockets))
-               return NULL;
-
-       as = array_get(&auth_set->sockets, &count);
-       if (count > 0 && strcmp(as[0]->type, "connect") == 0)
-               return as[0];
-       else
-               return NULL;
-}
-
-static int create_auth_process(struct auth_process_group *group)
-{
-       const struct master_auth_socket_settings *as;
-       struct master_auth_socket_unix_settings *const *masters;
-       const char *prefix, *executable;
-       struct log_io *log;
-       pid_t pid;
-       unsigned int count;
-       int fd[2], log_fd, i;
-
-       /* see if this is a connect socket */
-       as = get_connect_socket(group->set);
-       if (as != NULL) {
-               masters = array_get(&as->masters, &count);
-               if (count > 0)
-                       return connect_auth_socket(group, masters[0]->path);
-       }
-
-       /* create communication to process with a socket pair */
-       if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd) < 0) {
-               i_error("socketpair() failed: %m");
-               return -1;
-       }
-
-       log_fd = log_create_pipe(&log, 0);
-       if (log_fd < 0)
-               pid = -1;
-       else {
-               pid = fork();
-               if (pid < 0)
-                       i_error("fork() failed: %m");
-       }
-
-       if (pid < 0) {
-               (void)close(fd[0]);
-               (void)close(fd[1]);
-               (void)close(log_fd);
-               return -1;
-       }
-
-       if (pid != 0) {
-               /* master */
-               prefix = t_strdup_printf("auth(%s): ", group->set->name);
-               log_set_prefix(log, prefix);
-               log_set_pid(log, pid);
-
-               net_set_nonblock(fd[0], TRUE);
-               fd_close_on_exec(fd[0], TRUE);
-               auth_process_new(pid, fd[0], group);
-               (void)close(fd[1]);
-               (void)close(log_fd);
-               return 0;
-       }
-
-       prefix = t_strdup_printf("master-auth(%s): ", group->set->name);
-       log_set_prefix(log, prefix);
-
-       /* move master communication handle to 0 */
-       if (dup2(fd[1], 0) < 0)
-               i_fatal("dup2(stdin) failed: %m");
-
-       (void)close(fd[0]);
-       (void)close(fd[1]);
-
-       /* make sure we don't leak syslog fd. try to do it as late as possible,
-          but also before dup2()s in case syslog fd is one of them. */
-       closelog();
-
-       /* set stdout to /dev/null, so anything written into it gets ignored. */
-       if (dup2(null_fd, 1) < 0)
-               i_fatal("dup2(stdout) failed: %m");
-
-       if (dup2(log_fd, 2) < 0)
-               i_fatal("dup2(stderr) failed: %m");
-
-       child_process_init_env(group->master_set);
-
-       if (group->listen_fd != 3) {
-               if (dup2(group->listen_fd, 3) < 0)
-                       i_fatal("dup2() failed: %m");
-       }
-       fd_close_on_exec(3, FALSE);
-
-       for (i = 0; i <= 2; i++)
-               fd_close_on_exec(i, FALSE);
-
-        auth_set_environment(group->master_set, group->set);
-
-       env_put(t_strdup_printf("AUTH_WORKER_PATH=%s/auth-worker.%s",
-                               *group->set->chroot != '\0' ? "" :
-                               group->master_set->base_dir,
-                               dec2str(getpid())));
-
-       executable = group->set->executable;
-       client_process_exec(executable, "");
-       i_fatal_status(FATAL_EXEC, "execv(%s) failed: %m", executable);
-       return -1;
-}
-
-static int create_auth_worker(struct auth_process *process, int fd)
-{
-       struct log_io *log;
-       const char *prefix, *executable;
-       pid_t pid;
-       int log_fd, i;
-
-       log_fd = log_create_pipe(&log, 0);
-       if (log_fd < 0)
-               pid = -1;
-       else {
-               pid = fork();
-               if (pid < 0)
-                       i_error("fork() failed: %m");
-       }
-
-       if (pid < 0) {
-               (void)close(log_fd);
-               return -1;
-       }
-
-       if (pid != 0) {
-               /* master */
-               child_process_add(pid, &auth_worker_child_process);
-               prefix = t_strdup_printf("auth-worker(%s): ",
-                                        process->group->set->name);
-               log_set_prefix(log, prefix);
-               (void)close(fd);
-               (void)close(log_fd);
-               return 0;
-       }
-
-       prefix = t_strdup_printf("master-auth-worker(%s): ",
-                                process->group->set->name);
-       log_set_prefix(log, prefix);
-
-       /* make sure we don't leak syslog fd. try to do it as late as possible,
-          but also before dup2()s in case syslog fd is one of them. */
-       closelog();
-
-       /* set stdin and stdout to /dev/null, so anything written into it
-          gets ignored. */
-       if (dup2(null_fd, 0) < 0)
-               i_fatal("dup2(stdin) failed: %m");
-       if (dup2(null_fd, 1) < 0)
-               i_fatal("dup2(stdout) failed: %m");
-
-       if (dup2(log_fd, 2) < 0)
-               i_fatal("dup2(stderr) failed: %m");
-
-       if (dup2(fd, 4) < 0)
-               i_fatal("dup2(4) failed: %m");
-
-       for (i = 0; i <= 2; i++)
-               fd_close_on_exec(i, FALSE);
-       fd_close_on_exec(4, FALSE);
-
-       child_process_init_env(process->group->master_set);
-        auth_set_environment(process->group->master_set, process->group->set);
-
-       executable = t_strconcat(process->group->set->executable, " -w", NULL);
-       client_process_exec(executable, "");
-       i_fatal_status(FATAL_EXEC, "execv(%s) failed: %m", executable);
-       return -1;
-}
-
-struct auth_process *auth_process_find(unsigned int pid)
-{
-       struct auth_process_group *group;
-       struct auth_process *p;
-
-       for (group = process_groups; group != NULL; group = group->next) {
-               for (p = group->processes; p != NULL; p = p->next) {
-                       if ((unsigned int)p->pid == pid)
-                               return p;
-               }
-       }
-
-       return NULL;
-}
-
-static void auth_process_group_create(struct master_settings *set,
-                                     struct master_auth_settings *auth_set)
-{
-       struct auth_process_group *group;
-       const char *path;
-
-       group = i_new(struct auth_process_group, 1);
-       group->master_set = set;
-       group->set = auth_set;
-
-       group->next = process_groups;
-       process_groups = group;
-
-       if (get_connect_socket(auth_set) != NULL)
-               return;
-
-       path = t_strconcat(set->login_dir, "/", auth_set->name, NULL);
-       group->listen_fd = unix_socket_create(path, 0660, master_uid,
-                                             set->server->login_gid, 128);
-       if (group->listen_fd == -1)
-               i_fatal("Couldn't create auth process listener");
-
-       net_set_nonblock(group->listen_fd, TRUE);
-       fd_close_on_exec(group->listen_fd, TRUE);
-}
-
-static void auth_process_group_destroy(struct auth_process_group *group)
-{
-       struct auth_process *next;
-       const char *path;
-
-       while (group->processes != NULL) {
-               next = group->processes->next;
-               auth_process_destroy(group->processes);
-                group->processes = next;
-       }
-
-       path = t_strconcat(group->master_set->login_dir, "/",
-                          group->set->name, NULL);
-       (void)unlink(path);
-
-       if (close(group->listen_fd) < 0)
-               i_error("close(%s) failed: %m", path);
-       i_free(group);
-}
-
-void auth_processes_destroy_all(void)
-{
-       struct auth_process_group *next;
-
-       while (process_groups != NULL) {
-               next = process_groups->next;
-               auth_process_group_destroy(process_groups);
-               process_groups = next;
-       }
-
-       have_initialized_auth_processes = FALSE;
-}
-
-static void auth_process_groups_create(struct master_settings *set)
-{
-       struct master_auth_settings *const *auth_sets;
-       unsigned int i, count;
-
-       auth_sets = array_get(&set->auths, &count);
-       for (i = 0; i < count; i++)
-               auth_process_group_create(set, auth_sets[i]);
-}
-
-static void auth_processes_stall(void)
-{
-       if (auth_stalled)
-               return;
-
-       i_error("Temporary failure in creating authentication processes, "
-               "slowing down for now");
-       auth_stalled = TRUE;
-
-       timeout_remove(&to);
-       to = timeout_add(60*1000, auth_processes_start_missing, NULL);
-}
-
-static void
-auth_processes_start_missing(void *context ATTR_UNUSED)
-{
-       struct auth_process_group *group;
-       unsigned int count;
-
-       if (process_groups == NULL) {
-               /* first time here, create the groups */
-               auth_process_groups_create(master_set->defaults);
-       }
-
-       for (group = process_groups; group != NULL; group = group->next) {
-               count = group->process_count;
-               for (; count < group->set->count; count++) {
-                       if (create_auth_process(group) < 0) {
-                               auth_processes_stall();
-                               return;
-                       }
-               }
-       }
-
-       if (auth_stalled) {
-               /* processes were created successfully */
-               i_info("Created authentication processes successfully, "
-                      "unstalling");
-
-               auth_stalled = FALSE;
-               timeout_remove(&to);
-               to = timeout_add(1000, auth_processes_start_missing, NULL);
-       }
-}
-
-void auth_processes_init(void)
-{
-       process_groups = NULL;
-       to = timeout_add(1000, auth_processes_start_missing, NULL);
-
-       auth_processes_start_missing(NULL);
-}
-
-void auth_processes_deinit(void)
-{
-       timeout_remove(&to);
-       auth_processes_destroy_all();
-}
diff --git a/src/master/auth-process.h b/src/master/auth-process.h
deleted file mode 100644 (file)
index 0f8b9e2..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-#ifndef AUTH_PROCESS_H
-#define AUTH_PROCESS_H
-
-struct login_auth_request;
-
-extern bool have_initialized_auth_processes;
-
-void auth_master_callback(const char *user, const char *const *args,
-                         struct login_auth_request *request);
-
-/* Find process for given id */
-struct auth_process *auth_process_find(unsigned int pid);
-
-/* Request information about given cookie */
-void auth_process_request(struct auth_process *process, unsigned int login_pid,
-                         unsigned int login_id,
-                         struct login_auth_request *request);
-
-/* Close any fds used by auth processes */
-void auth_processes_destroy_all(void);
-
-void auth_processes_init(void);
-void auth_processes_deinit(void);
-
-#endif
diff --git a/src/master/child-process.c b/src/master/child-process.c
deleted file mode 100644 (file)
index 80cb61c..0000000
+++ /dev/null
@@ -1,290 +0,0 @@
-/* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
-
-#include "common.h"
-#include "lib-signals.h"
-#include "array.h"
-#include "hash.h"
-#include "str.h"
-#include "env-util.h"
-#include "child-process.h"
-
-#include <unistd.h>
-#include <syslog.h>
-#include <sys/wait.h>
-
-const char *process_names[PROCESS_TYPE_MAX] = {
-       "unknown",
-       "auth",
-       "auth-worker",
-       "login",
-       "imap",
-       "pop3",
-       "ssl-build-param",
-       "dict"
-};
-
-struct hash_table *processes;
-static child_process_destroy_callback_t *destroy_callbacks[PROCESS_TYPE_MAX];
-
-struct child_process *child_process_lookup(pid_t pid)
-{
-       return hash_table_lookup(processes, POINTER_CAST(pid));
-}
-
-void child_process_add(pid_t pid, struct child_process *process)
-{
-       hash_table_insert(processes, POINTER_CAST(pid), process);
-}
-
-void child_process_remove(pid_t pid)
-{
-       hash_table_remove(processes, POINTER_CAST(pid));
-}
-
-void child_process_init_env(const struct master_settings *set)
-{
-       /* remove all environment, we don't need them */
-       env_clean();
-
-       /* we'll log through master process */
-       env_put("LOG_TO_MASTER=1");
-       env_put("DOVECONF_ENV=1");
-       if (env_tz != NULL)
-               env_put(t_strconcat("TZ=", env_tz, NULL));
-
-       if (master_set != NULL && !set->version_ignore)
-               env_put("DOVECOT_VERSION="PACKAGE_VERSION);
-#ifdef DEBUG
-       if (gdb) env_put("GDB=1");
-#endif
-}
-
-void client_process_exec(const char *cmd, const char *title)
-{
-       const char **argv;
-
-       /* very simple argument splitting. */
-       if (*title == '\0')
-               argv = t_strsplit(cmd, " ");
-       else
-               argv = t_strsplit(t_strconcat(cmd, " ", title, NULL), " ");
-
-       client_process_exec_argv(argv[0], argv);
-}
-
-void client_process_exec_argv(const char *executable, const char **argv)
-{
-       const char *p;
-
-       /* hide the path, it's ugly */
-       p = strrchr(argv[0], '/');
-       if (p != NULL) argv[0] = p+1;
-
-       execv(executable, (char **)argv);
-}
-
-static const char *get_exit_status_message(enum fatal_exit_status status,
-                                          enum process_type process_type)
-{
-       switch (status) {
-       case FATAL_LOGOPEN:
-               return "Can't open log file";
-       case FATAL_LOGWRITE:
-               return "Can't write to log file";
-       case FATAL_LOGERROR:
-               return "Internal logging error";
-       case FATAL_OUTOFMEM:
-               switch (process_type) {
-               case PROCESS_TYPE_AUTH:
-               case PROCESS_TYPE_AUTH_WORKER:
-                       return "Out of memory - see auth_process_size setting";
-               case PROCESS_TYPE_LOGIN:
-                       return "Out of memory - see login_process_size setting";
-               case PROCESS_TYPE_IMAP:
-               case PROCESS_TYPE_POP3:
-                       return "Out of memory - see mail_process_size setting";
-               case PROCESS_TYPE_UNKNOWN:
-               case PROCESS_TYPE_SSL_PARAM:
-               case PROCESS_TYPE_DICT:
-               case PROCESS_TYPE_MAX:
-                       break;
-               }
-               return "Out of memory";
-       case FATAL_EXEC:
-               return "exec() failed";
-
-       case FATAL_DEFAULT:
-               return "Fatal failure";
-       }
-
-       return NULL;
-}
-
-static void
-log_coredump(string_t *str, enum process_type process_type, int status)
-{
-#ifdef WCOREDUMP
-       struct master_auth_settings *const *auth_set;
-       int signum = WTERMSIG(status);
-
-       if (WCOREDUMP(status)) {
-               str_append(str, " (core dumped)");
-               return;
-       }
-
-       if (signum != SIGABRT && signum != SIGSEGV && signum != SIGBUS)
-               return;
-
-       /* let's try to figure out why we didn't get a core dump */
-       if (core_dumps_disabled) {
-               str_printfa(str, " (core dumps disabled)");
-               return;
-       }
-
-       switch (process_type) {
-       case PROCESS_TYPE_LOGIN:
-#ifdef HAVE_PR_SET_DUMPABLE
-               str_append(str, " (core not dumped - add -D to login_executable)");
-               return;
-#else
-               break;
-#endif
-       case PROCESS_TYPE_IMAP:
-       case PROCESS_TYPE_POP3:
-#ifndef HAVE_PR_SET_DUMPABLE
-               if (!master_set->defaults->mail_drop_priv_before_exec) {
-                       str_append(str, " (core not dumped - set mail_drop_priv_before_exec=yes)");
-                       return;
-               }
-               if (*master_set->defaults->mail_privileged_group != '\0') {
-                       str_append(str, " (core not dumped - mail_privileged_group prevented it)");
-                       return;
-               }
-#endif
-               str_append(str, " (core not dumped - is home dir set?)");
-               return;
-       case PROCESS_TYPE_AUTH:
-       case PROCESS_TYPE_AUTH_WORKER:
-               auth_set = array_idx(&master_set->defaults->auths, 0);
-               if (auth_set[0]->uid == 0)
-                       break;
-#ifdef HAVE_PR_SET_DUMPABLE
-               str_printfa(str, " (core not dumped - "
-                           "no permissions for auth user %s in %s?)",
-                           auth_set[0]->user, master_set->defaults->base_dir);
-#else
-               str_append(str, " (core not dumped - auth user is not root)");
-#endif
-               return;
-       default:
-               break;
-       }
-       str_append(str, " (core not dumped)");
-#endif
-}
-
-static void sigchld_handler(const siginfo_t *si ATTR_UNUSED,
-                           void *context ATTR_UNUSED)
-{
-       struct child_process *process;
-       const char *process_type_name, *msg;
-       enum process_type process_type;
-       string_t *str;
-       pid_t pid;
-       int status;
-       bool abnormal_exit;
-
-       str = t_str_new(128);
-       while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
-               /* get the type and remove from hash */
-               str_truncate(str, 0);
-               process = child_process_lookup(pid);
-               if (process == NULL)
-                       process_type = PROCESS_TYPE_UNKNOWN;
-               else {
-                       process_type = process->type;
-                       child_process_remove(pid);
-               }
-               abnormal_exit = TRUE;
-
-               /* write errors to syslog */
-               process_type_name = process_names[process_type];
-               if (WIFEXITED(status)) {
-                       status = WEXITSTATUS(status);
-                       if (status == 0) {
-                               abnormal_exit = FALSE;
-                               if (process_type == PROCESS_TYPE_UNKNOWN) {
-                                       i_error("unknown child %s exited "
-                                               "successfully", dec2str(pid));
-                               }
-                       } else if (status == 1 &&
-                                  process_type == PROCESS_TYPE_SSL_PARAM) {
-                               /* kludgy. hide this failure. */
-                       } else if (status == FATAL_DEFAULT &&
-                                  process->seen_fatal) {
-                               /* the error was already logged. */
-                       } else {
-                               msg = get_exit_status_message(status,
-                                                             process_type);
-                               msg = msg == NULL ? "" :
-                                       t_strconcat(" (", msg, ")", NULL);
-                               str_printfa(str,
-                                           "child %s (%s) returned error %d%s",
-                                           dec2str(pid), process_type_name,
-                                           status, msg);
-                       }
-               } else if (WIFSIGNALED(status)) {
-                       str_printfa(str, "child %s (%s) killed with signal %d",
-                                   dec2str(pid), process_type_name,
-                                   WTERMSIG(status));
-                       log_coredump(str, process_type, status);
-               }
-
-               if (str_len(str) > 0) {
-                       if (process != NULL && process->ip.family != 0) {
-                               if (!process->ip_changed)
-                                       str_append(str, " (ip=");
-                               else
-                                       str_append(str, " (latest ip=");
-                               str_printfa(str, "%s)",
-                                           net_ip2addr(&process->ip));
-                       }
-                       i_error("%s", str_c(str));
-               }
-
-               if (destroy_callbacks[process_type] != NULL) {
-                       destroy_callbacks[process_type](process, pid,
-                                                       abnormal_exit);
-               }
-       }
-
-       if (pid == -1 && errno != EINTR && errno != ECHILD)
-               i_warning("waitpid() failed: %m");
-}
-
-void child_process_set_destroy_callback(enum process_type type,
-                                       child_process_destroy_callback_t *cb)
-{
-       i_assert(type < PROCESS_TYPE_MAX);
-
-       destroy_callbacks[type] = cb;
-}
-
-void child_processes_init(void)
-{
-       processes = hash_table_create(default_pool, default_pool, 128, NULL, NULL);
-       lib_signals_set_handler(SIGCHLD, TRUE, sigchld_handler, NULL);
-}
-
-void child_processes_flush(void)
-{
-       /* make sure we log if child processes died unexpectedly */
-       sigchld_handler(NULL, NULL);
-}
-
-void child_processes_deinit(void)
-{
-       child_processes_flush();
-       lib_signals_unset_handler(SIGCHLD, sigchld_handler, NULL);
-       hash_table_destroy(&processes);
-}
diff --git a/src/master/child-process.h b/src/master/child-process.h
deleted file mode 100644 (file)
index 6f1417f..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-#ifndef CHILD_PROCESS_H
-#define CHILD_PROCESS_H
-
-enum process_type {
-       PROCESS_TYPE_UNKNOWN,
-       PROCESS_TYPE_AUTH,
-       PROCESS_TYPE_AUTH_WORKER,
-       PROCESS_TYPE_LOGIN,
-       PROCESS_TYPE_IMAP,
-       PROCESS_TYPE_POP3,
-       PROCESS_TYPE_SSL_PARAM,
-       PROCESS_TYPE_DICT,
-
-       PROCESS_TYPE_MAX
-};
-
-struct child_process {
-       enum process_type type;
-       struct ip_addr ip;
-       unsigned int allow_change_ip:1;
-       unsigned int seen_fatal:1;
-       unsigned int ip_changed:1;
-};
-
-typedef void child_process_destroy_callback_t(struct child_process *process,
-                                             pid_t pid, bool abnormal_exit);
-
-extern const char *process_names[];
-extern struct hash_table *processes;
-
-struct child_process *child_process_lookup(pid_t pid);
-void child_process_add(pid_t pid, struct child_process *process);
-void child_process_remove(pid_t pid);
-
-void child_process_init_env(const struct master_settings *set);
-void client_process_exec(const char *cmd, const char *title);
-void client_process_exec_argv(const char *executable, const char **argv);
-
-void child_process_set_destroy_callback(enum process_type type,
-                                       child_process_destroy_callback_t *cb);
-
-void child_processes_init(void);
-void child_processes_flush(void);
-void child_processes_deinit(void);
-
-#endif
index b694407a11f7a6bc71e2d39074d507b0719a4085..50e39194804589c1881fff8738b38d311c07c683 100644 (file)
@@ -1,26 +1,16 @@
 #ifndef COMMON_H
 #define COMMON_H
 
-struct ip_addr;
-
 #include "lib.h"
+#include "master-interface.h"
 #include "master-settings.h"
 
-#define AUTH_SUCCESS_PATH PKG_STATEDIR"/auth-success"
-
-extern struct ioloop *ioloop;
-extern int null_fd, inetd_login_fd;
+extern struct master_service *master_service;
 extern uid_t master_uid;
-extern char program_path[];
-extern char ssl_manual_key_password[];
-extern const char *env_tz;
-extern bool auth_success_written;
+extern gid_t master_gid;
 extern bool core_dumps_disabled;
-#ifdef DEBUG
-extern bool gdb;
-#endif
+extern int null_fd;
 
-#define IS_INETD() \
-       (inetd_login_fd != -1)
+void process_exec(const char *cmd, const char *extra_args[]) ATTR_NORETURN;
 
 #endif
diff --git a/src/master/dict-process.c b/src/master/dict-process.c
deleted file mode 100644 (file)
index 5f8909b..0000000
+++ /dev/null
@@ -1,241 +0,0 @@
-/* Copyright (c) 2006-2009 Dovecot authors, see the included COPYING file */
-
-#include "common.h"
-#include "array.h"
-#include "ioloop.h"
-#include "network.h"
-#include "fd-close-on-exec.h"
-#include "env-util.h"
-#include "log.h"
-#include "child-process.h"
-#include "dict-process.h"
-
-#include <syslog.h>
-#include <unistd.h>
-#include <sys/stat.h>
-
-#define DICT_SERVER_SOCKET_NAME "dict-server"
-
-struct dict_listener {
-       char *path;
-       int fd;
-       struct io *io;
-
-       struct dict_process *processes;
-       unsigned int destroyed:1;
-};
-
-struct dict_process {
-       struct child_process process;
-       struct dict_process *next;
-
-       struct dict_listener *listener;
-       struct log_io *log;
-};
-
-static struct dict_listener *dict_listener;
-
-static int dict_process_create(struct dict_listener *listener)
-{
-       struct dict_process *process;
-       struct log_io *log;
-       const char *executable;
-       unsigned int i;
-       int log_fd;
-       pid_t pid;
-
-       process = i_new(struct dict_process, 1);
-       process->process.type = PROCESS_TYPE_DICT;
-       process->listener = listener;
-
-       log_fd = log_create_pipe(&log, 0);
-       if (log_fd < 0)
-               pid = -1;
-       else {
-               pid = fork();
-               if (pid < 0)
-                       i_error("fork() failed: %m");
-       }
-
-       if (pid < 0) {
-               (void)close(log_fd);
-               i_free(process);
-               return -1;
-       }
-
-       if (pid != 0) {
-               /* master */
-               process->next = process->listener->processes;
-               process->listener->processes = process;
-
-               child_process_add(pid, &process->process);
-               log_set_prefix(log, "dict: ");
-               log_set_pid(log, pid);
-               (void)close(log_fd);
-
-               process->log = log;
-               log_ref(process->log);
-               return 0;
-       }
-       log_set_prefix(log, "master-dict: ");
-
-       /* make sure we don't leak syslog fd. try to do it as late as possible,
-          but also before dup2()s in case syslog fd is one of them. */
-       closelog();
-
-       /* set stdin and stdout to /dev/null, so anything written into it
-          gets ignored. */
-       if (dup2(null_fd, 0) < 0)
-               i_fatal("dup2(stdin) failed: %m");
-       if (dup2(null_fd, 1) < 0)
-               i_fatal("dup2(stdout) failed: %m");
-
-       /* stderr = log, 3 = listener */
-       if (dup2(log_fd, 2) < 0)
-               i_fatal("dup2(stderr) failed: %m");
-       if (dup2(process->listener->fd, 3) < 0)
-               i_fatal("dup2(3) failed: %m");
-
-       for (i = 0; i <= 3; i++)
-               fd_close_on_exec(i, FALSE);
-
-       child_process_init_env(master_set->defaults);
-       master_settings_export_to_env(master_set->defaults);
-       env_put(t_strconcat("DICT_LISTEN_FROM_FD=",
-                           process->listener->path, NULL));
-
-       executable = PKG_LIBEXECDIR"/dict";
-       client_process_exec(executable, "");
-       i_fatal_status(FATAL_EXEC, "execv(%s) failed: %m", executable);
-       return -1;
-}
-
-static void dict_listener_unref(struct dict_listener *listener)
-{
-       if (listener->processes == NULL && listener->destroyed)
-               i_free(listener);
-}
-
-static void dict_process_deinit(struct dict_process *process)
-{
-       struct dict_listener *listener = process->listener;
-       struct dict_process **p;
-
-       for (p = &listener->processes; *p != NULL; p = &(*p)->next) {
-               if (*p == process) {
-                       *p = process->next;
-                       break;
-               }
-       }
-
-       if (process->log != NULL)
-               log_unref(process->log);
-       i_free(process);
-
-       dict_listener_unref(listener);
-}
-
-static void dict_listener_input(struct dict_listener *listener)
-{
-       unsigned int i = 0;
-       int fd;
-
-       i_assert(listener->processes == NULL);
-
-       for (i = 0; i < master_set->defaults->dict_process_count; i++) {
-               if (dict_process_create(listener) < 0)
-                       break;
-       }
-       if (i > 0)
-               io_remove(&listener->io);
-       else {
-               /* failed to create dict process, so just reject this
-                  connection and try again later */
-               fd = net_accept(listener->fd, NULL, NULL);
-               if (fd >= 0)
-                       (void)close(fd);
-       }
-}
-
-static struct dict_listener *dict_listener_init(const char *path)
-{
-       struct dict_listener *listener;
-       mode_t old_umask;
-
-       listener = i_new(struct dict_listener, 1);
-       listener->path = i_strdup(path);
-       old_umask = umask(0);
-       listener->fd = net_listen_unix_unlink_stale(path, 128);
-       umask(old_umask);
-       if (listener->fd == -1) {
-               if (errno == EADDRINUSE)
-                       i_fatal("Socket already exists: %s", path);
-               else
-                       i_fatal("net_listen_unix(%s) failed: %m", path);
-       }
-       fd_close_on_exec(listener->fd, TRUE);
-       listener->io = io_add(listener->fd, IO_READ,
-                             dict_listener_input, listener);
-       return listener;
-}
-
-static void dict_listener_deinit(struct dict_listener *listener)
-{
-       listener->destroyed = TRUE;
-
-       if (listener->io != NULL)
-               io_remove(&listener->io);
-       if (close(listener->fd) < 0)
-               i_error("close(dict listener) failed: %m");
-       listener->fd = -1;
-
-       /* don't try to free the dict processes here,
-          let dict_process_destroyed() do it to avoid "unknown child exited"
-          errors. */
-       dict_listener_unref(listener);
-}
-
-static void
-dict_process_destroyed(struct child_process *_process,
-                      pid_t pid ATTR_UNUSED, bool abnormal_exit ATTR_UNUSED)
-{
-       struct dict_process *process = (struct dict_process *)_process;
-       struct dict_listener *listener = process->listener;
-
-       if (listener->processes == NULL && listener->fd != -1) {
-               /* last listener died, create new ones */
-               listener->io = io_add(listener->fd, IO_READ,
-                                     dict_listener_input, listener);
-       }
-       dict_process_deinit(process);
-}
-
-void dict_processes_init(void)
-{
-       const char *path;
-
-       path = t_strconcat(master_set->defaults->base_dir,
-                          "/"DICT_SERVER_SOCKET_NAME, NULL);
-       dict_listener = dict_listener_init(path);
-
-       child_process_set_destroy_callback(PROCESS_TYPE_DICT,
-                                          dict_process_destroyed);
-}
-
-void dict_processes_deinit(void)
-{
-       dict_listener_deinit(dict_listener);
-}
-
-void dict_processes_kill(void)
-{
-       struct dict_process *process;
-
-       process = dict_listener->processes;
-       for (; process != NULL; process = process->next) {
-               if (process->log != NULL) {
-                       log_unref(process->log);
-                       process->log = NULL;
-               }
-       }
-}
diff --git a/src/master/dict-process.h b/src/master/dict-process.h
deleted file mode 100644 (file)
index 9a32450..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-#ifndef DICT_PROCESS_H
-#define DICT_PROCESS_H
-
-void dict_processes_init(void);
-void dict_processes_deinit(void);
-void dict_processes_kill(void);
-
-#endif
diff --git a/src/master/listener.c b/src/master/listener.c
deleted file mode 100644 (file)
index 337c44b..0000000
+++ /dev/null
@@ -1,350 +0,0 @@
-/* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
-
-#include "common.h"
-#include "array.h"
-#include "ioloop.h"
-#include "fd-close-on-exec.h"
-#include "listener.h"
-
-#include <stdlib.h>
-#include <unistd.h>
-
-static void resolve_ip(const char *set_name, const char *name,
-                      struct ip_addr *ip, unsigned int *port)
-{
-       struct ip_addr *ip_list;
-       const char *p;
-       unsigned int ips_count;
-       int ret;
-
-       if (*name == '\0') {
-                /* defaults to "*" or "[::]" */
-               ip->family = 0;
-               return;
-       }
-
-       if (name[0] == '[') {
-               /* IPv6 address */
-               p = strchr(name, ']');
-               if (p == NULL) {
-                       i_fatal("%s: Missing ']' in address %s",
-                               set_name, name);
-               }
-               name = t_strdup_until(name+1, p);
-
-               p++;
-               if (*p == '\0')
-                       p = NULL;
-               else if (*p != ':') {
-                       i_fatal("%s: Invalid data after ']' in address %s",
-                               set_name, name);
-               }
-       } else {
-               p = strrchr(name, ':');
-               if (p != NULL)
-                       name = t_strdup_until(name, p);
-       }
-
-       if (p != NULL) {
-               if (!is_numeric(p+1, '\0')) {
-                       i_fatal("%s: Invalid port in address %s",
-                               set_name, name);
-               }
-               *port = atoi(p+1);
-       }
-
-       if (strcmp(name, "*") == 0) {
-               /* IPv4 any */
-               net_get_ip_any4(ip);
-               return;
-       }
-
-       if (strcmp(name, "::") == 0) {
-               /* IPv6 any */
-               net_get_ip_any6(ip);
-               return;
-       }
-
-       /* Return the first IP if there happens to be multiple. */
-       ret = net_gethostbyname(name, &ip_list, &ips_count);
-       if (ret != 0) {
-               i_fatal("%s: Can't resolve address %s: %s",
-                       set_name, name, net_gethosterror(ret));
-       }
-
-       if (ips_count < 1)
-               i_fatal("%s: No IPs for address: %s", set_name, name);
-
-       *ip = ip_list[0];
-}
-
-static void
-check_conflicts_set(const struct master_settings *set, const struct ip_addr *ip,
-                   unsigned int port, const char *name1, const char *name2)
-{
-       const struct listener *listens = NULL;
-       unsigned int i, count;
-
-       if (array_is_created(&set->listens))
-               listens = array_get(&set->listens, &count);
-       else
-               count = 0;
-       for (i = 0; i < count; i++) {
-               if (listens[i].fd <= 0 || listens[i].port != port ||
-                   !net_ip_compare(&listens[i].ip, ip))
-                       continue;
-
-               i_fatal("Protocols %s and %s are listening in same ip/port",
-                       name1, name2);
-       }
-
-       if (array_is_created(&set->ssl_listens))
-               listens = array_get(&set->ssl_listens, &count);
-       else
-               count = 0;
-       for (i = 0; i < count; i++) {
-               if (listens[i].fd <= 0 || listens[i].port != port ||
-                   !net_ip_compare(&listens[i].ip, ip))
-                       continue;
-
-               i_fatal("Protocols %ss and %ss are listening in same ip/port",
-                       name1, name2);
-       }
-}
-
-static void check_conflicts(const struct ip_addr *ip, unsigned int port,
-                           const char *proto)
-{
-       if (master_set->imap != NULL) {
-               check_conflicts_set(master_set->imap, ip, port,
-                                   "imap", proto);
-       }
-       if (master_set->pop3 != NULL) {
-               check_conflicts_set(master_set->pop3, ip, port,
-                                   "pop3", proto);
-       }
-}
-
-static void
-listener_init(const char *set_name, const char *listen_list,
-             unsigned int default_port, ARRAY_TYPE(listener) *listens_arr)
-{
-       const char *const *tmp;
-       struct listener l, *listens;
-       unsigned int i, count;
-
-       if (!array_is_created(listens_arr))
-               i_array_init(listens_arr, 4);
-
-       listens = array_get_modifiable(listens_arr, &count);
-       for (i = 0; i < count; i++)
-               listens[i].wanted = FALSE;
-
-       memset(&l, 0, sizeof(l));
-       l.fd = -1;
-       l.wanted = TRUE;
-
-       for (tmp = t_strsplit_spaces(listen_list, ", "); *tmp != NULL; tmp++) {
-               l.port = default_port;
-               resolve_ip(set_name, *tmp, &l.ip, &l.port);
-
-               /* see if it already exists */
-               for (i = 0; i < count; i++) {
-                       if (listens[i].port == l.port &&
-                           net_ip_compare(&listens[i].ip, &l.ip)) {
-                               listens[i].wanted = TRUE;
-                               break;
-                       }
-               }
-
-               if (i == count) {
-                       array_append(listens_arr, &l, 1);
-                       listens = array_get_modifiable(listens_arr, &count);
-               }
-       }
-
-       /* close unwanted fds */
-       for (i = 0; i < count; ) {
-               if (listens[i].wanted)
-                       i++;
-               else {
-                       if (listens[i].fd > 0) {
-                               if (close(listens[i].fd) < 0)
-                                       i_error("close(listener) failed: %m");
-                       }
-                       array_delete(listens_arr, i, 1);
-                       listens = array_get_modifiable(listens_arr, &count);
-               }
-       }
-}
-
-static void listener_close_fds(ARRAY_TYPE(listener) *listens_arr)
-{
-       const struct listener *listens;
-       unsigned int i, count;
-
-       if (!array_is_created(listens_arr))
-               return;
-
-       listens = array_get(listens_arr, &count);
-       for (i = 0; i < count; i++) {
-               if (listens[i].fd > 0) {
-                       if (close(listens[i].fd) < 0)
-                               i_error("close(listener) failed: %m");
-               }
-       }
-       array_free(listens_arr);
-}
-
-static void listen_parse_and_close_unneeded(struct master_settings *set)
-{
-       const char *const *proto;
-       unsigned int default_port;
-       bool nonssl_listen = FALSE, ssl_listen = FALSE;
-
-       if (set == NULL)
-               return;
-
-       /* register wanted protocols */
-        proto = t_strsplit_spaces(set->protocols, " ");
-       for (; *proto != NULL; proto++) {
-               if (strcasecmp(*proto, "imap") == 0) {
-                       if (set->protocol == MAIL_PROTOCOL_IMAP)
-                               nonssl_listen = TRUE;
-               } else if (strcasecmp(*proto, "imaps") == 0) {
-                       if (set->protocol == MAIL_PROTOCOL_IMAP &&
-                           strcmp(set->ssl, "no") != 0)
-                               ssl_listen = TRUE;
-               } else if (strcasecmp(*proto, "pop3") == 0) {
-                       if (set->protocol == MAIL_PROTOCOL_POP3)
-                               nonssl_listen = TRUE;
-               } else if (strcasecmp(*proto, "pop3s") == 0) {
-                       if (set->protocol == MAIL_PROTOCOL_POP3 &&
-                           strcmp(set->ssl, "no") != 0)
-                               ssl_listen = TRUE;
-               }
-       }
-
-       if (!nonssl_listen)
-               listener_close_fds(&set->listens);
-       else {
-               default_port = set->protocol == MAIL_PROTOCOL_IMAP ? 143 : 110;
-               listener_init("listen", set->listen, default_port,
-                             &set->listens);
-       }
-       if (!ssl_listen)
-               listener_close_fds(&set->ssl_listens);
-       else {
-               default_port = set->protocol == MAIL_PROTOCOL_IMAP ? 993 : 995;
-               listener_init("ssl_listen", *set->ssl_listen != '\0' ?
-                             set->ssl_listen : set->listen, default_port,
-                             &set->ssl_listens);
-       }
-}
-
-static void listen_copy_old(struct master_settings *old_set,
-                           struct master_settings *new_set)
-{
-       if (old_set == NULL || new_set == NULL) {
-               if (old_set != NULL) {
-                       listener_close_fds(&old_set->listens);
-                       listener_close_fds(&old_set->ssl_listens);
-               }
-               return;
-       }
-
-       i_assert(!array_is_created(&new_set->listens));
-       i_assert(!array_is_created(&new_set->ssl_listens));
-
-       new_set->listens = old_set->listens;
-       new_set->ssl_listens = old_set->ssl_listens;
-
-       old_set->listens.arr.buffer = NULL;
-       old_set->ssl_listens.arr.buffer = NULL;
-}
-
-static void
-listener_array_listen_missing(const char *proto,
-                             ARRAY_TYPE(listener) *listens_arr, bool retry)
-{
-       struct listener *listens;
-       unsigned int i, j, count;
-
-       if (!array_is_created(listens_arr))
-               return;
-
-       listens = array_get_modifiable(listens_arr, &count);
-       for (i = 0; i < count; i++) {
-               if (listens[i].fd > 0)
-                       continue;
-
-               for (j = 0; j < 10; j++) {
-                       listens[i].fd = net_listen(&listens[i].ip,
-                                                  &listens[i].port, 128);
-                       if (listens[i].fd != -1)
-                               break;
-
-                       if (errno == EADDRINUSE) {
-                               /* retry */
-                       } else if (errno == EINTR &&
-                                  io_loop_is_running(ioloop)) {
-                               /* SIGHUPing sometimes gets us here.
-                                  we don't want to die. */
-                       } else {
-                               /* error */
-                               break;
-                       }
-
-                       check_conflicts(&listens[i].ip, listens[i].port, proto);
-                       if (!retry)
-                               break;
-
-                       /* wait a while and try again. we're SIGHUPing
-                          so we most likely just closed it ourself.. */
-                       sleep(1);
-               }
-
-               if (listens[i].fd == -1) {
-                       i_fatal("listen(%s, %d) failed: %m",
-                               net_ip2addr(&listens[i].ip), listens[i].port);
-               }
-               net_set_nonblock(listens[i].fd, TRUE);
-               fd_close_on_exec(listens[i].fd, TRUE);
-       }
-}
-
-static void
-listener_listen_missing(struct master_settings *set,
-                       const char *proto, bool retry)
-{
-       listener_array_listen_missing(proto, &set->listens, retry);
-       listener_array_listen_missing(t_strconcat(proto, "s", NULL),
-                                     &set->ssl_listens, retry);
-}
-
-void listeners_open_fds(struct master_server_settings *old_set, bool retry)
-{
-       if (old_set != NULL) {
-               listen_copy_old(old_set->imap, master_set->imap);
-               listen_copy_old(old_set->pop3, master_set->pop3);
-       }
-       listen_parse_and_close_unneeded(master_set->imap);
-       listen_parse_and_close_unneeded(master_set->pop3);
-
-       if (master_set->imap != NULL)
-               listener_listen_missing(master_set->imap, "imap", retry);
-       if (master_set->pop3 != NULL)
-               listener_listen_missing(master_set->pop3, "pop3", retry);
-}
-
-void listeners_close_fds(void)
-{
-       if (master_set->imap != NULL) {
-               listener_close_fds(&master_set->imap->listens);
-               listener_close_fds(&master_set->imap->ssl_listens);
-       }
-       if (master_set->pop3 != NULL) {
-               listener_close_fds(&master_set->pop3->listens);
-               listener_close_fds(&master_set->pop3->ssl_listens);
-       }
-}
diff --git a/src/master/listener.h b/src/master/listener.h
deleted file mode 100644 (file)
index 0bd5a6f..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-#ifndef LISTENER_H
-#define LISTENER_H
-
-void listeners_open_fds(struct master_server_settings *old_set, bool retry);
-void listeners_close_fds(void);
-
-#endif
diff --git a/src/master/log.c b/src/master/log.c
deleted file mode 100644 (file)
index 92c0ee3..0000000
+++ /dev/null
@@ -1,338 +0,0 @@
-/* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
-
-#include "common.h"
-#include "ioloop.h"
-#include "istream.h"
-#include "fd-set-nonblock.h"
-#include "fd-close-on-exec.h"
-#include "child-process.h"
-#include "log.h"
-
-#include <unistd.h>
-
-struct log_io {
-       struct log_io *prev, *next;
-       int refcount;
-
-       struct io *io;
-       struct istream *stream;
-       pid_t pid;
-       struct ip_addr ip;
-
-       time_t log_stamp;
-       unsigned int log_counter;
-        unsigned int max_lines_per_sec;
-
-       char *prefix;
-       char next_log_type;
-       unsigned int throttle_msg:1;
-       unsigned int destroying:1;
-};
-
-static struct log_io *log_ios;
-static struct timeout *to;
-static unsigned int throttle_count;
-
-static int log_it(struct log_io *log_io, const char *line, bool continues);
-static int log_read(struct log_io *log_io);
-static void log_throttle_timeout(void *context);
-
-static bool log_write_pending(struct log_io *log_io)
-{
-       const char *line;
-       bool ret;
-
-       if (log_io->log_stamp != ioloop_time) {
-               log_io->log_stamp = ioloop_time;
-               log_io->log_counter = 0;
-       }
-
-       while ((line = i_stream_next_line(log_io->stream)) != NULL) {
-               T_BEGIN {
-                       ret = log_it(log_io, line, FALSE);
-               } T_END;
-               if (!ret)
-                       return FALSE;
-       }
-
-       return TRUE;
-}
-
-static void log_throttle(struct log_io *log_io)
-{
-       if (!log_io->throttle_msg) {
-                log_io->throttle_msg = TRUE;
-               log_it(log_io, "Sending log messages too fast, throttling..",
-                      FALSE);
-       }
-
-       if (log_io->io == NULL) {
-               i_assert(to != NULL);
-               return;
-       }
-
-       io_remove(&log_io->io);
-        throttle_count++;
-
-       if (to == NULL)
-               to = timeout_add(1000, log_throttle_timeout, NULL);
-}
-
-static void log_read_callback(struct log_io *log_io)
-{
-       (void)log_read(log_io);
-}
-
-static void log_unthrottle(struct log_io *log_io)
-{
-       if (log_io->io != NULL)
-               return;
-
-       if (--throttle_count == 0 && to != NULL)
-               timeout_remove(&to);
-       log_io->io = io_add(i_stream_get_fd(log_io->stream),
-                           IO_READ, log_read_callback, log_io);
-}
-
-static int log_it(struct log_io *log_io, const char *line, bool continues)
-{
-       struct child_process *process;
-       const char *prefix;
-       enum log_type log_type;
-
-       if (log_io->next_log_type == '\0') {
-               if (line[0] == 1 && line[1] != '\0') {
-                       /* our internal protocol.
-                          \001 + log_type */
-                       log_io->next_log_type = line[1];
-                       line += 2;
-               } else {
-                       log_io->next_log_type = 'E';
-               }
-       }
-
-       prefix = log_io->prefix != NULL ? log_io->prefix : "";
-       switch (log_io->next_log_type) {
-       case 'I':
-               log_type = LOG_TYPE_INFO;
-               break;
-       case 'W':
-               log_type = LOG_TYPE_WARNING;
-               break;
-       case 'E':
-               log_type = LOG_TYPE_ERROR;
-               break;
-       case 'F':
-       case 'P':
-               log_type = log_io->next_log_type == 'F' ?
-                       LOG_TYPE_FATAL : LOG_TYPE_PANIC;
-               process = child_process_lookup(log_io->pid);
-               if (process != NULL)
-                       process->seen_fatal = TRUE;
-               break;
-       case 'O':
-               /* logging option. ignore unknown ones. */
-               if (strncmp(line, "ip=", 3) == 0) {
-                       process = child_process_lookup(log_io->pid);
-                       if (process != NULL &&
-                           (process->allow_change_ip ||
-                            process->ip.family == 0)) {
-                               if (process->ip.family != 0)
-                                       process->ip_changed = TRUE;
-                               net_addr2ip(line + 3, &process->ip);
-                       }
-               }
-               log_io->next_log_type = '\0';
-               return 1;
-       default:
-               log_type = LOG_TYPE_ERROR;
-               break;
-       }
-       i_log_type(log_type, "%s%s", prefix, line);
-
-       if (!continues)
-               log_io->next_log_type = '\0';
-
-       if (++log_io->log_counter > log_io->max_lines_per_sec &&
-           !log_io->destroying) {
-               log_throttle(log_io);
-               return 0;
-       }
-       return 1;
-}
-
-static int log_read(struct log_io *log_io)
-{
-       const unsigned char *data;
-       const char *line;
-       size_t size;
-       int ret;
-
-       if (!log_write_pending(log_io))
-               return 0;
-
-       ret = i_stream_read(log_io->stream);
-       if (ret < 0) {
-               if (ret == -1) {
-                       /* closed */
-                       log_unref(log_io);
-                       return -1;
-               }
-
-               /* buffer full. treat it as one line */
-               data = i_stream_get_data(log_io->stream, &size);
-               line = t_strndup(data, size);
-               i_stream_skip(log_io->stream, size);
-
-               if (!log_it(log_io, line, TRUE))
-                       return 0;
-       }
-
-       if (!log_write_pending(log_io))
-               return 0;
-
-       if (log_io->log_counter < log_io->max_lines_per_sec)
-               log_unthrottle(log_io);
-       return 0;
-}
-
-int log_create_pipe(struct log_io **log_r, unsigned int max_lines_per_sec)
-{
-       struct log_io *log_io;
-       int fd[2];
-
-       if (pipe(fd) < 0) {
-               i_error("pipe() failed: %m");
-               return -1;
-       }
-
-       fd_set_nonblock(fd[0], TRUE);
-       fd_close_on_exec(fd[0], TRUE);
-       fd_close_on_exec(fd[1], TRUE);
-
-       log_io = i_new(struct log_io, 1);
-       log_io->refcount = 1;
-       log_io->pid = (pid_t)-1;
-       log_io->stream = i_stream_create_fd(fd[0], 1024, TRUE);
-       log_io->max_lines_per_sec =
-               max_lines_per_sec != 0 ? max_lines_per_sec : (unsigned int)-1;
-
-       throttle_count++;
-        log_unthrottle(log_io);
-
-       if (log_ios != NULL)
-               log_ios->prev = log_io;
-       log_io->next = log_ios;
-       log_ios = log_io;
-
-       if (log_r != NULL)
-               *log_r = log_io;
-       return fd[1];
-}
-
-void log_set_prefix(struct log_io *log, const char *prefix)
-{
-       i_free(log->prefix);
-       log->prefix = i_strdup(prefix);
-}
-
-void log_set_pid(struct log_io *log, pid_t pid)
-{
-       log->pid = pid;
-}
-
-void log_ref(struct log_io *log_io)
-{
-       log_io->refcount++;
-}
-
-static void log_close(struct log_io *log_io)
-{
-       const unsigned char *data;
-       size_t size;
-
-       if (log_io->destroying)
-               return;
-
-       /* if there was something in buffer, write it */
-       log_io->destroying = TRUE;
-       (void)log_write_pending(log_io);
-
-       /* write partial data as well */
-       data = i_stream_get_data(log_io->stream, &size);
-       if (size != 0) {
-               T_BEGIN {
-                       log_it(log_io, t_strndup(data, size), TRUE);
-               } T_END;
-       }
-
-       if (log_io == log_ios)
-               log_ios = log_io->next;
-       else
-               log_io->prev->next = log_io->next;
-       if (log_io->next != NULL)
-               log_io->next->prev = log_io->prev;
-
-       if (log_io->io != NULL)
-               io_remove(&log_io->io);
-       else
-               throttle_count--;
-       i_stream_destroy(&log_io->stream);
-}
-
-void log_unref(struct log_io *log_io)
-{
-       i_assert(log_io->refcount > 0);
-
-       log_close(log_io);
-
-       if (--log_io->refcount > 0)
-               return;
-
-       i_free(log_io->prefix);
-       i_free(log_io);
-}
-
-static void log_throttle_timeout(void *context ATTR_UNUSED)
-{
-       struct log_io *log, *next;
-       unsigned int left = throttle_count;
-
-       i_assert(left > 0);
-
-       for (log = log_ios; log != NULL; log = next) {
-               next = log->next;
-
-               if (log->io == NULL) {
-                       if (log_write_pending(log))
-                               log_unthrottle(log);
-
-                       if (--left == 0)
-                               break;
-               }
-       }
-}
-
-void log_init(void)
-{
-       log_ios = NULL;
-        throttle_count = 0;
-       to = NULL;
-}
-
-void log_deinit(void)
-{
-       struct log_io *next;
-
-       while (log_ios != NULL) {
-               next = log_ios->next;
-               /* do one final log read in case there's still something
-                  waiting */
-               if (log_read(log_ios) == 0)
-                       log_unref(log_ios);
-               log_ios = next;
-       }
-
-       if (to != NULL)
-               timeout_remove(&to);
-}
diff --git a/src/master/log.h b/src/master/log.h
deleted file mode 100644 (file)
index ac490d3..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-#ifndef LOG_H
-#define LOG_H
-
-struct log_io;
-
-int log_create_pipe(struct log_io **log_r, unsigned int max_lines_per_sec);
-void log_set_prefix(struct log_io *log, const char *prefix);
-void log_set_pid(struct log_io *log, pid_t pid);
-
-void log_ref(struct log_io *log_io);
-void log_unref(struct log_io *log_io);
-
-void log_init(void);
-void log_deinit(void);
-
-#endif
diff --git a/src/master/login-process.c b/src/master/login-process.c
deleted file mode 100644 (file)
index 9a4efff..0000000
+++ /dev/null
@@ -1,893 +0,0 @@
-/* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
-
-#include "common.h"
-#include "array.h"
-#include "ioloop.h"
-#include "hash.h"
-#include "network.h"
-#include "ostream.h"
-#include "fdpass.h"
-#include "fd-close-on-exec.h"
-#include "env-util.h"
-#include "restrict-access.h"
-#include "restrict-process-size.h"
-#include "dup2-array.h"
-#include "login-process.h"
-#include "auth-process.h"
-#include "mail-process.h"
-#include "master-login-interface.h"
-#include "master-settings.h"
-#include "log.h"
-#include "ssl-init.h"
-
-#include <unistd.h>
-#include <syslog.h>
-#include <sys/stat.h>
-
-#define LOGIN_LIMIT_WARNING_MIN_INTERVAL (60*5)
-
-struct login_process {
-       struct child_process process;
-
-       struct login_group *group;
-       struct login_process *prev_prelogin, *next_prelogin;
-       int refcount;
-
-       pid_t pid;
-       int fd;
-       struct io *io;
-       struct ostream *output;
-       enum master_login_state state;
-
-       unsigned int initialized:1;
-       unsigned int destroyed:1;
-       unsigned int inetd_child:1;
-};
-
-struct login_auth_request {
-       struct login_process *process;
-       unsigned int tag;
-       unsigned int login_tag;
-
-       struct mail_login_request mail_request;
-       unsigned char data[FLEXIBLE_ARRAY_MEMBER];
-};
-
-static unsigned int auth_id_counter, login_pid_counter;
-static struct timeout *to;
-static struct io *io_listen;
-static bool logins_stalled = FALSE;
-
-static struct login_group *login_groups;
-
-static void login_processes_stall(void);
-static void login_process_destroy(struct login_process *p);
-static void login_process_unref(struct login_process *p);
-static bool login_process_init_group(struct login_process *p);
-static void login_processes_start_missing(void *context);
-
-static void login_group_create(struct master_settings *set)
-{
-       struct login_group *group;
-
-       group = i_new(struct login_group, 1);
-       group->refcount = 1;
-       group->set = set;
-       group->mail_process_type = set->protocol == MAIL_PROTOCOL_IMAP ?
-               PROCESS_TYPE_IMAP : PROCESS_TYPE_POP3;
-
-       group->next = login_groups;
-       login_groups = group;
-}
-
-static void login_group_unref(struct login_group *group)
-{
-       i_assert(group->refcount > 0);
-
-       if (--group->refcount > 0)
-               return;
-
-       i_free(group);
-}
-
-void auth_master_callback(const char *user, const char *const *args,
-                         struct login_auth_request *request)
-{
-       struct master_login_reply master_reply;
-       ssize_t ret;
-
-       memset(&master_reply, 0, sizeof(master_reply));
-       if (user == NULL)
-               master_reply.status = MASTER_LOGIN_STATUS_INTERNAL_ERROR;
-       else T_BEGIN {
-               struct login_group *group = request->process->group;
-
-               master_reply.status =
-                       create_mail_process(group->mail_process_type,
-                                           group->set, &request->mail_request,
-                                           user, args, request->data, FALSE,
-                                           &master_reply.mail_pid);
-       } T_END;
-
-       /* reply to login */
-       master_reply.tag = request->login_tag;
-
-       ret = o_stream_send(request->process->output, &master_reply,
-                           sizeof(master_reply));
-       if (ret != sizeof(master_reply)) {
-               if (ret >= 0) {
-                       i_warning("Login process %s transmit buffer full, "
-                                 "killing..", dec2str(request->process->pid));
-               }
-               login_process_destroy(request->process);
-       }
-
-       if (close(request->mail_request.fd) < 0)
-               i_error("close(mail client) failed: %m");
-       login_process_unref(request->process);
-       i_free(request);
-}
-
-static void process_remove_from_prelogin_lists(struct login_process *p)
-{
-       if (p->state != LOGIN_STATE_FULL_PRELOGINS)
-               return;
-
-       if (p->prev_prelogin == NULL)
-               p->group->oldest_prelogin_process = p->next_prelogin;
-       else
-               p->prev_prelogin->next_prelogin = p->next_prelogin;
-
-       if (p->next_prelogin == NULL)
-               p->group->newest_prelogin_process = p->prev_prelogin;
-       else
-               p->next_prelogin->prev_prelogin = p->prev_prelogin;
-
-       p->prev_prelogin = p->next_prelogin = NULL;
-}
-
-static void process_mark_nonlistening(struct login_process *p,
-                                     enum master_login_state new_state)
-{
-       if (p->group == NULL)
-               return;
-
-       if (p->state == LOGIN_STATE_LISTENING)
-               p->group->listening_processes--;
-
-       if (new_state == LOGIN_STATE_FULL_PRELOGINS) {
-               /* add to prelogin list */
-               i_assert(p->state != new_state);
-
-               p->prev_prelogin = p->group->newest_prelogin_process;
-               if (p->group->newest_prelogin_process == NULL)
-                       p->group->oldest_prelogin_process = p;
-               else
-                       p->group->newest_prelogin_process->next_prelogin = p;
-               p->group->newest_prelogin_process = p;
-       } else {
-               process_remove_from_prelogin_lists(p);
-       }
-}
-
-static void process_mark_listening(struct login_process *p)
-{
-       if (p->group == NULL)
-               return;
-
-       if (p->state != LOGIN_STATE_LISTENING)
-               p->group->listening_processes++;
-
-       process_remove_from_prelogin_lists(p);
-}
-
-static void login_process_set_initialized(struct login_process *p)
-{
-       p->initialized = TRUE;
-
-       if (logins_stalled) {
-               /* processes were created successfully */
-               i_info("Created login processes successfully, unstalling");
-
-               logins_stalled = FALSE;
-               timeout_remove(&to);
-               to = timeout_add(1000, login_processes_start_missing, NULL);
-       }
-}
-
-static void
-login_process_set_state(struct login_process *p, enum master_login_state state)
-{
-       if (state == p->state || state > LOGIN_STATE_COUNT ||
-           (state < p->state && p->group->set->login_process_per_connection)) {
-               i_error("login: tried to change state %d -> %d "
-                       "(if you can't login at all, see src/lib/fdpass.c)",
-                       p->state, state);
-               login_process_destroy(p);
-               return;
-       }
-
-       if (state == LOGIN_STATE_LISTENING) {
-               process_mark_listening(p);
-       } else {
-               process_mark_nonlistening(p, state);
-       }
-
-       p->state = state;
-}
-
-static void login_process_groups_create(void)
-{
-       if (master_set->imap != NULL)
-               login_group_create(master_set->imap);
-       if (master_set->pop3 != NULL)
-               login_group_create(master_set->pop3);
-}
-
-static struct login_group *
-login_group_process_find(enum mail_protocol protocol)
-{
-       struct login_group *group;
-
-       if (login_groups == NULL)
-                login_process_groups_create();
-
-       for (group = login_groups; group != NULL; group = group->next) {
-               if (group->set->protocol == protocol)
-                       return group;
-       }
-
-       return NULL;
-}
-
-static bool login_process_read_group(struct login_process *p)
-{
-       struct login_group *group;
-       const char *proto;
-       unsigned char buf[256];
-       enum mail_protocol protocol;
-       unsigned int len;
-       ssize_t ret;
-
-       /* read length */
-       ret = read(p->fd, buf, 1);
-       if (ret != 1)
-               len = 0;
-       else {
-               len = buf[0];
-               if (len >= sizeof(buf)) {
-                       i_error("login: Server name length too large");
-                       return FALSE;
-               }
-
-               ret = read(p->fd, buf, len);
-       }
-
-       if (ret < 0)
-               i_error("login: read() failed: %m");
-       else if (len == 0 || (size_t)ret != len)
-               i_error("login: Server name wasn't sent");
-       else {
-               proto = t_strndup(buf, len);
-               if (strcmp(proto, "imap") == 0)
-                       protocol = MAIL_PROTOCOL_IMAP;
-               else if (strcmp(proto, "pop3") == 0)
-                       protocol = MAIL_PROTOCOL_POP3;
-               else {
-                       i_error("login: Unknown protocol '%s'", proto);
-                       return FALSE;
-               }
-
-               group = login_group_process_find(protocol);
-               if (group == NULL) {
-                       i_error("login: No group for protocol '%s'", proto);
-                       return FALSE;
-               }
-
-               p->group = group;
-               return login_process_init_group(p);
-       }
-       return FALSE;
-}
-
-static int
-login_read_request(struct login_process *p, struct master_login_request *req,
-                  unsigned char data[MASTER_LOGIN_MAX_DATA_SIZE],
-                  int *client_fd_r)
-{
-       struct stat st;
-       ssize_t ret;
-
-       *client_fd_r = -1;
-
-       ret = fd_read(p->fd, req, sizeof(*req), client_fd_r);
-       if (ret >= (ssize_t)sizeof(req->version) &&
-           req->version != MASTER_LOGIN_PROTOCOL_VERSION) {
-               i_error("login: Protocol version mismatch "
-                       "(mixed old and new binaries?)");
-               return -1;
-       }
-
-       if (ret != sizeof(*req)) {
-               if (ret == 0) {
-                       /* disconnected, ie. the login process died */
-               } else if (ret > 0) {
-                       /* request wasn't fully read */
-                       i_error("login: fd_read() returned partial %d",
-                               (int)ret);
-               } else {
-                       if (errno == EAGAIN)
-                               return 0;
-
-                       i_error("login: fd_read() failed: %m");
-               }
-               return -1;
-       }
-
-       if (req->ino == (ino_t)-1) {
-               if (*client_fd_r != -1) {
-                       i_error("login: Notification request sent "
-                               "a file descriptor");
-                       return -1;
-               }
-               return 1;
-       }
-       if (req->data_size != 0) {
-               if (req->data_size > MASTER_LOGIN_MAX_DATA_SIZE) {
-                       i_error("login: Too large data_size sent");
-                       return -1;
-               }
-               /* @UNSAFE */
-               ret = read(p->fd, data, req->data_size);
-               if (ret != req->data_size) {
-                       if (ret == 0) {
-                               /* disconnected */
-                       } else if (ret > 0) {
-                               /* request wasn't fully read */
-                               i_error("login: Data read partially %d/%u",
-                                       (int)ret, req->data_size);
-                       } else {
-                               i_error("login: read(data) failed: %m");
-                       }
-                       return -1;
-               }
-       }
-
-       if (*client_fd_r == -1) {
-               i_error("login: Login request missing a file descriptor");
-               return -1;
-       }
-
-       if (fstat(*client_fd_r, &st) < 0) {
-               i_error("login: fstat(mail client) failed: %m");
-               return -1;
-       }
-       if (st.st_ino != req->ino) {
-               i_error("login: Login request inode mismatch: %s != %s",
-                       dec2str(st.st_ino), dec2str(req->ino));
-               return -1;
-       }
-       return 1;
-}
-
-static void login_process_input(struct login_process *p)
-{
-       struct auth_process *auth_process;
-       struct login_auth_request *authreq;
-       struct master_login_request req;
-       unsigned char data[MASTER_LOGIN_MAX_DATA_SIZE];
-       int client_fd;
-       ssize_t ret;
-
-       if (p->group == NULL) {
-               /* we want to read the group */
-               if (!login_process_read_group(p))
-                       login_process_destroy(p);
-               return;
-       }
-
-       ret = login_read_request(p, &req, data, &client_fd);
-       if (ret == 0)
-               return;
-       if (ret < 0) {
-               if (client_fd != -1) {
-                       if (close(client_fd) < 0)
-                               i_error("login: close(mail client) failed: %m");
-               }
-               login_process_destroy(p);
-               return;
-       }
-
-       if (req.ino == (ino_t)-1) {
-               /* state notification */
-               enum master_login_state state = req.tag;
-
-               if (!p->initialized) {
-                       /* initialization notify */
-                       login_process_set_initialized(p);
-               } else {
-                       /* change "listening for new connections" status */
-                       login_process_set_state(p, state);
-               }
-               return;
-       }
-
-       if (!p->initialized) {
-               i_error("login: trying to log in before initialization");
-               login_process_destroy(p);
-               return;
-       }
-
-       fd_close_on_exec(client_fd, TRUE);
-
-       /* ask the cookie from the auth process */
-       authreq = i_malloc(sizeof(*authreq) + req.data_size);
-       p->refcount++;
-       authreq->process = p;
-       authreq->tag = ++auth_id_counter;
-       authreq->login_tag = req.tag;
-       authreq->mail_request.fd = client_fd;
-       authreq->mail_request.local_ip = req.local_ip;
-       authreq->mail_request.remote_ip = req.remote_ip;
-       authreq->mail_request.cmd_tag_size = req.cmd_tag_size;
-       authreq->mail_request.data_size = req.data_size;
-       memcpy(authreq->data, data, req.data_size);
-
-       auth_process = auth_process_find(req.auth_pid);
-       if (auth_process == NULL) {
-               i_error("login: Authentication process %u doesn't exist",
-                       req.auth_pid);
-               auth_master_callback(NULL, NULL, authreq);
-       } else {
-               auth_process_request(auth_process, p->pid,
-                                    req.auth_id, authreq);
-       }
-}
-
-static struct login_process *
-login_process_new(struct login_group *group, pid_t pid, int fd,
-                 bool inetd_child)
-{
-       struct login_process *p;
-
-       i_assert(pid != 0);
-
-       p = i_new(struct login_process, 1);
-       p->process.type = PROCESS_TYPE_LOGIN;
-       p->group = group;
-       p->refcount = 2; /* once for fd close, another for process exit */
-       p->pid = pid;
-       p->fd = fd;
-       p->inetd_child = inetd_child;
-       p->io = io_add(fd, IO_READ, login_process_input, p);
-       p->output = o_stream_create_fd(fd, sizeof(struct master_login_reply)*10,
-                                      FALSE);
-       if (!inetd_child) {
-               if (!group->set->login_process_per_connection)
-                       p->process.allow_change_ip = TRUE;
-               child_process_add(pid, &p->process);
-       }
-
-       p->state = LOGIN_STATE_LISTENING;
-
-       if (p->group != NULL) {
-               p->group->refcount++;
-               p->group->processes++;
-               p->group->listening_processes++;
-       }
-       return p;
-}
-
-static void login_process_exited(struct login_process *p)
-{
-       if (p->group != NULL)
-               p->group->processes--;
-
-       login_process_unref(p);
-}
-
-static void login_process_destroy(struct login_process *p)
-{
-       if (p->destroyed)
-               return;
-       p->destroyed = TRUE;
-
-       if (!p->initialized)
-               login_processes_stall();
-
-       o_stream_close(p->output);
-       io_remove(&p->io);
-       if (close(p->fd) < 0)
-               i_error("close(login) failed: %m");
-
-       process_mark_nonlistening(p, LOGIN_STATE_FULL_LOGINS);
-
-       if (p->inetd_child)
-               login_process_exited(p);
-       login_process_unref(p);
-}
-
-static void login_process_unref(struct login_process *p)
-{
-       i_assert(p->refcount > 0);
-       if (--p->refcount > 0)
-               return;
-
-       if (p->group != NULL)
-               login_group_unref(p->group);
-
-       o_stream_unref(&p->output);
-       i_free(p);
-}
-
-static void login_process_init_env(struct login_group *group, pid_t pid)
-{
-       struct master_settings *set = group->set;
-       struct restrict_access_settings rset;
-
-       child_process_init_env(group->set);
-
-       /* setup access environment - needs to be done after
-          clean_child_process() since it clears environment. Don't set user
-          parameter since we don't want to call initgroups() for login
-          processes. */
-       restrict_access_init(&rset);
-       rset.uid = set->login_uid;
-       rset.gid = set->server->login_gid;
-       if (set->login_chroot)
-               rset.chroot_dir = set->login_dir;
-       restrict_access_set_env(&rset);
-
-       env_put("DOVECOT_MASTER=1");
-
-       master_settings_export_to_env(group->set);
-       if (ssl_manual_key_password != NULL) {
-               env_put(t_strconcat("SSL_MANUAL_KEY_PASSWORD=",
-                                   ssl_manual_key_password, NULL));
-       }
-
-       env_put(t_strconcat("PROCESS_UID=", dec2str(pid), NULL));
-       if (group->mail_process_type == PROCESS_TYPE_IMAP &&
-           set->imap_generated_capability != NULL) {
-               env_put(t_strconcat("CAPABILITY_STRING=",
-                                   set->imap_generated_capability, NULL));
-       }
-       env_put(t_strconcat("LOGIN_DIR=", set->login_dir, NULL));
-}
-
-static pid_t create_login_process(struct login_group *group)
-{
-       struct log_io *log;
-       const struct listener *listens;
-       unsigned int max_log_lines_per_sec;
-       const char *prefix;
-       pid_t pid;
-       ARRAY_TYPE(dup2) dups;
-       unsigned int i, listen_count = 0, ssl_listen_count = 0;
-       int fd[2], log_fd, cur_fd, tmp_fd;
-
-       if (group->set->login_uid == 0)
-               i_fatal("Login process must not run as root");
-
-       /* create communication to process with a socket pair */
-       if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd) < 0) {
-               i_error("socketpair() failed: %m");
-               return -1;
-       }
-
-       max_log_lines_per_sec =
-               group->set->login_process_per_connection ? 10 : 0;
-       log_fd = log_create_pipe(&log, max_log_lines_per_sec);
-       if (log_fd < 0)
-               pid = -1;
-       else {
-               pid = fork();
-               if (pid < 0)
-                       i_error("fork() failed: %m");
-       }
-
-       if (pid < 0) {
-               (void)close(fd[0]);
-               (void)close(fd[1]);
-               (void)close(log_fd);
-               return -1;
-       }
-
-       if (pid != 0) {
-               /* master */
-               prefix = t_strdup_printf("%s-login: ",
-                               process_names[group->mail_process_type]);
-               log_set_prefix(log, prefix);
-               log_set_pid(log, pid);
-
-               net_set_nonblock(fd[0], TRUE);
-               fd_close_on_exec(fd[0], TRUE);
-               (void)login_process_new(group, pid, fd[0], FALSE);
-               (void)close(fd[1]);
-               (void)close(log_fd);
-               return pid;
-       }
-
-       prefix = t_strdup_printf("master-%s-login: ",
-                                process_names[group->mail_process_type]);
-       log_set_prefix(log, prefix);
-
-       t_array_init(&dups, 16);
-       dup2_append(&dups, null_fd, STDIN_FILENO);
-       /* redirect writes to stdout also to error log. For example OpenSSL
-          can be made to log its debug messages to stdout. */
-       dup2_append(&dups, log_fd, STDOUT_FILENO);
-       dup2_append(&dups, log_fd, STDERR_FILENO);
-       dup2_append(&dups, fd[1], LOGIN_MASTER_SOCKET_FD);
-
-       /* redirect listener fds */
-       cur_fd = LOGIN_MASTER_SOCKET_FD + 1;
-       if (array_is_created(&group->set->listens)) {
-               listens = array_get(&group->set->listens, &listen_count);
-               for (i = 0; i < listen_count; i++, cur_fd++)
-                       dup2_append(&dups, listens[i].fd, cur_fd);
-       }
-
-       if (array_is_created(&group->set->ssl_listens)) {
-               listens = array_get(&group->set->ssl_listens,
-                                   &ssl_listen_count);
-               for (i = 0; i < ssl_listen_count; i++, cur_fd++)
-                       dup2_append(&dups, listens[i].fd, cur_fd);
-       }
-
-       /* make sure we don't leak syslog fd. try to do it as late as possible,
-          but also before dup2()s in case syslog fd is one of them. */
-       closelog();
-
-       if (dup2_array(&dups) < 0)
-               i_fatal("Failed to dup2() fds");
-
-       /* don't close any of these */
-       for (tmp_fd = 0; tmp_fd < cur_fd; tmp_fd++)
-               fd_close_on_exec(tmp_fd, FALSE);
-
-       (void)close(fd[0]);
-       (void)close(fd[1]);
-
-       login_process_init_env(group, getpid());
-
-       env_put(t_strdup_printf("LISTEN_FDS=%u", listen_count));
-       env_put(t_strdup_printf("SSL_LISTEN_FDS=%u", ssl_listen_count));
-
-       restrict_process_size(group->set->login_process_size, (unsigned int)-1);
-
-       client_process_exec(group->set->login_executable, "");
-       i_fatal_status(FATAL_EXEC, "execv(%s) failed: %m",
-                      group->set->login_executable);
-       return -1;
-}
-
-static void
-login_process_destroyed(struct child_process *process,
-                       pid_t pid ATTR_UNUSED, bool abnormal_exit)
-{
-       struct login_process *p = (struct login_process *)process;
-
-       i_assert(!p->inetd_child);
-
-       if (abnormal_exit) {
-               /* don't start raising the process count if they're dying all
-                  the time */
-               if (p->group != NULL)
-                       p->group->wanted_processes_count = 0;
-       }
-
-       login_process_exited(p);
-}
-
-void login_processes_destroy_all(void)
-{
-       struct hash_iterate_context *iter;
-       void *key, *value;
-
-       iter = hash_table_iterate_init(processes);
-       while (hash_table_iterate(iter, &key, &value)) {
-               struct login_process *p = value;
-
-               if (p->process.type == PROCESS_TYPE_LOGIN)
-                       login_process_destroy(p);
-       }
-       hash_table_iterate_deinit(&iter);
-
-       while (login_groups != NULL) {
-               struct login_group *group = login_groups;
-
-               login_groups = group->next;
-               login_group_unref(group);
-       }
-}
-
-static void login_processes_notify_group(struct login_group *group)
-{
-       struct hash_iterate_context *iter;
-       struct master_login_reply reply;
-       void *key, *value;
-
-       memset(&reply, 0, sizeof(reply));
-
-       iter = hash_table_iterate_init(processes);
-       while (hash_table_iterate(iter, &key, &value)) {
-               struct login_process *p = value;
-
-               if (p->process.type == PROCESS_TYPE_LOGIN && p->group == group)
-                       (void)o_stream_send(p->output, &reply, sizeof(reply));
-       }
-       hash_table_iterate_deinit(&iter);
-}
-
-static int login_group_start_missings(struct login_group *group)
-{
-       if (group->set->login_process_per_connection &&
-           group->processes >= group->set->login_max_processes_count &&
-           group->listening_processes == 0) {
-               /* destroy the oldest listening process. non-listening
-                  processes are logged in users who we don't want to kick out
-                  because someone's started flooding */
-               if (group->oldest_prelogin_process != NULL &&
-                   group->oldest_prelogin_process->initialized)
-                       login_process_destroy(group->oldest_prelogin_process);
-               else if (ioloop_time - group->last_limit_warning >
-                        LOGIN_LIMIT_WARNING_MIN_INTERVAL) {
-                       group->last_limit_warning = ioloop_time;
-                       i_warning("All login processes are in use. You may "
-                                 "need to increase login_max_processes_count");
-               }
-       }
-
-       /* we want to respond fast when multiple clients are connecting
-          at once, but we also want to prevent fork-bombing. use the
-          same method as apache: check once a second if we need new
-          processes. if yes and we've used all the existing processes,
-          double their amount (unless we've hit the high limit).
-          Then for each second that didn't use all existing processes,
-          drop the max. process count by one. */
-       if (group->wanted_processes_count < group->set->login_processes_count) {
-               group->wanted_processes_count =
-                       group->set->login_processes_count;
-       } else if (group->listening_processes == 0)
-               group->wanted_processes_count *= 2;
-       else if (group->wanted_processes_count >
-                group->set->login_processes_count)
-               group->wanted_processes_count--;
-
-       while (group->listening_processes < group->wanted_processes_count &&
-              group->processes < group->set->login_max_processes_count) {
-               if (create_login_process(group) < 0)
-                       return -1;
-       }
-
-       if (group->listening_processes == 0 &&
-           !group->set->login_process_per_connection) {
-               /* we've reached our limit. notify the processes to start
-                  listening again which makes them kill some of their
-                  oldest clients when accepting the next connection */
-               login_processes_notify_group(group);
-       }
-       return 0;
-}
-
-static void login_processes_stall(void)
-{
-       if (logins_stalled || IS_INETD())
-               return;
-
-       i_error("Temporary failure in creating login processes, "
-               "slowing down for now");
-       logins_stalled = TRUE;
-
-       timeout_remove(&to);
-       to = timeout_add(60*1000, login_processes_start_missing, NULL);
-}
-
-static void
-login_processes_start_missing(void *context ATTR_UNUSED)
-{
-       struct login_group *group;
-
-       if (!have_initialized_auth_processes) {
-               /* don't create login processes before at least one auth
-                  process has finished initializing */
-               return;
-       }
-
-       if (login_groups == NULL)
-               login_process_groups_create();
-
-       for (group = login_groups; group != NULL; group = group->next) {
-               if (login_group_start_missings(group) < 0) {
-                       login_processes_stall();
-                       return;
-               }
-       }
-}
-
-static int login_process_send_env(struct login_process *p)
-{
-       extern char **environ;
-       char **env;
-       ssize_t len;
-       int ret = 0;
-
-       /* this will clear our environment. luckily we don't need it. */
-       login_process_init_env(p->group, p->pid);
-
-       for (env = environ; *env != NULL; env++) {
-               len = strlen(*env);
-
-               if (o_stream_send(p->output, *env, len) != len ||
-                   o_stream_send(p->output, "\n", 1) != 1) {
-                       ret = -1;
-                       break;
-               }
-       }
-
-       if (ret == 0 && o_stream_send(p->output, "\n", 1) != 1)
-               ret = -1;
-
-       env_clean();
-       return ret;
-}
-
-static bool login_process_init_group(struct login_process *p)
-{
-       p->group->refcount++;
-       p->group->processes++;
-       p->group->listening_processes++;
-
-       if (login_process_send_env(p) < 0) {
-               i_error("login: Couldn't send environment");
-               return FALSE;
-       }
-
-       return TRUE;
-}
-
-static void inetd_login_accept(void *context ATTR_UNUSED)
-{
-        struct login_process *p;
-       int fd;
-
-       fd = net_accept(inetd_login_fd, NULL, NULL);
-       if (fd < 0) {
-               if (fd < -1)
-                       i_error("accept(inetd_login_fd) failed: %m");
-       } else {
-               net_set_nonblock(fd, TRUE);
-               fd_close_on_exec(fd, TRUE);
-
-               p = login_process_new(NULL, ++login_pid_counter, fd, TRUE);
-               p->initialized = TRUE;
-       }
-}
-
-void login_processes_init(void)
-{
-       auth_id_counter = 0;
-        login_pid_counter = 0;
-       login_groups = NULL;
-
-       child_process_set_destroy_callback(PROCESS_TYPE_LOGIN,
-                                          login_process_destroyed);
-
-       if (!IS_INETD()) {
-               to = timeout_add(1000, login_processes_start_missing, NULL);
-               io_listen = NULL;
-       } else {
-               to = NULL;
-               io_listen = io_add(inetd_login_fd, IO_READ,
-                                  inetd_login_accept, NULL);
-       }
-}
-
-void login_processes_deinit(void)
-{
-       if (to != NULL)
-               timeout_remove(&to);
-       if (io_listen != NULL)
-               io_remove(&io_listen);
-}
diff --git a/src/master/login-process.h b/src/master/login-process.h
deleted file mode 100644 (file)
index f567ac2..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-#ifndef LOGIN_PROCESS_H
-#define LOGIN_PROCESS_H
-
-#include "child-process.h"
-
-struct login_group {
-       struct login_group *next;
-       int refcount;
-
-       enum process_type mail_process_type;
-       struct master_settings *set;
-
-       unsigned int processes;
-       unsigned int listening_processes;
-       unsigned int wanted_processes_count;
-       time_t last_limit_warning;
-
-       /* if login_process_per_connection=yes this contains the list of
-          processes that are in LOGIN_STATE_FULL_PRELOGINS state */
-       struct login_process *oldest_prelogin_process;
-       struct login_process *newest_prelogin_process;
-};
-
-void login_processes_destroy_all(void);
-
-void login_processes_init(void);
-void login_processes_deinit(void);
-
-#endif
diff --git a/src/master/mail-process.c b/src/master/mail-process.c
deleted file mode 100644 (file)
index a4e6845..0000000
+++ /dev/null
@@ -1,702 +0,0 @@
-/* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
-
-#include "common.h"
-#include "array.h"
-#include "hash.h"
-#include "fd-close-on-exec.h"
-#include "eacces-error.h"
-#include "env-util.h"
-#include "base64.h"
-#include "str.h"
-#include "network.h"
-#include "mountpoint.h"
-#include "restrict-access.h"
-#include "restrict-process-size.h"
-#include "home-expand.h"
-#include "var-expand.h"
-#include "settings-parser.h"
-#include "mail-process.h"
-#include "login-process.h"
-#include "log.h"
-
-#include <stdlib.h>
-#include <unistd.h>
-#include <grp.h>
-#include <syslog.h>
-#include <sys/stat.h>
-
-#ifdef HAVE_SYS_RESOURCE_H
-#  include <sys/resource.h>
-#endif
-
-/* Timeout chdir() completely after this many seconds */
-#define CHDIR_TIMEOUT 30
-/* Give a warning about chdir() taking a while if it took longer than this
-   many seconds to finish. */
-#define CHDIR_WARN_SECS 10
-
-struct mail_process_group {
-       /* process.type + user + remote_ip identifies this process group */
-       struct child_process process;
-       char *user;
-       struct ip_addr remote_ip;
-
-       /* processes array acts also as refcount */
-       ARRAY_DEFINE(processes, pid_t);
-};
-
-/* type+user -> struct mail_process_group */
-static struct hash_table *mail_process_groups;
-static unsigned int mail_process_count = 0;
-
-static unsigned int mail_process_group_hash(const void *p)
-{
-       const struct mail_process_group *group = p;
-
-       return str_hash(group->user) ^ group->process.type ^
-               net_ip_hash(&group->remote_ip);
-}
-
-static int mail_process_group_cmp(const void *p1, const void *p2)
-{
-       const struct mail_process_group *group1 = p1, *group2 = p2;
-       int ret;
-
-       ret = strcmp(group1->user, group2->user);
-       if (ret == 0)
-               ret = group1->process.type - group2->process.type;
-       if (ret == 0 && !net_ip_compare(&group1->remote_ip, &group2->remote_ip))
-               ret = -1;
-       return ret;
-}
-
-static struct mail_process_group *
-mail_process_group_lookup(enum process_type type, const char *user,
-                         const struct ip_addr *ip)
-{
-       struct mail_process_group lookup_group;
-
-       lookup_group.process.type = type;
-       lookup_group.user = t_strdup_noconst(user);
-       lookup_group.remote_ip = *ip;
-
-       return hash_table_lookup(mail_process_groups, &lookup_group);
-}
-
-static struct mail_process_group *
-mail_process_group_create(enum process_type type, const char *user,
-                         const struct ip_addr *ip)
-{
-       struct mail_process_group *group;
-
-       group = i_new(struct mail_process_group, 1);
-       group->process.type = type;
-       group->user = i_strdup(user);
-       group->remote_ip = *ip;
-
-       i_array_init(&group->processes, 10);
-       hash_table_insert(mail_process_groups, group, group);
-       return group;
-}
-
-static void
-mail_process_group_add(struct mail_process_group *group, pid_t pid)
-{
-       mail_process_count++;
-       array_append(&group->processes, &pid, 1);
-       child_process_add(pid, &group->process);
-}
-
-static void mail_process_group_free(struct mail_process_group *group)
-{
-       array_free(&group->processes);
-       i_free(group->user);
-       i_free(group);
-}
-
-static bool validate_uid_gid(struct master_settings *set, uid_t uid, gid_t gid,
-                            const char *user)
-{
-       if (uid == 0) {
-               i_error("user %s: Logins with UID 0 not permitted", user);
-               return FALSE;
-       }
-
-       if (set->login_uid == uid && master_uid != uid) {
-               i_error("user %s: Logins with login_user's UID %s "
-                       "not permitted (see http://wiki.dovecot.org/UserIds).",
-                       user, dec2str(uid));
-               return FALSE;
-       }
-
-       if (uid < (uid_t)set->first_valid_uid ||
-           (set->last_valid_uid != 0 && uid > (uid_t)set->last_valid_uid)) {
-               i_error("user %s: Logins with UID %s not permitted "
-                       "(see first_valid_uid in config file).",
-                       user, dec2str(uid));
-               return FALSE;
-       }
-
-       if (gid < (gid_t)set->first_valid_gid ||
-           (set->last_valid_gid != 0 && gid > (gid_t)set->last_valid_gid)) {
-               i_error("user %s: Logins for users with primary group ID %s "
-                       "not permitted (see first_valid_gid in config file).",
-                       user, dec2str(gid));
-               return FALSE;
-       }
-
-       return TRUE;
-}
-
-static bool validate_chroot(struct master_settings *set, const char *dir)
-{
-       const char *const *chroot_dirs;
-
-       if (*dir == '\0')
-               return FALSE;
-
-       if (*set->valid_chroot_dirs == '\0')
-               return FALSE;
-
-       chroot_dirs = t_strsplit(set->valid_chroot_dirs, ":");
-       while (*chroot_dirs != NULL) {
-               if (**chroot_dirs != '\0' &&
-                   strncmp(dir, *chroot_dirs, strlen(*chroot_dirs)) == 0)
-                       return TRUE;
-               chroot_dirs++;
-       }
-
-       return FALSE;
-}
-
-static const struct var_expand_table *
-get_var_expand_table(const char *protocol,
-                    const char *user, const char *home,
-                    const char *local_ip, const char *remote_ip,
-                    pid_t pid, uid_t uid)
-{
-#define VAR_EXPAND_HOME_IDX 4
-       static struct var_expand_table static_tab[] = {
-               { 'u', NULL, "user" },
-               { 'n', NULL, "username" },
-               { 'd', NULL, "domain" },
-               { 's', NULL, "service" },
-               { 'h', NULL, "home" },
-               { 'l', NULL, "lip" },
-               { 'r', NULL, "rip" },
-               { 'p', NULL, "pid" },
-               { 'i', NULL, "uid" },
-               { '\0', NULL, NULL }
-       };
-       struct var_expand_table *tab;
-
-       tab = t_malloc(sizeof(static_tab));
-       memcpy(tab, static_tab, sizeof(static_tab));
-
-       tab[0].value = user;
-       tab[1].value = user == NULL ? NULL : t_strcut(user, '@');
-       tab[2].value = user == NULL ? NULL : strchr(user, '@');
-       if (tab[2].value != NULL) tab[2].value++;
-       tab[3].value = t_str_ucase(protocol);
-       tab[VAR_EXPAND_HOME_IDX].value = home;
-       tab[5].value = local_ip;
-       tab[6].value = remote_ip;
-       tab[7].value = dec2str(pid);
-       tab[8].value = dec2str(uid);
-
-       return tab;
-}
-
-static void mail_process_set_environment(struct master_settings *set,
-                                        bool dump_capability)
-{
-       if (dump_capability) {
-               /* the only settings we need to have are what plugins to load
-                  and from where. the rest will only make the dump-capability
-                  more likely to fail. */
-               const char *const *sets;
-               unsigned int i, count;
-
-               env_put("MAIL=raw:/tmp");
-               sets = array_get(&set->all_settings, &count);
-               for (i = 0; i < count; i++) {
-                       if (strncmp(sets[i], "mail_plugin", 11) == 0)
-                               env_put(sets[i]);
-               }
-       } else {
-               /* we don't know all the settings, so since we can't expand all
-                  of them just let the mail process expand all of them
-                  internally. */
-               master_settings_export_to_env(set);
-       }
-}
-
-void mail_process_exec(const char *protocol, const char **args)
-{
-       const struct var_expand_table *var_expand_table;
-       struct master_settings *set;
-       const char *executable;
-
-       if (strcmp(protocol, "ext") == 0) {
-               /* external binary. section contains path for it. */
-               if (*args == NULL)
-                       i_fatal("External binary parameter not given");
-               set = master_set->defaults;
-               executable = *args;
-       } else {
-               if (strcmp(protocol, "imap") == 0)
-                       set = master_set->imap;
-               else if (strcmp(protocol, "pop3") == 0)
-                       set = master_set->pop3;
-               else
-                       i_fatal("Unknown protocol: '%s'", protocol);
-               executable = set->mail_executable;
-               args = NULL;
-       }
-
-       var_expand_table =
-               get_var_expand_table(protocol, getenv("USER"), getenv("HOME"),
-                                    getenv("TCPLOCALIP"),
-                                    getenv("TCPREMOTEIP"),
-                                    getpid(), geteuid());
-
-       /* set up logging */
-       env_put(t_strconcat("LOG_TIMESTAMP=", set->log_timestamp, NULL));
-       if (*set->log_path == '\0')
-               env_put("USE_SYSLOG=1");
-       else
-               env_put(t_strconcat("LOGFILE=", set->log_path, NULL));
-       if (*set->info_log_path != '\0')
-               env_put(t_strconcat("INFOLOGFILE=", set->info_log_path, NULL));
-       if (*set->mail_log_prefix != '\0') {
-               string_t *str = t_str_new(256);
-
-               str_append(str, "LOG_PREFIX=");
-               var_expand(str, set->mail_log_prefix, var_expand_table);
-               env_put(str_c(str));
-       }
-
-       mail_process_set_environment(set, FALSE);
-       if (args == NULL)
-               client_process_exec(executable, "");
-       else
-               client_process_exec_argv(executable, args);
-
-       i_fatal_status(FATAL_EXEC, "execv(%s) failed: %m", executable);
-}
-
-enum master_login_status
-create_mail_process(enum process_type process_type, struct master_settings *set,
-                   const struct mail_login_request *request,
-                   const char *user, const char *const *args,
-                   const unsigned char *data, bool dump_capability,
-                   pid_t *pid_r)
-{
-       const struct var_expand_table *var_expand_table;
-       const char *p, *addr, *chroot_dir, *home_dir, *full_home_dir;
-       const char *system_groups_user, *master_user, *key;
-       struct mail_process_group *process_group;
-       struct restrict_access_settings rset;
-       char title[1024];
-       struct log_io *log;
-       string_t *str, *expanded_vars;
-       pid_t pid;
-       uid_t uid;
-       gid_t gid;
-       ARRAY_DEFINE(extra_args, const char *);
-       unsigned int i, len, count, left, process_count, throttle;
-       int ret, log_fd, nice_value, chdir_errno;
-       bool home_given;
-
-       i_assert(process_type == PROCESS_TYPE_IMAP ||
-                process_type == PROCESS_TYPE_POP3);
-
-       if (mail_process_count == set->max_mail_processes) {
-               i_error("Maximum number of mail processes exceeded "
-                       "(see max_mail_processes setting)");
-               return MASTER_LOGIN_STATUS_INTERNAL_ERROR;
-       }
-
-       t_array_init(&extra_args, 16);
-       home_dir = chroot_dir = system_groups_user = "";
-       master_user = NULL;
-       uid = (uid_t)-1; gid = (gid_t)-1; nice_value = 0;
-       home_given = FALSE;
-       for (; *args != NULL; args++) {
-               if (strncmp(*args, "home=", 5) == 0) {
-                       home_dir = *args + 5;
-                       home_given = TRUE;
-               } else if (strncmp(*args, "mail=", 5) == 0) {
-                       const char *arg;
-
-                       arg = t_strconcat("mail_location=", *args + 5, NULL);
-                       array_append(&extra_args, &arg, 1);
-               } else if (strncmp(*args, "chroot=", 7) == 0)
-                       chroot_dir = *args + 7;
-               else if (strncmp(*args, "nice=", 5) == 0)
-                       nice_value = atoi(*args + 5);
-               else if (strncmp(*args, "system_groups_user=", 19) == 0)
-                       system_groups_user = *args + 19;
-               else if (strncmp(*args, "uid=", 4) == 0) {
-                       if (uid != (uid_t)-1) {
-                               i_error("uid specified multiple times for %s",
-                                       user);
-                               return MASTER_LOGIN_STATUS_INTERNAL_ERROR;
-                       }
-                       uid = (uid_t)strtoul(*args + 4, NULL, 10);
-               } else if (strncmp(*args, "gid=", 4) == 0) {
-                       gid = (gid_t)strtoul(*args + 4, NULL, 10);
-               } else if (strncmp(*args, "master_user=", 12) == 0) {
-                       const char *arg = *args;
-
-                       master_user = arg + 12;
-                       array_append(&extra_args, &arg, 1);
-               } else {
-                       const char *arg = *args;
-                       array_append(&extra_args, &arg, 1);
-               }
-       }
-
-       /* check process limit for this user, but not if this is a master
-          user login. */
-       process_group = dump_capability ? NULL :
-               mail_process_group_lookup(process_type, user,
-                                         &request->remote_ip);
-       process_count = process_group == NULL ? 0 :
-               array_count(&process_group->processes);
-       if (process_count >= set->mail_max_userip_connections &&
-           set->mail_max_userip_connections != 0 &&
-           master_user == NULL)
-               return MASTER_LOGIN_STATUS_MAX_CONNECTIONS;
-
-       /* if uid/gid wasn't returned, use the defaults */
-       if (uid == (uid_t)-1) {
-               uid = set->mail_uid_t;
-               if (uid == (uid_t)-1) {
-                       i_error("User %s is missing UID (see mail_uid setting)",
-                               user);
-                       return MASTER_LOGIN_STATUS_INTERNAL_ERROR;
-               }
-       }
-       if (gid == (gid_t)-1) {
-               gid = set->mail_gid_t;
-               if (gid == (gid_t)-1) {
-                       i_error("User %s is missing GID (see mail_gid setting)",
-                               user);
-                       return MASTER_LOGIN_STATUS_INTERNAL_ERROR;
-               }
-       }
-
-       if (*chroot_dir == '\0' && *set->valid_chroot_dirs != '\0' &&
-           (p = strstr(home_dir, "/./")) != NULL) {
-               /* wu-ftpd like <chroot>/./<home> - check only if there's even
-                  a possibility of using them (non-empty valid_chroot_dirs)*/
-               chroot_dir = t_strdup_until(home_dir, p);
-               home_dir = p + 2;
-       } else if (*chroot_dir != '\0' && *home_dir != '/') {
-               /* home directories should never be relative, but force this
-                  with chroots. */
-               home_dir = t_strconcat("/", home_dir, NULL);
-       }
-
-       if (!dump_capability) {
-               if (!validate_uid_gid(set, uid, gid, user))
-                       return MASTER_LOGIN_STATUS_INTERNAL_ERROR;
-       }
-
-       if (*chroot_dir != '\0') {
-               if (!validate_chroot(set, chroot_dir)) {
-                       i_error("Invalid chroot directory '%s' (user %s) "
-                               "(see valid_chroot_dirs setting)",
-                               chroot_dir, user);
-                       return MASTER_LOGIN_STATUS_INTERNAL_ERROR;
-               }
-       } else if (*set->mail_chroot != '\0') {
-               /* mail_chroot setting's value doesn't need to be in
-                  valid_chroot_dirs. */
-               chroot_dir = set->mail_chroot;
-       }
-       if (*chroot_dir != '\0' && set->mail_drop_priv_before_exec) {
-               i_error("Can't chroot to directory '%s' (user %s) "
-                       "with mail_drop_priv_before_exec=yes",
-                       chroot_dir, user);
-               return MASTER_LOGIN_STATUS_INTERNAL_ERROR;
-       }
-       len = strlen(chroot_dir);
-       if (len > 2 && strcmp(chroot_dir + len - 2, "/.") == 0 &&
-           strncmp(home_dir, chroot_dir, len - 2) == 0) {
-               /* strip chroot dir from home dir */
-               home_dir += len - 2;
-       }
-
-       if (!dump_capability) {
-               throttle = set->mail_debug ? 0 :
-                       set->mail_log_max_lines_per_sec;
-               log_fd = log_create_pipe(&log, throttle);
-               if (log_fd == -1)
-                       return MASTER_LOGIN_STATUS_INTERNAL_ERROR;
-       } else {
-               log = NULL;
-               log_fd = dup(STDERR_FILENO);
-               if (log_fd == -1) {
-                       i_error("dup() failed: %m");
-                       return MASTER_LOGIN_STATUS_INTERNAL_ERROR;
-               }
-               fd_close_on_exec(log_fd, TRUE);
-       }
-
-       pid = fork();
-       if (pid < 0) {
-               i_error("fork() failed: %m");
-               (void)close(log_fd);
-               return MASTER_LOGIN_STATUS_INTERNAL_ERROR;
-       }
-
-       var_expand_table =
-               get_var_expand_table(process_names[process_type],
-                                    user, home_given ? home_dir : NULL,
-                                    net_ip2addr(&request->local_ip),
-                                    net_ip2addr(&request->remote_ip),
-                                    pid != 0 ? pid : getpid(), uid);
-       str = t_str_new(128);
-
-       if (pid != 0) {
-               /* master */
-               var_expand(str, set->mail_log_prefix, var_expand_table);
-
-               if (!dump_capability) {
-                       log_set_prefix(log, str_c(str));
-                       log_set_pid(log, pid);
-                       if (process_group == NULL) {
-                               process_group = mail_process_group_create(
-                                                       process_type, user,
-                                                       &request->remote_ip);
-                       }
-                       mail_process_group_add(process_group, pid);
-               }
-               (void)close(log_fd);
-               *pid_r = pid;
-               return MASTER_LOGIN_STATUS_OK;
-       }
-
-#ifdef HAVE_SETPRIORITY
-       if (nice_value != 0) {
-               if (setpriority(PRIO_PROCESS, 0, nice_value) < 0)
-                       i_error("setpriority(%d) failed: %m", nice_value);
-       }
-#endif
-
-       if (!dump_capability) {
-               str_append(str, "master-");
-               var_expand(str, set->mail_log_prefix, var_expand_table);
-               log_set_prefix(log, str_c(str));
-       }
-
-       child_process_init_env(set);
-
-       /* setup environment - set the most important environment first
-          (paranoia about filling up environment without noticing) */
-       restrict_access_init(&rset);
-       rset.uid = uid;
-       rset.gid = gid;
-       rset.privileged_gid = set->mail_priv_gid_t;
-       rset.extra_groups = set->mail_access_groups;
-       rset.system_groups_user = system_groups_user;
-       rset.first_valid_gid = set->first_valid_gid;
-       rset.last_valid_gid = set->last_valid_gid;
-       rset.chroot_dir = dump_capability ? NULL : chroot_dir;
-       restrict_access_set_env(&rset);
-
-       restrict_process_size(set->mail_process_size, (unsigned int)-1);
-
-       if (dump_capability)
-               env_put("DUMP_CAPABILITY=1");
-
-       if ((*home_dir == '\0' && *chroot_dir == '\0') || dump_capability) {
-               full_home_dir = "";
-               ret = -1;
-       } else {
-               full_home_dir = *chroot_dir == '\0' ? home_dir :
-                       t_strconcat(chroot_dir, home_dir, NULL);
-               /* NOTE: if home directory is NFS-mounted, we might not
-                  have access to it as root. Change the effective UID and GID
-                  temporarily to make it work. */
-               if (uid != master_uid) {
-                       if (setegid(gid) < 0)
-                               i_fatal("setegid(%s) failed: %m", dec2str(gid));
-                       if (seteuid(uid) < 0)
-                               i_fatal("seteuid(%s) failed: %m", dec2str(uid));
-               }
-
-               alarm(CHDIR_TIMEOUT);
-               ret = chdir(full_home_dir);
-               chdir_errno = errno;
-               if ((left = alarm(0)) < CHDIR_TIMEOUT - CHDIR_WARN_SECS) {
-                       i_warning("chdir(%s) blocked for %u secs",
-                                 full_home_dir, CHDIR_TIMEOUT - left);
-               }
-
-               /* Change UID back. No need to change GID back, it doesn't
-                  really matter. */
-               if (uid != master_uid && seteuid(master_uid) < 0)
-                       i_fatal("seteuid(%s) failed: %m", dec2str(master_uid));
-
-               /* If user's home directory doesn't exist and we're not
-                  trying to chroot anywhere, fallback to /tmp as the mails
-                  could be stored elsewhere. The ENOTDIR check is mostly for
-                  /dev/null home directory. */
-               if (ret < 0 && (*chroot_dir != '\0' ||
-                               !(ENOTFOUND(chdir_errno) ||
-                                 chdir_errno == EINTR))) {
-                       errno = chdir_errno;
-                       if (errno != EACCES) {
-                               i_fatal("chdir(%s) failed with uid %s: %m",
-                                       full_home_dir, dec2str(uid));
-                       } else {
-                               i_fatal("%s", eacces_error_get("chdir",
-                                                              full_home_dir));
-                       }
-               }
-       }
-       if (ret < 0) {
-               /* We still have to change to some directory where we have
-                  rx-access. /tmp should exist everywhere. */
-               if (chdir("/tmp") < 0)
-                       i_fatal("chdir(/tmp) failed: %m");
-       }
-
-       mail_process_set_environment(set, dump_capability);
-
-       /* extra args. uppercase key value. */
-       expanded_vars = t_str_new(128);
-       str_append(expanded_vars, "VARS_EXPANDED=");
-       args = array_get(&extra_args, &count);
-       for (i = 0; i < count; i++) {
-               if (*args[i] == '=') {
-                       /* Should be caught by dovecot-auth already */
-                       i_fatal("Userdb returned data with empty key (%s)",
-                               args[i]);
-               }
-               p = strchr(args[i], '=');
-               if (p == NULL) {
-                       /* boolean */
-                       key = args[i];
-                       env_put(t_strconcat(t_str_ucase(key), "=1", NULL));
-
-               } else {
-                       /* key=value */
-                       key = t_strdup_until(args[i], p);
-                       env_put(t_strconcat(t_str_ucase(key), p, NULL));
-               }
-               str_append(expanded_vars, key);
-               str_append_c(expanded_vars, ' ');
-       }
-       env_put(str_c(expanded_vars));
-
-       env_put("LOGGED_IN=1");
-       if (*home_dir != '\0')
-               env_put(t_strconcat("HOME=", home_dir, NULL));
-       env_put(t_strconcat("USER=", user, NULL));
-
-       addr = net_ip2addr(&request->remote_ip);
-       env_put(t_strconcat("IP=", addr, NULL));
-       env_put(t_strconcat("LOCAL_IP=", net_ip2addr(&request->local_ip), NULL));
-
-       i_assert(request->cmd_tag_size <= request->data_size);
-       if (request->cmd_tag_size > 0) {
-               env_put(t_strconcat("IMAPLOGINTAG=",
-                       t_strndup(data, request->cmd_tag_size), NULL));
-       }
-
-       if (request->data_size > request->cmd_tag_size) {
-               str_truncate(str, 0);
-               str_append(str, "CLIENT_INPUT=");
-               base64_encode(data + request->cmd_tag_size,
-                             request->data_size - request->cmd_tag_size, str);
-               env_put(str_c(str));
-       }
-
-       if (!set->verbose_proctitle)
-               title[0] = '\0';
-       else {
-               if (addr == NULL)
-                       addr = "??";
-
-               i_snprintf(title, sizeof(title), "[%s %s]", user, addr);
-       }
-
-       /* make sure we don't leak syslog fd. try to do it as late as possible,
-          but also before dup2()s in case syslog fd is one of them. */
-       closelog();
-
-       /* move the client socket into stdin and stdout fds, log to stderr */
-       if (dup2(request->fd, 0) < 0)
-               i_fatal("dup2(stdin) failed: %m");
-       if (dup2(request->fd, 1) < 0)
-               i_fatal("dup2(stdout) failed: %m");
-       if (dup2(log_fd, 2) < 0)
-               i_fatal("dup2(stderr) failed: %m");
-
-       for (i = 0; i < 3; i++)
-               fd_close_on_exec(i, FALSE);
-
-       if (set->mail_drop_priv_before_exec) {
-               restrict_access_by_env(home_dir, TRUE);
-               /* privileged GID is now only in saved-GID. if we want to
-                  preserve it across exec, it needs to be temporarily
-                  in effective gid. unfortunately this also causes kernel
-                  to think we're a setgid-program. */
-               restrict_access_use_priv_gid();
-       }
-
-       client_process_exec(set->mail_executable, title);
-       i_fatal_status(FATAL_EXEC, "execv(%s) failed: %m",
-                      set->mail_executable);
-
-       /* not reached */
-       return MASTER_LOGIN_STATUS_INTERNAL_ERROR;
-}
-
-static void
-mail_process_destroyed(struct child_process *process,
-                      pid_t pid, bool abnormal_exit ATTR_UNUSED)
-{
-       struct mail_process_group *group = (struct mail_process_group *)process;
-       const pid_t *pids;
-       unsigned int i, count;
-
-       pids = array_get(&group->processes, &count);
-       if (count == 1) {
-               /* last process in this group */
-               i_assert(pids[0] == pid);
-               hash_table_remove(mail_process_groups, group);
-               mail_process_group_free(group);
-       } else {
-               for (i = 0; i < count; i++) {
-                       if (pids[i] == pid)
-                               break;
-               }
-               i_assert(i != count);
-               array_delete(&group->processes, i, 1);
-       }
-
-       mail_process_count--;
-}
-
-void mail_processes_init(void)
-{
-       mail_process_groups = hash_table_create(default_pool, default_pool, 0,
-                                               mail_process_group_hash,
-                                               mail_process_group_cmp);
-
-       child_process_set_destroy_callback(PROCESS_TYPE_IMAP,
-                                          mail_process_destroyed);
-       child_process_set_destroy_callback(PROCESS_TYPE_POP3,
-                                          mail_process_destroyed);
-}
-
-void mail_processes_deinit(void)
-{
-       /* we may still end up in mail_process_destroyed(), so don't free
-          anything. This deinit code needs a redesign.. */
-}
diff --git a/src/master/mail-process.h b/src/master/mail-process.h
deleted file mode 100644 (file)
index 1dd62d2..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-#ifndef MAIL_PROCESS_H
-#define MAIL_PROCESS_H
-
-#include "master-login-interface.h"
-#include "child-process.h"
-
-struct mail_login_request {
-       int fd;
-       unsigned int cmd_tag_size;
-       unsigned int data_size;
-       struct ip_addr local_ip, remote_ip;
-};
-
-struct login_group;
-struct auth_master_reply;
-
-void mail_process_exec(const char *protocol, const char **args) ATTR_NORETURN;
-
-enum master_login_status
-create_mail_process(enum process_type process_type, struct master_settings *set,
-                   const struct mail_login_request *request,
-                   const char *user, const char *const *args,
-                   const unsigned char *data, bool dump_capability,
-                   pid_t *pid_r);
-
-void mail_processes_init(void);
-void mail_processes_deinit(void);
-
-#endif
index 9ed15707ade5a1c6b5f7491f6a64043afcae23aa..5d8931e59e056aec605f38421855c6447a72b0df 100644 (file)
-/* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
+/* Copyright (c) 2005-2009 Dovecot authors, see the included COPYING file */
 
 #include "common.h"
-#include "array.h"
-#include "ioloop.h"
 #include "lib-signals.h"
-#include "network.h"
-#include "env-util.h"
 #include "fd-close-on-exec.h"
 #include "write-full.h"
+#include "env-util.h"
+#include "hostpid.h"
 #include "restrict-process-size.h"
-
-#include "askpass.h"
-#include "auth-process.h"
+#include "master-service.h"
+#include "master-service-settings.h"
 #include "capabilities.h"
-#include "dict-process.h"
-#include "login-process.h"
-#include "mail-process.h"
-#include "syslog-util.h"
-#include "listener.h"
-#include "ssl-init.h"
-#include "log.h"
-#include "sysinfo-get.h"
-#include "hostpid.h"
+#include "service.h"
+#include "service-listen.h"
+#include "service-monitor.h"
+#include "service-log.h"
 
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <fcntl.h>
-#include <syslog.h>
 #include <sys/stat.h>
 
-#define FATAL_FILENAME "master-fatal.lastlog"
-
-static const char *configfile = SYSCONFDIR "/" PACKAGE ".conf";
-static pid_t master_original_pid;
-
-struct ioloop *ioloop;
-int null_fd = -1, inetd_login_fd;
+struct master_service *master_service;
 uid_t master_uid;
-char program_path[PATH_MAX];
-char ssl_manual_key_password[100];
-const char *env_tz;
-bool auth_success_written;
+gid_t master_gid;
 bool core_dumps_disabled;
-#ifdef DEBUG
-bool gdb;
-#endif
+int null_fd;
 
-static void ATTR_NORETURN ATTR_FORMAT(3, 0)
-master_fatal_callback(enum log_type type, int status,
-                     const char *format, va_list args)
-{
-       const struct master_settings *set = master_set->defaults;
-       const char *path, *str;
-       va_list args2;
-       int fd;
+static char *pidfile_path;
+static struct service_list *services;
 
-       /* if we already forked a child process, this isn't fatal for the
-          main process and there's no need to write the fatal file. */
-       if (getpid() == master_original_pid) {
-               /* write the error message to a file */
-               path = t_strconcat(set->base_dir, "/"FATAL_FILENAME, NULL);
-               fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, 0600);
-               if (fd != -1) {
-                       VA_COPY(args2, args);
-                       str = t_strdup_vprintf(format, args2);
-                       write_full(fd, str, strlen(str));
-                       (void)close(fd);
-               }
-       }
+static const char *child_process_env[3]; /* @UNSAFE */
 
-       /* write it to log as well */
-       if (*set->log_path == '\0')
-               i_syslog_fatal_handler(type, status, format, args);
-       else
-               default_fatal_handler(type, status, format, args);
-}
-
-static void fatal_log_check(void)
+void process_exec(const char *cmd, const char *extra_args[])
 {
-       const struct master_settings *set = master_set->defaults;
-       const char *path;
-       char buf[1024];
-       ssize_t ret;
-       int fd;
-
-       path = t_strconcat(set->base_dir, "/"FATAL_FILENAME, NULL);
-       fd = open(path, O_RDONLY);
-       if (fd == -1)
-               return;
-
-       ret = read(fd, buf, sizeof(buf));
-       if (ret < 0)
-               i_error("read(%s) failed: %m", path);
-       else {
-               buf[ret] = '\0';
-               i_warning("Last died with error (see error log for more "
-                         "information): %s", buf);
+       const char *executable, *p, **argv;
+
+       argv = t_strsplit(cmd, " ");
+       executable = argv[0];
+
+       if (extra_args != NULL) {
+               unsigned int count1, count2;
+               const char **new_argv;
+
+               /* @UNSAFE */
+               count1 = str_array_length(argv);
+               count2 = str_array_length(extra_args);
+               new_argv = t_new(const char *, count1 + count2 + 1);
+               memcpy(new_argv, argv, sizeof(const char *) * count1);
+               memcpy(new_argv + count1, extra_args,
+                      sizeof(const char *) * count2);
+               argv = new_argv;
        }
 
-       close(fd);
-       if (unlink(path) < 0)
-               i_error("unlink(%s) failed: %m", path);
-}
+       /* hide the path, it's ugly */
+       p = strrchr(argv[0], '/');
+       if (p != NULL) argv[0] = p+1;
 
-static void auth_warning_print(const struct master_server_settings *set)
-{
-       struct master_auth_settings *const *auths;
-       unsigned int count;
-       struct stat st;
-
-       auth_success_written = stat(AUTH_SUCCESS_PATH, &st) == 0;
-       auths = array_get(&set->defaults->auths, &count);
-       if (!auth_success_written && count > 0 && !auths[0]->debug &&
-           strcmp(set->defaults->protocols, "none") != 0) {
-               i_info("If you have trouble with authentication failures,\n"
-                      "enable auth_debug setting. "
-                      "See http://wiki.dovecot.org/WhyDoesItNotWork");
+       /* prefix with dovecot/ */
+       argv[0] = t_strconcat(PACKAGE"/", argv[0], NULL);
 
-       }
+       execv(executable, (char **)argv);
+       i_fatal_status(FATAL_EXEC, "execvp(%s) failed: %m", executable);
 }
 
-static void set_logfile(struct master_settings *set)
+static void create_pid_file(const char *path)
 {
-       int facility;
-
-       if (*set->log_path == '\0') {
-               if (!syslog_facility_find(set->syslog_facility, &facility))
-                       facility = LOG_MAIL;
-
-               i_set_failure_syslog("dovecot", LOG_NDELAY, facility);
-       } else {
-               /* log to file or stderr */
-               i_set_failure_file(set->log_path, "dovecot: ");
-       }
-       i_set_fatal_handler(master_fatal_callback);
+       const char *pid;
+       int fd;
 
-       if (*set->info_log_path != '\0')
-               i_set_info_file(set->info_log_path);
+       pid = t_strconcat(dec2str(getpid()), "\n", NULL);
 
-       i_set_failure_timestamp_format(set->log_timestamp);
+       fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, 0644);
+       if (fd == -1)
+               i_fatal("open(%s) failed: %m", path);
+       if (write_full(fd, pid, strlen(pid)) < 0)
+               i_fatal("write() failed in %s: %m", path);
+       (void)close(fd);
 }
 
-static void settings_reload(void)
+static void
+sig_settings_reload(const siginfo_t *si ATTR_UNUSED,
+                   void *context ATTR_UNUSED)
 {
-       /* this old_set wrapping works because master settings are
-          alternatingly read using two different pools */
-       struct master_server_settings *set, *old_set = master_set;
-
-       i_warning("SIGHUP received - reloading configuration");
-
-       /* restart auth and login processes */
-        login_processes_destroy_all();
-        auth_processes_destroy_all();
-        dict_processes_kill();
+       struct master_settings *new_set;
+       struct service_list *new_services;
+       const char *error;
+       pool_t pool;
 
        /* see if hostname changed */
        hostpid_init();
 
-       if (master_settings_read(configfile, &set) < 0 ||
-           !master_settings_check(set, FALSE, FALSE))
-               i_warning("Invalid configuration, keeping old one");
-       else {
-               if (!IS_INETD())
-                       listeners_open_fds(old_set, TRUE);
-                set_logfile(set->defaults);
-       }
-}
-
-static void sig_die(const siginfo_t *si, void *context ATTR_UNUSED)
-{
-       /* warn about being killed because of some signal, except SIGINT (^C)
-          which is too common at least while testing :) */
-       if (si->si_signo != SIGINT) {
-               i_warning("Killed with signal %d (by pid=%s uid=%s code=%s)",
-                         si->si_signo, dec2str(si->si_pid),
-                         dec2str(si->si_uid),
-                         lib_signal_code_to_str(si->si_signo, si->si_code));
+#if 0 // FIXME
+       /* FIXME: this loses process structures for existing processes.
+          figure out something. */
+       new_set = master_settings_read(pool, config_binary, config_path);
+       new_services = new_set == NULL ? NULL :
+               services_create(new_set, child_process_env, &error);
+#endif
+       if (new_services == NULL) {
+               /* new configuration is invalid, keep the old */
+               i_error("Config reload failed: %s", error);
+               return;
        }
-       io_loop_stop(ioloop);
-}
-
-static void sig_reload_settings(const siginfo_t *si ATTR_UNUSED,
-                               void *context ATTR_UNUSED)
-{
-       settings_reload();
-}
-
-static void sig_reopen_logs(const siginfo_t *si ATTR_UNUSED,
-                           void *context ATTR_UNUSED)
-{
-       set_logfile(master_set->defaults);
-}
 
-static bool have_stderr_set(struct master_settings *set)
-{
-       if (*set->log_path != '\0' &&
-           strcmp(set->log_path, "/dev/stderr") == 0)
-               return TRUE;
-
-       if (*set->info_log_path != '\0' &&
-           strcmp(set->info_log_path, "/dev/stderr") == 0)
-               return TRUE;
+       /* switch to new configuration. */
+       (void)services_listen_using(new_services, services);
+       services_destroy(services);
+       services = new_services;
 
-       return FALSE;
-}
-
-static bool have_stderr(struct master_server_settings *server)
-{
-       if (server->imap != NULL && have_stderr_set(server->imap))
-               return TRUE;
-       if (server->pop3 != NULL && have_stderr_set(server->pop3))
-               return TRUE;
-       return FALSE;
+        services_monitor_start(services);
 }
 
-static void open_null_fd(void)
+static void
+sig_log_reopen(const siginfo_t *si ATTR_UNUSED, void *context ATTR_UNUSED)
 {
-       null_fd = open("/dev/null", O_RDONLY);
-       if (null_fd == -1)
-               i_fatal("Can't open /dev/null: %m");
-       fd_close_on_exec(null_fd, TRUE);
-}
-
-static void open_fds(void)
-{
-       /* make sure all fds between 0..3 are used. */
-       while (null_fd < 4) {
-               null_fd = dup(null_fd);
-               if (null_fd == -1)
-                       i_fatal("dup(null_fd) failed: %m");
-               fd_close_on_exec(null_fd, TRUE);
-       }
-
-       if (!IS_INETD()) {
-               T_BEGIN {
-                       listeners_open_fds(NULL, FALSE);
-               } T_END;
-       }
-
-       /* close stdin and stdout. */
-       if (dup2(null_fd, 0) < 0)
-               i_fatal("dup2(0) failed: %m");
-       if (dup2(null_fd, 1) < 0)
-               i_fatal("dup2(1) failed: %m");
+        service_signal(services->log, SIGUSR1);
 }
 
-static void create_pid_file(const char *path)
+static void
+sig_reap_children(const siginfo_t *si ATTR_UNUSED, void *context ATTR_UNUSED)
 {
-       const char *pid;
-       int fd;
-
-       pid = t_strconcat(dec2str(getpid()), "\n", NULL);
-
-       fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, 0644);
-       if (fd == -1)
-               i_fatal("open(%s) failed: %m", path);
-       if (write_full(fd, pid, strlen(pid)) < 0)
-               i_fatal("write() failed in %s: %m", path);
-       (void)close(fd);
+       services_monitor_reap_children(services);
 }
 
-static void pid_file_check_running(const struct master_settings *set)
+static void sig_die(const siginfo_t *si, void *context ATTR_UNUSED)
 {
-       const char *path;
-       char buf[32];
-       int fd;
-       ssize_t ret;
-
-       path = t_strconcat(set->base_dir, "/master.pid", NULL);
-       fd = open(path, O_RDONLY);
-       if (fd == -1) {
-               if (errno == ENOENT)
-                       return;
-               i_fatal("open(%s) failed: %m", path);
-       }
-
-       ret = read(fd, buf, sizeof(buf));
-       if (ret <= 0) {
-               if (ret == 0)
-                       i_error("Empty PID file in %s, overriding", path);
-               else
-                       i_fatal("read(%s) failed: %m", path);
-       } else {
-               pid_t pid;
-
-               if (buf[ret-1] == '\n')
-                       ret--;
-               buf[ret] = '\0';
-               pid = atoi(buf);
-               if (pid == getpid() || (kill(pid, 0) < 0 && errno == ESRCH)) {
-                       /* doesn't exist */
-               } else {
-                       i_fatal("Dovecot is already running with PID %s "
-                               "(read from %s)", buf, path);
-               }
-       }
-       (void)close(fd);
+       i_warning("Killed with signal %d (by pid=%s uid=%s code=%s)",
+                 si->si_signo, dec2str(si->si_pid),
+                 dec2str(si->si_uid),
+                 lib_signal_code_to_str(si->si_signo, si->si_code));
+       master_service_stop(master_service);
 }
 
 static void main_log_startup(void)
@@ -312,20 +146,13 @@ static void main_log_startup(void)
                i_info(STARTUP_STRING);
 }
 
-static void main_init(bool log_error)
+static void main_init(const struct master_settings *set, bool log_error)
 {
        drop_capabilities();
 
        /* deny file access from everyone else except owner */
         (void)umask(0077);
 
-       set_logfile(master_set->defaults);
-       /* close stderr unless we're logging into /dev/stderr. */
-       if (!have_stderr(master_set)) {
-               if (dup2(null_fd, 2) < 0)
-                       i_fatal("dup2(2) failed: %m");
-       }
-
        if (log_error) {
                printf("Writing to error logs and killing myself..\n");
                i_info("This is Dovecot's info log");
@@ -336,51 +163,44 @@ static void main_init(bool log_error)
        main_log_startup();
 
        lib_signals_init();
-        lib_signals_set_handler(SIGINT, TRUE, sig_die, NULL);
-        lib_signals_set_handler(SIGTERM, TRUE, sig_die, NULL);
         lib_signals_ignore(SIGPIPE, TRUE);
         lib_signals_ignore(SIGALRM, FALSE);
-        lib_signals_set_handler(SIGHUP, TRUE, sig_reload_settings, NULL);
-        lib_signals_set_handler(SIGUSR1, TRUE, sig_reopen_logs, NULL);
-
-       child_processes_init();
-       log_init();
-       ssl_init();
-       dict_processes_init();
-       auth_processes_init();
-       login_processes_init();
-       mail_processes_init();
-
-       create_pid_file(t_strconcat(master_set->defaults->base_dir,
-                                   "/master.pid", NULL));
+        lib_signals_set_handler(SIGHUP, TRUE, sig_settings_reload, NULL);
+        lib_signals_set_handler(SIGUSR1, TRUE, sig_log_reopen, NULL);
+        lib_signals_set_handler(SIGCHLD, TRUE, sig_reap_children, NULL);
+        lib_signals_set_handler(SIGINT, TRUE, sig_die, NULL);
+        lib_signals_set_handler(SIGTERM, TRUE, sig_die, NULL);
+
+        pidfile_path = i_strconcat(set->base_dir, "/master.pid", NULL);
+       create_pid_file(pidfile_path);
+
+       services_monitor_start(services);
 }
 
 static void main_deinit(void)
 {
-       (void)unlink(t_strconcat(master_set->defaults->base_dir,
-                                "/master.pid", NULL));
+       if (unlink(pidfile_path) < 0)
+               i_error("unlink(%s) failed: %m", pidfile_path);
+       i_free(pidfile_path);
 
-       login_processes_destroy_all();
-
-       mail_processes_deinit();
-       login_processes_deinit();
-       auth_processes_deinit();
-       dict_processes_deinit();
-       ssl_deinit();
+       services_destroy(services);
+}
 
-       listeners_close_fds();
+static const char *get_full_config_path(struct service_list *list)
+{
+       const char *path;
+       char cwd[PATH_MAX];
 
-       if (close(null_fd) < 0)
-               i_error("close(null_fd) failed: %m");
+       path = master_service_get_config_path(master_service);
+       if (*path == '/')
+               return path;
 
-       log_deinit();
-       /* log_deinit() may still want to look up child processes */
-       child_processes_deinit();
-       lib_signals_deinit();
-       closelog();
+       if (getcwd(cwd, sizeof(cwd)) == NULL)
+               i_fatal("getcwd() failed: %m");
+       return p_strconcat(list->pool, cwd, "/", path, NULL);
 }
 
-static void daemonize(struct master_settings *set)
+static void daemonize(void)
 {
        pid_t pid;
 
@@ -394,21 +214,20 @@ static void daemonize(struct master_settings *set)
        if (setsid() < 0)
                i_fatal("setsid() failed: %m");
 
-       if (chdir(set->base_dir) < 0)
-               i_fatal("chdir(%s) failed: %m", set->base_dir);
+       /* update my_pid */
+       hostpid_init();
 }
 
 static void print_help(void)
 {
        printf(
 "Usage: dovecot [-F] [-c <config file>] [-p] [-n] [-a]\n"
-"       [--version] [--build-options] [--exec-mail <protocol> [<args>]]\n");
+"       [-cb <config binary path>] [--version] [--build-options]\n");
 }
 
 static void print_build_options(void)
 {
-       static const char *build_options =
-               "Build options:"
+       printf("Build options:"
 #ifdef IOLOOP_EPOLL
                " ioloop=epoll"
 #endif
@@ -439,8 +258,7 @@ static void print_build_options(void)
 #ifdef HAVE_OPENSSL
                " openssl"
 #endif
-       "\nMail storages: "MAIL_STORAGES"\n"
-       "SQL drivers:"
+       "\nSQL drivers:"
 #ifdef BUILD_MYSQL
                " mysql"
 #endif
@@ -472,9 +290,6 @@ static void print_build_options(void)
 #ifdef PASSDB_SHADOW 
                " shadow"
 #endif
-#ifdef PASSDB_SIA
-               " sia"
-#endif
 #ifdef PASSDB_SQL 
                " sql"
 #endif
@@ -482,8 +297,8 @@ static void print_build_options(void)
                " vpopmail"
 #endif
        "\nUserdb:"
-#ifdef USERDB_NSS
-               " nss"
+#ifdef USERDB_CHECKPASSWORD
+               " checkpassword"
 #endif
 #ifdef USERDB_LDAP
                " ldap"
@@ -491,12 +306,12 @@ static void print_build_options(void)
 #ifdef USERDB_PASSWD
                " passwd"
 #endif
-#ifdef USERDB_PASSWD_FILE
-               " passwd-file"
-#endif
 #ifdef USERDB_PREFETCH
                " prefetch"
 #endif
+#ifdef USERDB_PASSWD_FILE
+               " passwd-file"
+#endif
 #ifdef USERDB_SQL 
                " sql"
 #endif
@@ -506,160 +321,139 @@ static void print_build_options(void)
 #ifdef USERDB_VPOPMAIL
                " vpopmail"
 #endif
-       "\n";
-       puts(build_options);
+       "\n");
 }
 
 int main(int argc, char *argv[])
 {
-       /* parse arguments */
-       struct master_server_settings *set;
-       const char *exec_protocol = NULL, **exec_args = NULL, *user, *home;
+       static const struct setting_parser_info *set_roots[] = {
+               &master_setting_parser_info,
+               NULL
+       };
+       struct master_settings *set;
+       unsigned int child_process_env_idx = 0;
+       const char *getopt_str, *error, *env_tz;
+       void **sets;
        bool foreground = FALSE, ask_key_pass = FALSE, log_error = FALSE;
-       bool dump_config = FALSE, dump_config_nondefaults = FALSE;
-       int i;
+       int c;
 
 #ifdef DEBUG
-       gdb = getenv("GDB") != NULL;
+       if (getenv("GDB") == NULL)
+               fd_debug_verify_leaks(3, 1024);
+       else
+               child_process_env[child_process_env_idx++] = "GDB=1";
 #endif
-       lib_init();
+       master_service = master_service_init("master", 0, argc, argv);
 
        master_uid = geteuid();
-        inetd_login_fd = -1;
-       for (i = 1; i < argc; i++) {
-               if (strcmp(argv[i], "-F") == 0) {
-                       /* foreground */
+       master_gid = getegid();
+
+       getopt_str = t_strconcat("Fp", master_service_getopt_string(), NULL);
+       while ((c = getopt(argc, argv, getopt_str)) > 0) {
+               switch (c) {
+               case 'F':
                        foreground = TRUE;
-               } else if (strcmp(argv[i], "-a") == 0) {
-                       dump_config = TRUE;
-               } else if (strcmp(argv[i], "-c") == 0) {
-                       /* config file */
-                       i++;
-                       if (i == argc) i_fatal("Missing config file argument");
-                       configfile = argv[i];
-               } else if (strcmp(argv[i], "-n") == 0) {
-                       dump_config_nondefaults = dump_config = TRUE;
-               } else if (strcmp(argv[i], "-p") == 0) {
+                       break;
+               case 'p':
                        /* Ask SSL private key password */
                        ask_key_pass = TRUE;
-               } else if (strcmp(argv[i], "--exec-mail") == 0) {
-                       /* <protocol> [<args>]
-                          read configuration and execute mail process */
-                       i++;
-                       if (i == argc) i_fatal("Missing protocol argument");
-                       exec_protocol = argv[i];
-                       exec_args = (const char **)&argv[i+1];
                        break;
-               } else if (strcmp(argv[i], "--version") == 0) {
+               default:
+                       if (!master_service_parse_option(master_service,
+                                                        c, optarg)) {
+                               print_help();
+                               exit(FATAL_DEFAULT);
+                       }
+                       break;
+               }
+       }
+
+       if (optind < argc) {
+               if (strcmp(argv[optind], "--version") == 0) {
                        printf("%s\n", VERSION);
                        return 0;
-               } else if (strcmp(argv[i], "--build-options") == 0) {
+               } else if (strcmp(argv[optind], "--build-options") == 0) {
                        print_build_options();
                        return 0;
-               } else if (strcmp(argv[i], "--log-error") == 0) {
+               } else if (strcmp(argv[optind], "--log-error") == 0) {
                        log_error = TRUE;
                        foreground = TRUE;
                } else {
                        print_help();
-                       i_fatal("Unknown argument: %s", argv[1]);
+                       i_fatal("Unknown argument: %s", argv[optind]);
                }
        }
 
-       /* need to have this open before reading settings */
-       open_null_fd();
-
-       if (getenv("DOVECOT_INETD") != NULL) {
-               /* starting through inetd. */
-               inetd_login_fd = dup(0);
-               if (inetd_login_fd == -1)
-                       i_fatal("dup(0) failed: %m");
-               fd_close_on_exec(inetd_login_fd, TRUE);
-               foreground = TRUE;
-       }
-
-       if (dump_config) {
-
-               /* print the config file path before parsing it, so in case
-                  of errors it's still shown */
-               printf("# "VERSION": %s\n", configfile);
-       }
-
-       /* read and verify settings before forking */
-       T_BEGIN {
-               master_settings_init();
-               if (master_settings_read(configfile, &set) < 0)
-                       i_fatal("Invalid configuration in %s", configfile);
-       } T_END;
-
-       if (!log_error && !dump_config && exec_protocol == NULL)
-               pid_file_check_running(set->defaults);
-
-       if (!master_settings_check(set, exec_protocol != NULL,
-                                  dump_config || log_error))
-               i_fatal("Invalid configuration in %s", configfile);
+       do {
+               null_fd = open("/dev/null", O_WRONLY);
+               if (null_fd == -1)
+                       i_fatal("Can't open /dev/null: %m");
+               fd_close_on_exec(null_fd, TRUE);
+       } while (null_fd <= STDERR_FILENO);
 
-       if (dump_config) {
-               const char *info;
+       if (dup2(null_fd, STDIN_FILENO) < 0 ||
+           dup2(null_fd, STDOUT_FILENO) < 0)
+               i_fatal("dup2(null_fd) failed: %m");
 
-               info = sysinfo_get(master_set->defaults->mail_location);
-               if (*info != '\0')
-                       printf("# %s\n", info);
+       if (master_service_settings_read(master_service, set_roots, NULL, FALSE,
+                                        &error) < 0)
+               i_fatal("Error reading configuration: %s", error);
+       sets = master_service_settings_get_others(master_service);
+       set = sets[0];
 
-               if (dump_config_nondefaults)
-                       client_process_exec(DOVECOT_CONFIG_BIN_PATH" -a", "");
-               else
-                       client_process_exec(DOVECOT_CONFIG_BIN_PATH" -n", "");
-               i_fatal("exec(%s) failed: %m", DOVECOT_CONFIG_BIN_PATH);
-       }
+       master_service_init_log(master_service, "dovecot: ", 0);
+       if (!log_error)
+               master_settings_do_fixes(set);
 
-       if (ask_key_pass && !log_error) T_BEGIN {
+#if 0 // FIXME
+       if (ask_key_pass) {
                const char *prompt;
 
                prompt = t_strdup_printf("Give the password for SSL key file "
-                                        "%s: ",
-                                        master_set->defaults->ssl_key_file);
+                                        "%s: ", set->ssl_key_file);
                askpass(prompt, ssl_manual_key_password,
                        sizeof(ssl_manual_key_password));
-       } T_END;
+       }
+#endif
 
        /* save TZ environment. AIX depends on it to get the timezone
           correctly. */
        env_tz = getenv("TZ");
-       user = getenv("USER");
-       home = getenv("HOME");
 
        /* clean up the environment of everything */
        env_clean();
 
        /* put back the TZ */
-       if (env_tz != NULL)
-               env_put(t_strconcat("TZ=", env_tz, NULL));
-
-       if (exec_protocol != NULL) {
-               /* Put back user and home */
-               env_put(t_strconcat("USER=", user, NULL));
-               env_put(t_strconcat("HOME=", home, NULL));
-               mail_process_exec(exec_protocol, exec_args);
+       if (env_tz != NULL) {
+               const char *env = t_strconcat("TZ=", env_tz, NULL);
+
+               env_put(env);
+               child_process_env[child_process_env_idx++] = env;
        }
+       i_assert(child_process_env_idx <
+                sizeof(child_process_env) / sizeof(child_process_env[0]));
+       child_process_env[child_process_env_idx++] = NULL;
 
-       if (!log_error)
-               open_fds();
+       /* create service structures from settings. if there are any errors in
+          service configuration we'll catch it here. */
+       services = services_create(set, child_process_env, &error);
+       if (services == NULL)
+               i_fatal("%s", error);
 
-       fatal_log_check();
-       auth_warning_print(master_set);
-       if (!foreground)
-               daemonize(master_set->defaults);
-       master_original_pid = getpid();
+       services->config->config_file_path = get_full_config_path(services);
 
-       ioloop = io_loop_create();
-
-       main_init(log_error);
-        io_loop_run(ioloop);
-       main_deinit();
+       /* if any listening fails, fail completely */
+       if (services_listen(services) <= 0)
+               return FATAL_DEFAULT;
 
-       master_settings_deinit();
-       io_loop_destroy(&ioloop);
-       lib_deinit();
+       if (!foreground)
+               daemonize();
+       if (chdir(set->base_dir) < 0)
+               i_fatal("chdir(%s) failed: %m", set->base_dir);
 
+       main_init(set, log_error);
+       master_service_run(master_service, NULL);
+       main_deinit();
+       master_service_deinit(&master_service);
         return 0;
 }
diff --git a/src/master/master-login-interface.h b/src/master/master-login-interface.h
deleted file mode 100644 (file)
index 5186d03..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-#ifndef MASTER_LOGIN_INTERFACE_H
-#define MASTER_LOGIN_INTERFACE_H
-
-#include "network.h"
-
-#define LOGIN_MASTER_SOCKET_FD 3
-
-/* Increase the version number every time master_login_request
-   (or something else) is changed. */
-#define MASTER_LOGIN_PROTOCOL_VERSION 3
-
-/* This should be kept in sync with LOGIN_MAX_INBUF_SIZE. Multiply it by two
-   to make sure there's space to transfer the command tag  */
-#define MASTER_LOGIN_MAX_DATA_SIZE (4096*2)
-
-enum master_login_state {
-       /* process is accepting new connections */
-       LOGIN_STATE_LISTENING = 0,
-       /* process isn't accepting new connections, but it'd be able to kill
-          some connections which haven't logged in yet */
-       LOGIN_STATE_FULL_PRELOGINS,
-       /* process is handling only logged in users */
-       LOGIN_STATE_FULL_LOGINS,
-
-       LOGIN_STATE_COUNT
-};
-
-struct master_login_request {
-       uint32_t version;
-       /* if fd == -1, tag is used as master_login_state */
-       uint32_t tag;
-
-       uint32_t auth_pid;
-       uint32_t auth_id;
-       /* request follows this many bytes of client input */
-       uint16_t data_size;
-       uint16_t cmd_tag_size;
-
-       ino_t ino;
-
-       struct ip_addr local_ip, remote_ip;
-};
-
-enum master_login_status {
-       MASTER_LOGIN_STATUS_OK,
-       MASTER_LOGIN_STATUS_INTERNAL_ERROR,
-       /* user reached max. simultaneous connections */
-       MASTER_LOGIN_STATUS_MAX_CONNECTIONS
-};
-
-struct master_login_reply {
-       unsigned int tag;
-       enum master_login_status status;
-       /* PID of the post-login mail process handling this connection */
-       pid_t mail_pid;
-};
-
-#endif
index 845c7732bac62cdf86051c4a43cb7c2a1e6dac80..af9181082b36631154ec76054024c2d439c6697f 100644 (file)
-/* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
+/* Copyright (c) 2005-2009 Dovecot authors, see the included COPYING file */
 
 #include "common.h"
 #include "array.h"
-#include "str.h"
-#include "istream.h"
 #include "env-util.h"
-#include "fd-close-on-exec.h"
-#include "safe-mkdir.h"
+#include "istream.h"
 #include "mkdir-parents.h"
-#include "unlink-directory.h"
-#include "syslog-util.h"
-#include "mail-process.h"
-#include "master-login-interface.h"
 #include "settings-parser.h"
+#include "master-settings.h"
 
-#include <stdio.h>
 #include <stddef.h>
-#include <stdlib.h>
 #include <dirent.h>
 #include <unistd.h>
-#include <fcntl.h>
-#include <ctype.h>
-#include <signal.h>
 #include <sys/stat.h>
 #include <sys/wait.h>
-#include <pwd.h>
-#include <grp.h>
-#ifdef HAVE_SYS_RESOURCE_H
-#  include <sys/resource.h>
-#endif
 
-extern struct setting_parser_info master_auth_setting_parser_info;
-extern struct setting_parser_info master_setting_parser_info;
-extern struct setting_parser_info master_auth_socket_setting_parser_info;
+static bool master_settings_verify(void *_set, pool_t pool,
+                                  const char **error_r);
+
+extern struct setting_parser_info service_setting_parser_info;
 
 #undef DEF
 #define DEF(type, name) \
-       { type, #name, offsetof(struct master_auth_socket_unix_settings, name), NULL }
+       { type, #name, offsetof(struct file_listener_settings, name), NULL }
 
-static struct setting_define master_auth_socket_master_setting_defines[] = {
+static struct setting_define file_listener_setting_defines[] = {
        DEF(SET_STR, path),
+       DEF(SET_UINT, mode),
+       DEF(SET_STR, user),
+       DEF(SET_STR, group),
 
        SETTING_DEFINE_LIST_END
 };
 
-static struct master_auth_socket_unix_settings master_auth_socket_master_default_settings = {
-       MEMBER(path) "auth-master"
+static struct file_listener_settings file_listener_default_settings = {
+       MEMBER(path) "",
+       MEMBER(mode) 0600,
+       MEMBER(user) "",
+       MEMBER(group) "",
 };
 
-struct setting_parser_info master_auth_socket_master_setting_parser_info = {
-       MEMBER(defines) master_auth_socket_master_setting_defines,
-       MEMBER(defaults) &master_auth_socket_master_default_settings,
-
-       MEMBER(parent) &master_auth_socket_setting_parser_info,
-       MEMBER(dynamic_parsers) NULL,
+static struct setting_parser_info file_listener_setting_parser_info = {
+       MEMBER(defines) file_listener_setting_defines,
+       MEMBER(defaults) &file_listener_default_settings,
 
+       MEMBER(parent) &service_setting_parser_info,
        MEMBER(parent_offset) (size_t)-1,
-       MEMBER(type_offset) (size_t)-1,
-       MEMBER(struct_size) sizeof(struct master_auth_socket_unix_settings)
+       MEMBER(struct_size) sizeof(struct file_listener_settings)
 };
 
 #undef DEF
-#undef DEFLIST
 #define DEF(type, name) \
-       { type, #name, offsetof(struct master_auth_socket_settings, name), NULL }
-#define DEFLIST(field, name, defines) \
-       { SET_DEFLIST, name, offsetof(struct master_auth_socket_settings, field), defines }
-static struct setting_define master_auth_socket_setting_defines[] = {
-       DEF(SET_STR, type),
-       DEFLIST(masters, "master", &master_auth_socket_master_setting_parser_info),
+       { type, #name, offsetof(struct inet_listener_settings, name), NULL }
+
+static struct setting_define inet_listener_setting_defines[] = {
+       DEF(SET_STR, address),
+       DEF(SET_UINT, port),
 
        SETTING_DEFINE_LIST_END
 };
 
-struct setting_parser_info master_auth_socket_setting_parser_info = {
-       MEMBER(defines) master_auth_socket_setting_defines,
-       MEMBER(defaults) NULL,
+static struct inet_listener_settings inet_listener_default_settings = {
+       MEMBER(address) "*",
+       MEMBER(port) 0
+};
 
-       MEMBER(parent) &master_auth_setting_parser_info,
-       MEMBER(dynamic_parsers) NULL,
+static struct setting_parser_info inet_listener_setting_parser_info = {
+       MEMBER(defines) inet_listener_setting_defines,
+       MEMBER(defaults) &inet_listener_default_settings,
 
+       MEMBER(parent) &service_setting_parser_info,
        MEMBER(parent_offset) (size_t)-1,
-       MEMBER(type_offset) offsetof(struct master_auth_socket_settings, type),
-       MEMBER(struct_size) sizeof(struct master_auth_socket_settings)
+       MEMBER(struct_size) sizeof(struct inet_listener_settings)
 };
 
 #undef DEF
 #undef DEFLIST
 #define DEF(type, name) \
-       { type, #name, offsetof(struct master_auth_settings, name), NULL }
+       { type, #name, offsetof(struct service_settings, name), NULL }
 #define DEFLIST(field, name, defines) \
-       { SET_DEFLIST, name, offsetof(struct master_auth_settings, field), defines }
+       { SET_DEFLIST, name, offsetof(struct service_settings, field), defines }
 
-static struct setting_define master_auth_setting_defines[] = {
-       DEF(SET_STR, name),
+static struct setting_define service_setting_defines[] = {
+       DEF(SET_INTERNAL, master_set),
+       DEF(SET_STR, type),
        DEF(SET_STR, executable),
        DEF(SET_STR, user),
+       DEF(SET_STR, group),
+       DEF(SET_STR, privileged_group),
+       DEF(SET_STR, extra_groups),
        DEF(SET_STR, chroot),
-       DEF(SET_UINT, count),
-       DEF(SET_UINT, process_size),
-       DEF(SET_STR, mechanisms),
-       DEF(SET_BOOL, debug),
-       DEFLIST(sockets, "socket", &master_auth_socket_setting_parser_info),
+       DEF(SET_STR, auth_dest_service),
+
+       DEF(SET_BOOL, drop_priv_before_exec),
+
+       DEF(SET_UINT, process_limit),
+       DEF(SET_UINT, client_limit),
+       DEF(SET_UINT, vsz_limit),
+
+       DEFLIST(unix_listeners, "unix_listener",
+               &file_listener_setting_parser_info),
+       DEFLIST(fifo_listeners, "fifo_listener",
+               &file_listener_setting_parser_info),
+       DEFLIST(inet_listeners, "inet_listener",
+               &inet_listener_setting_parser_info),
 
        SETTING_DEFINE_LIST_END
 };
 
-static struct master_auth_settings master_auth_default_settings = {
-       MEMBER(name) "default",
-       MEMBER(executable) PKG_LIBEXECDIR"/dovecot-auth",
-       MEMBER(user) "root",
+static struct service_settings service_default_settings = {
+       MEMBER(master_set) NULL,
+
+       MEMBER(type) "",
+       MEMBER(executable) "",
+       MEMBER(user) "",
+       MEMBER(group) "",
+       MEMBER(privileged_group) "",
+       MEMBER(extra_groups) "",
        MEMBER(chroot) "",
-       MEMBER(count) 1,
-       MEMBER(process_size) 256,
-       MEMBER(mechanisms) "plain",
-       MEMBER(debug) FALSE
+       MEMBER(auth_dest_service) "",
+
+       MEMBER(drop_priv_before_exec) FALSE,
+
+       MEMBER(process_limit) (unsigned int)-1,
+       MEMBER(client_limit) 0,
+       MEMBER(vsz_limit) 256,
 
-       /* .. */
+       MEMBER(unix_listeners) ARRAY_INIT,
+       MEMBER(fifo_listeners) ARRAY_INIT,
+       MEMBER(inet_listeners) ARRAY_INIT
 };
 
-struct setting_parser_info master_auth_setting_parser_info = {
-       MEMBER(defines) master_auth_setting_defines,
-       MEMBER(defaults) &master_auth_default_settings,
+struct setting_parser_info service_setting_parser_info = {
+       MEMBER(defines) service_setting_defines,
+       MEMBER(defaults) &service_default_settings,
 
        MEMBER(parent) &master_setting_parser_info,
        MEMBER(dynamic_parsers) NULL,
 
-       MEMBER(parent_offset) (size_t)-1,
-       MEMBER(type_offset) offsetof(struct master_auth_settings, name),
-       MEMBER(struct_size) sizeof(struct master_auth_settings)
+       MEMBER(parent_offset) offsetof(struct service_settings, master_set),
+       MEMBER(type_offset) (size_t)-1,
+       MEMBER(struct_size) sizeof(struct service_settings)
 };
 
 #undef DEF
@@ -139,135 +150,37 @@ struct setting_parser_info master_auth_setting_parser_info = {
        { SET_DEFLIST, name, offsetof(struct master_settings, field), defines }
 
 static struct setting_define master_setting_defines[] = {
-       /* common */
        DEF(SET_STR, base_dir),
-       DEF(SET_STR, log_path),
-       DEF(SET_STR, info_log_path),
-       DEF(SET_STR, log_timestamp),
-       DEF(SET_STR, syslog_facility),
-
-       /* general */
-       DEF(SET_STR, protocols),
-       DEF(SET_STR, listen),
-       DEF(SET_STR, ssl_listen),
-
-       DEF(SET_STR, ssl),
-       DEF(SET_STR, ssl_key_file),
-       DEF(SET_UINT, ssl_parameters_regenerate),
-       DEF(SET_BOOL, version_ignore),
-
-       /* login */
-       DEF(SET_STR, login_dir),
-       DEF(SET_STR, login_executable),
-       DEF(SET_STR, login_user),
-
-       DEF(SET_BOOL, login_process_per_connection),
-       DEF(SET_BOOL, login_chroot),
-       DEF(SET_BOOL, disable_plaintext_auth),
-
-       DEF(SET_UINT, login_process_size),
-       DEF(SET_UINT, login_processes_count),
-       DEF(SET_UINT, login_max_processes_count),
+       DEF(SET_STR, libexec_dir),
+       DEF(SET_UINT, default_process_limit),
+       DEF(SET_UINT, default_client_limit),
 
-       /* mail */
-       DEF(SET_STR, valid_chroot_dirs),
-       DEF(SET_STR, mail_chroot),
-       DEF(SET_UINT, max_mail_processes),
-       DEF(SET_UINT, mail_max_userip_connections),
-       DEF(SET_BOOL, verbose_proctitle),
+       DEF(SET_BOOL, version_ignore),
 
        DEF(SET_UINT, first_valid_uid),
        DEF(SET_UINT, last_valid_uid),
        DEF(SET_UINT, first_valid_gid),
        DEF(SET_UINT, last_valid_gid),
-       DEF(SET_STR, mail_access_groups),
-       DEF(SET_STR, mail_privileged_group),
-       DEF(SET_STR, mail_uid),
-       DEF(SET_STR, mail_gid),
-
-       DEF(SET_STR, mail_plugins),
-       DEF(SET_STR, imap_capability),
-
-       DEF(SET_STR_VARS, mail_location),
-       DEF(SET_BOOL, mail_debug),
-       DEF(SET_BOOL, mail_drop_priv_before_exec),
 
-       DEF(SET_STR, mail_executable),
-       DEF(SET_UINT, mail_process_size),
-       DEF(SET_STR, mail_log_prefix),
-       DEF(SET_UINT, mail_log_max_lines_per_sec),
-
-       /* dict */
-       DEF(SET_UINT, dict_process_count),
-       DEFLIST(auths, "auth", &master_auth_setting_parser_info),
+       DEFLIST(services, "service", &service_setting_parser_info),
 
        SETTING_DEFINE_LIST_END
 };
 
-struct master_settings master_default_settings = {
-       /* common */
+static struct master_settings master_default_settings = {
        MEMBER(base_dir) PKG_RUNDIR,
-       MEMBER(log_path) "",
-       MEMBER(info_log_path) "",
-       MEMBER(log_timestamp) DEFAULT_FAILURE_STAMP_FORMAT,
-       MEMBER(syslog_facility) "mail",
-
-       /* general */
-       MEMBER(protocols) "imap imaps",
-       MEMBER(listen) "*",
-       MEMBER(ssl_listen) "",
-
-       MEMBER(ssl) "yes",
-       MEMBER(ssl_key_file) SSLDIR"/private/dovecot.pem",
-       MEMBER(ssl_parameters_regenerate) 168,
-       MEMBER(version_ignore) FALSE,
+       MEMBER(libexec_dir) PKG_LIBEXECDIR,
+       MEMBER(default_process_limit) 100,
+       MEMBER(default_client_limit) 1000,
 
-       /* login */
-       MEMBER(login_dir) "login",
-       MEMBER(login_executable) NULL,
-       MEMBER(login_user) "dovecot",
-
-       MEMBER(login_process_per_connection) TRUE,
-       MEMBER(login_chroot) TRUE,
-       MEMBER(disable_plaintext_auth) TRUE,
-
-       MEMBER(login_process_size) 64,
-       MEMBER(login_processes_count) 3,
-       MEMBER(login_max_processes_count) 128,
-
-       /* mail */
-       MEMBER(valid_chroot_dirs) "",
-       MEMBER(mail_chroot) "",
-       MEMBER(max_mail_processes) 512,
-       MEMBER(mail_max_userip_connections) 10,
-       MEMBER(verbose_proctitle) FALSE,
+       MEMBER(version_ignore) FALSE,
 
        MEMBER(first_valid_uid) 500,
        MEMBER(last_valid_uid) 0,
        MEMBER(first_valid_gid) 1,
        MEMBER(last_valid_gid) 0,
-       MEMBER(mail_access_groups) "",
-       MEMBER(mail_privileged_group) "",
-       MEMBER(mail_uid) "",
-       MEMBER(mail_gid) "",
-       MEMBER(mail_plugins) "",
-       MEMBER(imap_capability) "",
-
-       MEMBER(mail_location) "",
-       MEMBER(mail_debug) FALSE,
-       MEMBER(maildir_very_dirty_syncs) FALSE,
-       MEMBER(dbox_purge_min_percentage) 0,
-       MEMBER(mail_drop_priv_before_exec) FALSE,
-
-       MEMBER(mail_executable) NULL,
-       MEMBER(mail_process_size) 256,
-       MEMBER(mail_log_prefix) "%Us(%u): ",
-       MEMBER(mail_log_max_lines_per_sec) 10,
-
-       /* dict */
-       MEMBER(dict_process_count) 1
-
-       /* .. */
+
+       MEMBER(services) ARRAY_INIT
 };
 
 struct setting_parser_info master_setting_parser_info = {
@@ -275,513 +188,73 @@ struct setting_parser_info master_setting_parser_info = {
        MEMBER(defaults) &master_default_settings,
 
        MEMBER(parent) NULL,
-       MEMBER(dynamic_parsers) NULL,
-
        MEMBER(parent_offset) (size_t)-1,
-       MEMBER(type_offset) (size_t)-1,
-       MEMBER(struct_size) sizeof(struct master_settings)
+       MEMBER(struct_size) sizeof(struct master_settings),
+       MEMBER(check_func) master_settings_verify
 };
 
-static pool_t settings_pool, settings2_pool;
-struct master_server_settings *master_set = NULL;
-
-#ifdef HAVE_MODULES
-static const char *
-get_process_capability(enum process_type ptype, struct master_settings *set)
+/* <settings checks> */
+static void fix_file_listener_paths(ARRAY_TYPE(file_listener_settings) *l,
+                                   pool_t pool, const char *base_dir)
 {
-       /* FIXME: pretty ugly code just for getting the capability
-          automatically */
-       static const char *args[] = {
-               "uid=65534",
-               "gid=65534",
-               "home=/tmp",
-               NULL
-       };
-       const char *pname = process_names[ptype];
-       enum master_login_status login_status;
-       struct mail_login_request request;
-       char buf[4096];
-       int fd[2], status;
-       ssize_t ret;
-       unsigned int pos;
-       uid_t uid;
-       pid_t pid;
-
-       uid = geteuid();
-       if (uid != 0) {
-               /* use the current user */
-               args[0] = t_strdup_printf("uid=%s", dec2str(uid));
-               args[1] = t_strdup_printf("gid=%s", dec2str(getegid()));
-       }
-
-       if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd) < 0) {
-               i_error("socketpair() failed: %m");
-               return NULL;
-       }
-       fd_close_on_exec(fd[0], TRUE);
-       fd_close_on_exec(fd[1], TRUE);
-
-       memset(&request, 0, sizeof(request));
-       request.fd = fd[1];
-       login_status = create_mail_process(ptype, set, &request,
-                                          "dump-capability", args, NULL, TRUE,
-                                          &pid);
-       if (login_status != MASTER_LOGIN_STATUS_OK) {
-               (void)close(fd[0]);
-               (void)close(fd[1]);
-               return NULL;
-       }
-       (void)close(fd[1]);
-
-       alarm(5);
-       if (wait(&status) == -1) {
-               i_fatal("%s dump-capability process %d got stuck",
-                       pname, (int)pid);
-       }
-       alarm(0);
-
-       if (status != 0) {
-               (void)close(fd[0]);
-               if (WIFSIGNALED(status)) {
-                       i_error("%s dump-capability process "
-                               "killed with signal %d",
-                               pname, WTERMSIG(status));
-               } else {
-                       i_error("%s dump-capability process returned %d",
-                               pname, WIFEXITED(status) ? WEXITSTATUS(status) :
-                               status);
-               }
-               return NULL;
-       }
-
-       pos = 0;
-       while ((ret = read(fd[0], buf + pos, sizeof(buf) - pos)) > 0)
-               pos += ret;
-
-       if (ret < 0) {
-               i_error("read(%s dump-capability process) failed: %m", pname);
-               (void)close(fd[0]);
-               return NULL;
-       }
-       (void)close(fd[0]);
-
-       if (pos == 0 || buf[pos-1] != '\n') {
-               i_error("%s dump-capability: Couldn't read capability "
-                       "(got %u bytes)", pname, pos);
-               return NULL;
-       }
-       buf[pos-1] = '\0';
-
-       return i_strdup(buf);
-}
-
-static bool get_imap_capability(struct master_settings *set)
-{
-       static const char *generated_capability = NULL;
-
-       if (generated_capability != NULL) {
-               /* Reloading configuration. Don't try to execute the imap
-                  process again. Too risky and the wait() call below will
-                  break it anyway. Just use the previous capability list we
-                  already had generated. */
-               set->imap_generated_capability =
-                       p_strdup(settings_pool, generated_capability);
-               return TRUE;
-       }
-
-       generated_capability = get_process_capability(PROCESS_TYPE_IMAP, set);
-       if (generated_capability == NULL)
-               return FALSE;
-
-       set->imap_generated_capability =
-               p_strdup(settings_pool, generated_capability);
-       return TRUE;
-}
-#endif
-
-static void fix_base_path(struct master_settings *set, const char **str)
-{
-       if (*str != NULL && **str != '\0' && **str != '/') {
-               *str = p_strconcat(settings_pool,
-                                  set->base_dir, "/", *str, NULL);
-       }
-}
-
-static bool parse_uid(const char *str, uid_t *uid_r)
-{
-       struct passwd *pw;
-       char *p;
-
-       if (*str >= '0' && *str <= '9') {
-               *uid_r = (uid_t)strtoul(str, &p, 10);
-               if (*p == '\0')
-                       return TRUE;
-       }
-
-       pw = getpwnam(str);
-       if (pw == NULL)
-               return FALSE;
-
-       *uid_r = pw->pw_uid;
-       return TRUE;
-}
-
-static bool parse_gid(const char *str, gid_t *gid_r)
-{
-       struct group *gr;
-       char *p;
-
-       if (*str >= '0' && *str <= '9') {
-               *gid_r = (gid_t)strtoul(str, &p, 10);
-               if (*p == '\0')
-                       return TRUE;
-       }
-
-       gr = getgrnam(str);
-       if (gr == NULL)
-               return FALSE;
-
-       *gid_r = gr->gr_gid;
-       return TRUE;
-}
-
-static bool get_login_uid(struct master_settings *set)
-{
-       struct passwd *pw;
-
-       if ((pw = getpwnam(set->login_user)) == NULL) {
-               i_error("Login user doesn't exist: %s", set->login_user);
-               return FALSE;
-       }
-
-       if (set->server->login_gid == 0)
-               set->server->login_gid = pw->pw_gid;
-       else if (set->server->login_gid != pw->pw_gid) {
-               i_error("All login process users must belong to same group "
-                       "(%s vs %s)", dec2str(set->server->login_gid),
-                       dec2str(pw->pw_gid));
-               return FALSE;
-       }
-       set->login_uid = pw->pw_uid;
-       return TRUE;
-}
-
-static bool auth_settings_verify(struct master_settings *set,
-                                struct master_auth_settings *auth)
-{
-       struct passwd *pw;
-       struct master_auth_socket_settings *const *sockets;
+       struct file_listener_settings *const *sets;
        unsigned int i, count;
 
-       if ((pw = getpwnam(auth->user)) == NULL) {
-               i_error("Auth user doesn't exist: %s", auth->user);
-               return FALSE;
-       }
-
-       if (set->login_uid == pw->pw_uid && master_uid != pw->pw_uid) {
-               i_error("login_user %s (uid %s) must not be same as auth_user",
-                       auth->user, dec2str(pw->pw_uid));
-               return FALSE;
-       }
-       auth->uid = pw->pw_uid;
-       auth->gid = pw->pw_gid;
-
-       if (access(t_strcut(auth->executable, ' '), X_OK) < 0) {
-               i_error("auth_executable: Can't use %s: %m",
-                       t_strcut(auth->executable, ' '));
-               return FALSE;
-       }
-
-       fix_base_path(set, &auth->chroot);
-       if (*auth->chroot != '\0' && access(auth->chroot, X_OK) < 0) {
-               i_error("Can't access auth chroot directory %s: %m",
-                       auth->chroot);
-               return FALSE;
-       }
+       if (!array_is_created(l))
+               return;
 
-       if (array_is_created(&auth->sockets))
-               sockets = array_get(&auth->sockets, &count);
-       else {
-               sockets = NULL;
-               count = 0;
-       }
+       sets = array_get(l, &count);
        for (i = 0; i < count; i++) {
-               if (auth->count > 1 &&
-                   strcmp(sockets[i]->type, "listen") == 0) {
-                       i_error("Currently auth process count must be 1 if "
-                               "you're using auth socket listeners.");
-                       return FALSE;
+               if (*sets[i]->path != '/') {
+                       sets[i]->path = p_strconcat(pool, base_dir, "/",
+                                                   sets[i]->path, NULL);
                }
        }
-       return TRUE;
 }
 
-static const char *get_directory(const char *path)
-{
-       char *str, *p;
-
-       str = t_strdup_noconst(path);
-       p = strrchr(str, '/');
-       if (p == NULL)
-               return ".";
-       else {
-               *p = '\0';
-               return str;
-       }
-}
-
-static bool settings_is_active(struct master_settings *set)
-{
-       if (*set->protocols == '\0') {
-               /* we're probably using this with --exec-mail */
-               return TRUE;
-       }
-
-       if (set->protocol == MAIL_PROTOCOL_IMAP) {
-               if (strstr(set->protocols, "imap") == NULL)
-                       return FALSE;
-       } else {
-               if (strstr(set->protocols, "pop3") == NULL)
-                       return FALSE;
-       }
-
-       return TRUE;
-}
-
-static bool settings_have_connect_sockets(struct master_settings *set)
-{
-       struct master_auth_settings *const *auths;
-       struct master_auth_socket_settings *const *sockets;
-       unsigned int i, count, count2;
-
-       auths = array_get(&set->auths, &count);
-       for (i = 0; i < count; i++) {
-               if (!array_is_created(&auths[i]->sockets))
-                       continue;
-               sockets = array_get(&auths[i]->sockets, &count2);
-               if (count2 > 0 && strcmp(sockets[0]->type, "connect") == 0)
-                       return TRUE;
-       }
-
-       return FALSE;
-}
-
-static bool settings_have_nonplaintext_auths(struct master_settings *set)
+static bool
+master_settings_verify(void *_set, pool_t pool, const char **error_r)
 {
-       struct master_auth_settings *const *auths;
-       const char *const *tmp;
+       const struct master_settings *set = _set;
+       struct service_settings *const *services;
        unsigned int i, count;
 
-       auths = array_get(&set->auths, &count);
-       for (i = 0; i < count; i++) {
-               tmp = t_strsplit_spaces(auths[i]->mechanisms, " ");
-               for (; *tmp != NULL; tmp++) {
-                       if (strcasecmp(*tmp, "PLAIN") != 0 &&
-                           strcasecmp(*tmp, "LOGIN") != 0)
-                               return TRUE;
-               }
-       }
-
-       return FALSE;
-}
-
-static void unlink_auth_sockets(const char *path, const char *prefix)
-{
-       DIR *dirp;
-       struct dirent *dp;
-       struct stat st;
-       string_t *str;
-       unsigned int prefix_len;
-
-       dirp = opendir(path);
-       if (dirp == NULL) {
-               i_error("opendir(%s) failed: %m", path);
-               return;
-       }
-
-       prefix_len = strlen(prefix);
-       str = t_str_new(256);
-       while ((dp = readdir(dirp)) != NULL) {
-               if (dp->d_name[0] == '.')
-                       continue;
-
-               if (strncmp(dp->d_name, prefix, prefix_len) != 0)
-                       continue;
-
-               str_truncate(str, 0);
-               str_printfa(str, "%s/%s", path, dp->d_name);
-               if (lstat(str_c(str), &st) < 0) {
-                       if (errno != ENOENT)
-                               i_error("lstat(%s) failed: %m", str_c(str));
-                       continue;
-               }
-               if (!S_ISSOCK(st.st_mode))
-                       continue;
-
-               /* try to avoid unlinking sockets if someone's already
-                  listening in them. do this only at startup, because
-                  when SIGHUPing a child process might catch the new
-                  connection before it notices that it's supposed
-                  to die. null_fd == -1 check is a bit kludgy, but works.. */
-               if (null_fd == -1) {
-                       int fd = net_connect_unix(str_c(str));
-                       if (fd != -1 || errno != ECONNREFUSED) {
-                               i_fatal("Dovecot is already running? "
-                                       "Socket already exists: %s",
-                                       str_c(str));
-                       }
-               }
-
-               if (unlink(str_c(str)) < 0 && errno != ENOENT)
-                       i_error("unlink(%s) failed: %m", str_c(str));
-       }
-       (void)closedir(dirp);
-}
-
-static bool settings_verify(struct master_settings *set)
-{
-       const char *dir;
-       int facility;
-
-       if (!get_login_uid(set))
-               return FALSE;
-
-       set->mail_uid_t = (uid_t)-1;
-       set->mail_gid_t = (gid_t)-1;
-       set->mail_priv_gid_t = (gid_t)-1;
-
-       if (*set->mail_uid != '\0') {
-               if (!parse_uid(set->mail_uid, &set->mail_uid_t)) {
-                       i_error("Non-existing mail_uid: %s", set->mail_uid);
-                       return FALSE;
-               }
-       }
-       if (*set->mail_gid != '\0') {
-               if (!parse_gid(set->mail_gid, &set->mail_gid_t)) {
-                       i_error("Non-existing mail_gid: %s", set->mail_uid);
-                       return FALSE;
-               }
-       }
-       if (*set->mail_privileged_group != '\0') {
-               if (!parse_gid(set->mail_privileged_group,
-                              &set->mail_priv_gid_t)) {
-                       i_error("Non-existing mail_privileged_group: %s",
-                               set->mail_privileged_group);
-                       return FALSE;
-               }
-       }
-
-       if (set->protocol != MAIL_PROTOCOL_ANY &&
-           access(t_strcut(set->mail_executable, ' '), X_OK) < 0) {
-               i_error("mail_executable: Can't use %s: %m",
-                       t_strcut(set->mail_executable, ' '));
-               return FALSE;
-       }
-
-       if (*set->log_path != '\0' && access(set->log_path, W_OK) < 0) {
-               dir = get_directory(set->log_path);
-               if (access(dir, W_OK) < 0) {
-                       i_error("log_path: Can't write to directory %s: %m",
-                               dir);
-                       return FALSE;
-               }
-       }
-
-       if (*set->info_log_path != '\0' &&
-           access(set->info_log_path, W_OK) < 0) {
-               dir = get_directory(set->info_log_path);
-               if (access(dir, W_OK) < 0) {
-                       i_error("info_log_path: Can't write to directory %s: %m",
-                               dir);
-                       return FALSE;
-               }
-       }
-
-       if (!syslog_facility_find(set->syslog_facility, &facility)) {
-               i_error("syslog_facility: Unknown value: %s",
-                       set->syslog_facility);
-               return FALSE;
-       }
-
-#ifndef HAVE_SSL
-       if (strcmp(set->ssl, "no") != 0) {
-               i_error("SSL support not compiled in but ssl=%s", set->ssl);
-               return FALSE;
-       }
-#endif
-       if (strcmp(set->ssl, "no") == 0 && set->disable_plaintext_auth &&
-           strncmp(set->listen, "127.", 4) != 0 &&
-           strcmp(set->protocols, "none") != 0 &&
-           !settings_have_nonplaintext_auths(set)) {
-               i_warning("There is no way to login to this server: "
-                         "disable_plaintext_auth=yes, ssl=no, "
-                         "no non-plaintext auth mechanisms.");
-       }
-
-       if (set->max_mail_processes < 1) {
-               i_error("max_mail_processes must be at least 1");
-               return FALSE;
-       }
-       if (strcmp(set->login_dir, set->base_dir) == 0) {
-               i_error("login_dir can't be the same as base_dir");
-               return FALSE;
-       }
-
        if (set->last_valid_uid != 0 &&
            set->first_valid_uid > set->last_valid_uid) {
-               i_error("first_valid_uid can't be larger than last_valid_uid");
+               *error_r = "first_valid_uid can't be larger than last_valid_uid";
                return FALSE;
        }
        if (set->last_valid_gid != 0 &&
            set->first_valid_gid > set->last_valid_gid) {
-               i_error("first_valid_gid can't be larger than last_valid_gid");
-               return FALSE;
-       }
-       if (set->mail_drop_priv_before_exec && *set->mail_chroot != '\0') {
-               i_error("mail_drop_priv_before_exec=yes and mail_chroot "
-                       "don't work together");
+               *error_r = "first_valid_gid can't be larger than last_valid_gid";
                return FALSE;
        }
 
-       if (set->protocol != MAIL_PROTOCOL_ANY &&
-           access(t_strcut(set->login_executable, ' '), X_OK) < 0) {
-               i_error("login_executable: Can't use %s: %m",
-                       t_strcut(set->login_executable, ' '));
+       /* check that we have at least one service. the actual service
+          structure validity is checked later while creating them. */
+       services = array_get(&set->services, &count);
+       if (count == 0) {
+               *error_r = "No services defined";
                return FALSE;
        }
-
-       if (set->login_processes_count < 1) {
-               i_error("login_processes_count must be at least 1");
-               return FALSE;
-       }
-
-#ifndef HAVE_MODULES
-       if (*set->mail_plugins != '\0') {
-               i_error("mail_plugins: Plugin support wasn't built into Dovecot, "
-                       "can't load plugins: %s", set->mail_plugins);
-               return FALSE;
+       for (i = 0; i < count; i++) {
+               if (*services[i]->executable != '/') {
+                       services[i]->executable =
+                               p_strconcat(pool, set->libexec_dir, "/",
+                                           services[i]->executable, NULL);
+               }
+               fix_file_listener_paths(&services[i]->unix_listeners,
+                                       pool, set->base_dir);
+               fix_file_listener_paths(&services[i]->fifo_listeners,
+                                       pool, set->base_dir);
        }
-#endif
        return TRUE;
 }
+/* </settings checks> */
 
-static bool login_want_core_dumps(struct master_server_settings *set)
-{
-       const char *p;
-
-       p = set->pop3 == NULL ? NULL :
-               strstr(set->pop3->login_executable, " -D");
-       if (p != NULL && p[3] == '\0')
-               return TRUE;
-       p = set->imap == NULL ? NULL :
-               strstr(set->imap->login_executable, " -D");
-       if (p != NULL && p[3] == '\0')
-               return TRUE;
-       return FALSE;
-}
-
-static bool settings_do_fixes(struct master_settings *set)
+bool master_settings_do_fixes(const struct master_settings *set)
 {
+       const char *login_dir;
        struct stat st;
 
        /* since base dir is under /var/run by default, it may have been
@@ -795,246 +268,117 @@ static bool settings_do_fixes(struct master_settings *set)
                i_error("stat(%s) failed: %m", set->base_dir);
                return FALSE;
        }
+       if (!S_ISDIR(st.st_mode)) {
+               i_error("%s is not a directory", set->base_dir);
+               return FALSE;
+       }
 
-       /* remove auth worker sockets left by unclean exits */
-       unlink_auth_sockets(set->base_dir, "auth-worker.");
+       /* create login directory under base dir */
+       login_dir = t_strconcat(set->base_dir, "/login", NULL);
+       if (mkdir(login_dir, 0755) < 0 && errno != EEXIST) {
+               i_error("mkdir(%s) failed: %m", login_dir);
+               return FALSE;
+       }
 
        /* Make sure our permanent state directory exists */
        if (mkdir_parents(PKG_STATEDIR, 0750) < 0 && errno != EEXIST) {
                i_error("mkdir(%s) failed: %m", PKG_STATEDIR);
                return FALSE;
        }
-
-#ifdef HAVE_MODULES
-       if (*set->mail_plugins != '\0' && set->protocol == MAIL_PROTOCOL_IMAP &&
-           *set->imap_capability == '\0') {
-               if (!get_imap_capability(set))
-                       return FALSE;
-       }
-#endif
        return TRUE;
 }
 
-static bool
-settings_fix(struct master_settings *set, bool nochecks, bool nofixes)
-{
-       /* fix relative paths */
-       fix_base_path(set, &set->login_dir);
-
-       if (nochecks)
-               return TRUE;
-       if (!settings_verify(set))
-               return FALSE;
-       return nofixes ? TRUE : settings_do_fixes(set);
-}
-
-static void
-settings_warn_needed_fds(struct master_server_settings *server ATTR_UNUSED)
-{
-#ifdef HAVE_SETRLIMIT
-       struct rlimit rlim;
-       unsigned int fd_count = 0;
-
-       if (getrlimit(RLIMIT_NOFILE, &rlim) < 0)
-               return;
-
-       /* count only log pipes needed for login and mail processes. we need
-          more, but they're the ones that can use up most of the fds */
-       if (server->imap != NULL)
-               fd_count += server->imap->login_max_processes_count;
-       if (server->pop3 != NULL)
-               fd_count += server->pop3->login_max_processes_count;
-       fd_count += server->defaults->max_mail_processes;
-
-       if (rlim.rlim_cur < fd_count) {
-               i_warning("fd limit %d is lower than what Dovecot can use under "
-                         "full load (more than %u). Either grow the limit or "
-                         "change login_max_processes_count and "
-                         "max_mail_processes master_settings",
-                         (int)rlim.rlim_cur, fd_count);
-       }
-#endif
-}
-
-static void
-config_split_all_settings(struct master_settings *set, const char *input)
-{
-       const char *p, *line;
-       string_t *str;
-
-       str = t_str_new(256);
-       p_array_init(&set->all_settings, settings_pool, 256);
-       for (p = input; *p != '\n'; p++) {
-               str_truncate(str, 0);
-               for (; *p != '='; p++) {
-                       i_assert(*p != '\n' && *p != '\0');
-                       str_append_c(str, i_toupper(*p));
-               }
-               for (; *p != '\n'; p++) {
-                       i_assert(*p != '\0');
-                       str_append_c(str, *p);
-               }
-               line = p_strdup(settings_pool, str_c(str));
-               array_append(&set->all_settings, &line, 1);
-       }
-}
-
-static int config_exec(const char *path, const char *service,
-                      struct master_settings **set_r)
+#if 0 //FIXME
+static struct master_settings *
+master_settings_read_fd(pool_t pool, int fd, const char **error_r)
 {
        struct setting_parser_context *parser;
-       string_t *all_settings;
+        struct master_settings *set;
+       struct istream *input;
        int ret;
 
-       env_put("LOG_TO_MASTER=1");
-
-       all_settings = str_new(default_pool, 10240);
-       parser = settings_parser_init(settings_pool,
-                                     &master_setting_parser_info,
+       parser = settings_parser_init(pool, &master_setting_parser_info,
                                      SETTINGS_PARSER_FLAG_IGNORE_UNKNOWN_KEYS);
-       settings_parse_save_input(parser, all_settings);
-       if ((ret = settings_parse_exec(parser, DOVECOT_CONFIG_BIN_PATH,
-                                      path, service)) == 0) {
-               *set_r = settings_parser_get(parser);
-               config_split_all_settings(*set_r, str_c(all_settings));
+       input = i_stream_create_fd(fd, 1024, FALSE);
+       ret = settings_parse_stream_read(parser, input);
+       i_stream_unref(&input);
+
+       if (ret == 0) {
+               *error_r = NULL;
+               set = settings_parser_get(parser);
+       } else {
+               *error_r = t_strdup_printf(
+                       "master: Error reading configuration: %s",
+                       settings_parser_get_error(parser));
+               set = NULL;
        }
        settings_parser_deinit(&parser);
-       str_free(&all_settings);
-       return ret;
-}
-
-int master_settings_read(const char *path,
-                        struct master_server_settings **set_r)
-{
-       struct master_server_settings *set;
-
-       p_clear(settings_pool);
-       set = p_new(settings_pool, struct master_server_settings, 1);
-
-       master_default_settings.mail_executable = NULL;
-       master_default_settings.login_executable = NULL;
-       if (config_exec(path, "", &set->defaults) < 0)
-               return -1;
-       set->defaults->protocol = MAIL_PROTOCOL_ANY;
-       set->defaults->server = set;
-
-       master_default_settings.mail_executable = PKG_LIBEXECDIR"/imap";
-       master_default_settings.login_executable = PKG_LIBEXECDIR"/imap-login";
-       if (config_exec(path, "imap", &set->imap) < 0)
-               return -1;
-       set->imap->protocol = MAIL_PROTOCOL_IMAP;
-       set->imap->server = set;
-
-       master_default_settings.mail_executable = PKG_LIBEXECDIR"/pop3";
-       master_default_settings.login_executable = PKG_LIBEXECDIR"/pop3-login";
-       if (config_exec(path, "pop3", &set->pop3) < 0)
-               return -1;
-       set->pop3->protocol = MAIL_PROTOCOL_POP3;
-       set->pop3->server = set;
-
-       *set_r = set;
-       return 0;
-}
 
-static void settings_verify_master(struct master_server_settings *set)
-{
-       if (!settings_have_connect_sockets(set->defaults)) {
-               /* we are not using external authentication, so make sure the
-                  login directory exists with correct permissions and it's
-                  empty. with external auth we wouldn't want to delete
-                  existing sockets or break the permissions required by the
-                  auth server. */
-               mode_t mode = login_want_core_dumps(set) ? 0770 : 0750;
-               if (safe_mkdir(set->defaults->login_dir, mode,
-                              master_uid, set->login_gid) == 0) {
-                       i_warning("Corrected permissions for login directory "
-                                 "%s", set->defaults->login_dir);
-               }
-
-               unlink_auth_sockets(set->defaults->login_dir, "");
-       }
+       return set;
 }
 
-bool master_settings_check(struct master_server_settings *set,
-                          bool nochecks, bool nofixes)
+struct master_settings *
+master_settings_read(pool_t pool, const char *config_binary,
+                    const char *config_path)
 {
-       struct master_auth_settings *const *auths;
-       unsigned int i, count;
-       pool_t temp;
+       struct master_settings *set;
+       const char *error;
+       pid_t pid;
+       int fd[2], status;
 
-       if ((*set->imap->protocols == '\0' ||
-            *set->pop3->protocols == '\0') && !nochecks) {
-               i_error("protocols: No protocols given in configuration file");
-               return FALSE;
-       }
-       /* --exec-mail is used if nochecks=TRUE. Allow it regardless
-          of what's in protocols setting. */
-       if (!settings_is_active(set->imap) && !nochecks) {
-               if (strcmp(set->imap->protocols, "none") == 0) {
-                       set->imap->protocol = MAIL_PROTOCOL_ANY;
-                       if (!settings_fix(set->imap, nochecks, nofixes))
-                               return FALSE;
-               }
-               set->imap = NULL;
-       } else {
-               if (!settings_fix(set->imap, nochecks, nofixes))
-                       return FALSE;
+       if (pipe(fd) < 0) {
+               i_error("pipe() failed: %m");
+               return NULL;
        }
 
-       if (!settings_is_active(set->pop3) && !nochecks)
-               set->pop3 = NULL;
-       else {
-               if (!settings_fix(set->pop3, nochecks, nofixes))
-                       return FALSE;
+       pid = fork();
+       if (pid < 0) {
+               i_error("fork() failed: %m");
+               (void)close(fd[0]);
+               (void)close(fd[1]);
+               return NULL;
        }
 
-       if (!settings_fix(set->defaults, nochecks, nofixes))
-               return FALSE;
+       if (pid == 0) {
+               /* child */
+               const char *argv[3] = { "-c", config_path, NULL };
 
-       if (!nofixes) {
-               settings_verify_master(set);
-               auths = array_get(&set->defaults->auths, &count);
-               if (count == 0) {
-                       i_error("Missing auth section");
-                       return FALSE;
-               }
+               if (dup2(fd[1], STDOUT_FILENO) < 0)
+                       i_fatal("dup2(stdout) failed: %m");
+               (void)close(fd[0]);
+               (void)close(fd[1]);
 
-               for (i = 0; i < count; i++) {
-                       if (!auth_settings_verify(set->defaults, auths[i]))
-                               return FALSE;
-               }
+               env_put("MASTER_INIT=1");
+               process_exec(config_binary, argv);
        }
 
-       if (!nochecks)
-               settings_warn_needed_fds(set);
+       /* parent. */
+       (void)close(fd[1]);
 
-       /* settings ok, swap them */
-       temp = settings_pool;
-       settings_pool = settings2_pool;
-       settings2_pool = temp;
+       set = master_settings_read_fd(pool, fd[0], &error);
+       if (close(fd[0]) < 0)
+               i_error("close(setpipe) failed: %m");
 
-       master_set = set;
-       return TRUE;
-}
-
-void master_settings_export_to_env(const struct master_settings *set)
-{
-       const char *const *sets;
-       unsigned int i, count;
-
-       sets = array_get(&set->all_settings, &count);
-       for (i = 0; i < count; i++)
-               env_put(sets[i]);
-}
+       /* if config process doesn't return successfully, assume the returned
+          configuration is invalid */
+       if (waitpid(pid, &status, 0) < 0) {
+               i_error("waitpid(config) failed: %m");
+               set = NULL;
+       } else if (status != 0) {
+               if (!WIFEXITED(status) || WEXITSTATUS(status) != FATAL_DEFAULT)
+                       process_log_status_error(pid, "config", status);
+               set = NULL;
+       } else if (set == NULL) {
+               i_error("%s", error);
+       }
 
-void master_settings_init(void)
-{
-       settings_pool = pool_alloconly_create("settings", 4096);
-       settings2_pool = pool_alloconly_create("settings2", 4096);
-}
+       if (set != NULL) {
+               if (!master_settings_verify(set))
+                       set = NULL;
+               else if (!settings_do_fixes(set))
+                       set = NULL;
+       }
 
-void master_settings_deinit(void)
-{
-       pool_unref(&settings_pool);
-       pool_unref(&settings2_pool);
+       return set;
 }
+#endif
index 535fb29a4bcead4b1f312afd659b1fc25fbb3e4b..7a1133bcec14552dbd45ec40b149cfbb511f1b79 100644 (file)
 #ifndef MASTER_SETTINGS_H
 #define MASTER_SETTINGS_H
 
-#include "network.h"
-
-#define DOVECOT_CONFIG_BIN_PATH BINDIR"/doveconf"
-
-enum mail_protocol {
-        MAIL_PROTOCOL_ANY,
-        MAIL_PROTOCOL_IMAP,
-       MAIL_PROTOCOL_POP3,
-       MAIL_PROTOCOL_LDA
+struct file_listener_settings {
+       const char *path;
+       unsigned int mode;
+       const char *user;
+       const char *group;
 };
+ARRAY_DEFINE_TYPE(file_listener_settings, struct file_listener_settings *);
 
-struct listener {
-       struct ip_addr ip;
+struct inet_listener_settings {
+       const char *address;
        unsigned int port;
-       int fd;
-       bool wanted;
 };
-ARRAY_DEFINE_TYPE(listener, struct listener);
 
-struct master_auth_socket_unix_settings {
-       const char *path;
-};
+struct service_settings {
+       struct master_settings *master_set;
 
-struct master_auth_socket_settings {
        const char *type;
-
-       ARRAY_DEFINE(masters, struct master_auth_socket_unix_settings *);
-};
-
-struct master_auth_settings {
-       const char *name;
        const char *executable;
        const char *user;
+       const char *group;
+       const char *privileged_group;
+       const char *extra_groups;
        const char *chroot;
+       const char *auth_dest_service;
 
-       unsigned int count;
-       unsigned int process_size;
+       bool drop_priv_before_exec;
 
-       const char *mechanisms;
-       bool debug;
+       unsigned int process_limit;
+       unsigned int client_limit;
+       unsigned int vsz_limit;
 
-       ARRAY_DEFINE(sockets, struct master_auth_socket_settings *);
-
-       /* .. */
-       uid_t uid;
-       gid_t gid;
+       ARRAY_TYPE(file_listener_settings) unix_listeners;
+       ARRAY_TYPE(file_listener_settings) fifo_listeners;
+       ARRAY_DEFINE(inet_listeners, struct inet_listener_settings *);
 };
 
 struct master_settings {
-       /* common */
        const char *base_dir;
-       const char *log_path;
-       const char *info_log_path;
-       const char *log_timestamp;
-       const char *syslog_facility;
-
-       /* general */
-       const char *protocols;
-       const char *listen;
-       const char *ssl_listen;
+       const char *libexec_dir;
+       unsigned int default_process_limit;
+       unsigned int default_client_limit;
 
-       const char *ssl;
-       const char *ssl_key_file;
-       unsigned int ssl_parameters_regenerate;
        bool version_ignore;
 
-       /* login */
-       const char *login_dir;
-       const char *login_executable;
-       const char *login_user;
-
-       bool login_process_per_connection;
-       bool login_chroot;
-       bool disable_plaintext_auth;
-
-       unsigned int login_process_size;
-       unsigned int login_processes_count;
-       unsigned int login_max_processes_count;
-
-       /* mail */
-       const char *valid_chroot_dirs;
-       const char *mail_chroot;
-       unsigned int max_mail_processes;
-       unsigned int mail_max_userip_connections;
-       bool verbose_proctitle;
-
        unsigned int first_valid_uid, last_valid_uid;
        unsigned int first_valid_gid, last_valid_gid;
-       const char *mail_access_groups;
-       const char *mail_privileged_group;
-       const char *mail_uid;
-       const char *mail_gid;
-
-       const char *mail_plugins;
-       const char *imap_capability;
-
-       const char *mail_location;
-       bool mail_debug;
-       bool maildir_very_dirty_syncs;
-       unsigned int dbox_purge_min_percentage;
-       bool mail_drop_priv_before_exec;
-
-       const char *mail_executable;
-       unsigned int mail_process_size;
-       const char *mail_log_prefix;
-       unsigned int mail_log_max_lines_per_sec;
-
-       /* dict */
-       unsigned int dict_process_count;
-
-       ARRAY_DEFINE(auths, struct master_auth_settings *);
-
-#ifndef CONFIG_BINARY
-       /* .. */
-       struct master_server_settings *server;
-       enum mail_protocol protocol;
 
-       ARRAY_TYPE(listener) listens;
-       ARRAY_TYPE(listener) ssl_listens;
-
-       uid_t login_uid, mail_uid_t;
-       gid_t mail_gid_t, mail_priv_gid_t;
-
-       const char *imap_generated_capability;
-       ARRAY_TYPE(const_string) all_settings;
-#endif
+       ARRAY_DEFINE(services, struct service_settings *);
 };
 
-struct master_server_settings {
-       struct master_settings *defaults;
-       struct master_settings *imap;
-       struct master_settings *pop3;
-
-       gid_t login_gid;
-};
-
-extern struct master_server_settings *master_set;
 extern struct setting_parser_info master_setting_parser_info;
 
-int master_settings_read(const char *path,
-                        struct master_server_settings **set_r);
-bool master_settings_check(struct master_server_settings *set,
-                          bool nochecks, bool nofixes);
-void master_settings_export_to_env(const struct master_settings *set);
-
-void master_settings_init(void);
-void master_settings_deinit(void);
+struct master_settings *
+master_settings_read(pool_t pool, const char *config_binary,
+                    const char *config_path);
+bool master_settings_do_fixes(const struct master_settings *set);
 
 #endif
diff --git a/src/master/service-auth-server.c b/src/master/service-auth-server.c
new file mode 100644 (file)
index 0000000..8c35418
--- /dev/null
@@ -0,0 +1,255 @@
+/* Copyright (c) 2005-2009 Dovecot authors, see the included COPYING file */
+
+#include "common.h"
+#include "ioloop.h"
+#include "istream.h"
+#include "ostream.h"
+#include "hash.h"
+#include "service.h"
+#include "service-process.h"
+#include "service-auth-server.h"
+#include "service-auth-source.h"
+#include "../auth/auth-master-interface.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+
+#define AUTH_MAX_INBUF_SIZE 8192
+
+static void
+service_process_auth_request_free(struct service_process_auth_request *request)
+{
+       if (request->fd != -1) {
+               if (close(request->fd) < 0)
+                       i_error("close(auth request fd) failed: %m");
+       }
+       i_free(request);
+}
+
+static void
+service_process_auth_server_close(struct service_process_auth_server *process)
+{
+       struct hash_iterate_context *iter;
+       void *key, *value;
+
+       if (process->auth_requests != NULL) {
+               iter = hash_table_iterate_init(process->auth_requests);
+               while (hash_table_iterate(iter, &key, &value)) {
+                       struct service_process_auth_request *request = value;
+
+                       service_process_unref(&request->process->process);
+                       service_process_auth_request_free(request);
+               }
+               hash_table_iterate_deinit(&iter);
+               hash_table_destroy(&process->auth_requests);
+       }
+
+       if (process->auth_input != NULL)
+               i_stream_close(process->auth_input);
+       if (process->auth_output != NULL)
+               o_stream_close(process->auth_output);
+
+       if (process->io_auth != NULL)
+               io_remove(&process->io_auth);
+       if (process->auth_fd != -1) {
+               if (close(process->auth_fd) < 0)
+                       i_error("close(auth_fd) failed: %m");
+               process->auth_fd = -1;
+       }
+}
+
+static struct service_process_auth_request *
+auth_process_lookup_request(struct service_process_auth_server *process,
+                           unsigned int id)
+{
+        struct service_process_auth_request *request;
+
+       request = hash_table_lookup(process->auth_requests, POINTER_CAST(id));
+       if (request == NULL) {
+               i_error("service(%s): authentication service %s "
+                       "sent reply with unknown ID %u",
+                       process->process.service->name,
+                       dec2str(process->process.pid), id);
+               return NULL;
+       }
+
+       hash_table_remove(process->auth_requests, POINTER_CAST(id));
+       if (!service_process_unref(&request->process->process)) {
+               /* process already died. */
+               service_process_auth_request_free(request);
+               return NULL;
+       }
+
+       return request;
+}
+
+static int
+auth_process_input_user(struct service_process_auth_server *process, const char *args)
+{
+        struct service_process_auth_request *request;
+       const char *const *list;
+       enum master_auth_status status;
+       unsigned int id;
+
+       /* <id> <userid> [..] */
+
+       list = t_strsplit(args, "\t");
+       if (list[0] == NULL || list[1] == NULL) {
+               i_error("BUG: Auth process %s sent corrupted USER line",
+                       dec2str(process->process.pid));
+               return FALSE;
+       }
+       id = (unsigned int)strtoul(list[0], NULL, 10);
+
+        request = auth_process_lookup_request(process, id);
+       if (request != NULL) {
+               struct service *dest_service =
+                       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);
+               status = dest_process != NULL ?
+                       MASTER_AUTH_STATUS_OK :
+                       MASTER_AUTH_STATUS_INTERNAL_ERROR;
+               service_process_auth_source_send_reply(request->process,
+                                                      request->process_tag,
+                                                      status);
+               service_process_auth_request_free(request);
+       }
+       return TRUE;
+}
+
+static int
+auth_process_input_notfound(struct service_process_auth_server *process,
+                           const char *args)
+{
+        struct service_process_auth_request *request;
+       unsigned int id;
+
+       id = (unsigned int)strtoul(args, NULL, 10);
+
+        request = auth_process_lookup_request(process, id);
+       if (request != NULL) {
+               service_process_auth_source_send_reply(request->process,
+                                                      request->process_tag,
+                                                      FALSE);
+               service_process_auth_request_free(request);
+       }
+       return TRUE;
+}
+
+static int
+auth_process_input_fail(struct service_process_auth_server *process,
+                       const char *args)
+{
+        struct service_process_auth_request *request;
+       const char *error;
+       unsigned int id;
+
+       error = strchr(args, '\t');
+       if (error != NULL)
+               error++;
+
+       id = (unsigned int)strtoul(args, NULL, 10);
+
+        request = auth_process_lookup_request(process, id);
+       if (request != NULL) {
+               service_process_auth_source_send_reply(request->process,
+                                                      request->process_tag,
+                                                      FALSE);
+               service_process_auth_request_free(request);
+       }
+       return TRUE;
+}
+
+static void
+service_process_auth_server_input(struct service_process_auth_server *process)
+{
+       const char *line;
+       int ret;
+
+       switch (i_stream_read(process->auth_input)) {
+       case 0:
+               return;
+       case -1:
+               /* disconnected */
+               service_process_auth_server_close(process);
+               return;
+       case -2:
+               /* buffer full */
+               i_error("service(%s): authentication server process %s "
+                       "sent us too long line", process->process.service->name,
+                       dec2str(process->process.pid));
+               service_process_auth_server_close(process);
+               return;
+       }
+
+       if (!process->auth_version_received) {
+               line = i_stream_next_line(process->auth_input);
+               if (line == NULL)
+                       return;
+
+               /* make sure the major version matches */
+               if (strncmp(line, "VERSION\t", 8) != 0 ||
+                   atoi(t_strcut(line + 8, '\t')) !=
+                   AUTH_MASTER_PROTOCOL_MAJOR_VERSION) {
+                       i_error("service(%s): authentication server process %s "
+                               "not compatible with master process "
+                               "(mixed old and new binaries?)",
+                               process->process.service->name,
+                               dec2str(process->process.pid));
+                       service_process_auth_server_close(process);
+                       return;
+               }
+               process->auth_version_received = TRUE;
+       }
+
+       while ((line = i_stream_next_line(process->auth_input)) != NULL) {
+               if (strncmp(line, "USER\t", 5) == 0)
+                       ret = auth_process_input_user(process, line + 5);
+               else if (strncmp(line, "NOTFOUND\t", 9) == 0)
+                       ret = auth_process_input_notfound(process, line + 9);
+               else if (strncmp(line, "FAIL\t", 5) == 0)
+                       ret = auth_process_input_fail(process, line + 5);
+               else
+                       ret = TRUE;
+
+               if (!ret) {
+                       service_process_auth_server_close(process);
+                       break;
+               }
+       }
+}
+
+void service_process_auth_server_init(struct service_process *_process, int fd)
+{
+       struct service_process_auth_server *process =
+               (struct service_process_auth_server *)_process;
+
+       i_assert(_process->service->type == SERVICE_TYPE_AUTH_SERVER);
+
+       process->auth_fd = fd;
+       process->auth_input = i_stream_create_fd(process->auth_fd,
+                                                AUTH_MAX_INBUF_SIZE, FALSE);
+       process->auth_output =
+               o_stream_create_fd(fd, (size_t)-1, FALSE);
+       process->io_auth =
+               io_add(process->auth_fd, IO_READ,
+                      service_process_auth_server_input, process);
+       process->auth_requests =
+               hash_table_create(default_pool, default_pool, 0, NULL, NULL);
+}
+
+void service_process_auth_server_deinit(struct service_process *_process)
+{
+       struct service_process_auth_server *process =
+               (struct service_process_auth_server *)_process;
+
+       i_assert(_process->service->type == SERVICE_TYPE_AUTH_SERVER);
+
+       service_process_auth_server_close(process);
+}
diff --git a/src/master/service-auth-server.h b/src/master/service-auth-server.h
new file mode 100644 (file)
index 0000000..a8d8603
--- /dev/null
@@ -0,0 +1,9 @@
+#ifndef SERVICE_AUTH_SERVER_H
+#define SERVICE_AUTH_SERVER_H
+
+struct service_process;
+
+void service_process_auth_server_init(struct service_process *process, int fd);
+void service_process_auth_server_deinit(struct service_process *process);
+
+#endif
diff --git a/src/master/service-auth-source.c b/src/master/service-auth-source.c
new file mode 100644 (file)
index 0000000..057056f
--- /dev/null
@@ -0,0 +1,276 @@
+/* Copyright (c) 2005-2009 Dovecot authors, see the included COPYING file */
+
+#include "common.h"
+#include "hash.h"
+#include "str.h"
+#include "ioloop.h"
+#include "ostream.h"
+#include "fdpass.h"
+#include "fd-close-on-exec.h"
+#include "../auth/auth-master-interface.h"
+#include "service.h"
+#include "service-process.h"
+#include "service-auth-source.h"
+
+#include <unistd.h>
+#include <sys/stat.h>
+
+#define AUTH_SOURCE_OUTBUF_THROTTLE_THRESHOLD (1024 - 256)
+#define AUTH_SERVER_MAX_OUTBUF_SIZE (1024*64)
+#define AUTH_BUSY_LOG_INTERVAL 30
+
+static void
+service_process_auth_source_input(struct service_process_auth_source *process);
+
+static void
+service_process_auth_source_close(struct service_process_auth_source *process)
+{
+       if (process->auth_output != NULL)
+               o_stream_close(process->auth_output);
+       if (process->io_auth != NULL)
+               io_remove(&process->io_auth);
+       if (process->auth_fd != -1) {
+               if (close(process->auth_fd) < 0)
+                       i_error("close(auth_fd) failed: %m");
+               process->auth_fd = -1;
+       }
+}
+
+static int
+process_auth_source_output(struct service_process_auth_source *process)
+{
+       int ret;
+
+       if ((ret = o_stream_flush(process->auth_output)) < 0)
+               return -1;
+
+       if (process->io_auth == NULL &&
+           o_stream_get_buffer_used_size(process->auth_output) <
+           AUTH_SOURCE_OUTBUF_THROTTLE_THRESHOLD) {
+               /* enable parsing input again */
+               o_stream_unset_flush_callback(process->auth_output);
+               process->io_auth = io_add(process->auth_fd, IO_READ,
+                                         service_process_auth_source_input,
+                                         process);
+       }
+       return ret;
+}
+
+void service_process_auth_source_send_reply(struct service_process_auth_source *process,
+                                           unsigned int tag,
+                                           enum master_auth_status status)
+{
+       struct master_auth_reply reply;
+
+       if (o_stream_get_buffer_used_size(process->auth_output) >=
+           AUTH_SOURCE_OUTBUF_THROTTLE_THRESHOLD) {
+               /* not reading our output. stop parsing input until it will. */
+               if (process->io_auth != NULL) {
+                       io_remove(&process->io_auth);
+
+                       o_stream_set_flush_callback(process->auth_output,
+                                                   process_auth_source_output,
+                                                   process);
+               }
+       }
+
+       /* Reply to login process */
+       memset(&reply, 0, sizeof(reply));
+       reply.tag = tag;
+       reply.status = status;
+
+       o_stream_send(process->auth_output, &reply, sizeof(reply));
+}
+
+static unsigned int
+auth_server_send_request(struct service_process_auth_server *server_process,
+                        struct service_process_auth_source *source_process,
+                        unsigned int auth_id)
+{
+       unsigned int tag = 0;
+       string_t *str;
+
+       while (tag == 0)
+                tag = ++server_process->auth_tag_counter;
+
+       str = t_str_new(256);
+       if (!server_process->auth_version_sent) {
+                server_process->auth_version_sent = TRUE;
+               str_printfa(str, "VERSION\t%u\t%u\n",
+                           AUTH_MASTER_PROTOCOL_MAJOR_VERSION,
+                           AUTH_MASTER_PROTOCOL_MINOR_VERSION);
+               o_stream_send(server_process->auth_output,
+                             str_data(str), str_len(str));
+               str_truncate(str, 0);
+       }
+
+       str_printfa(str, "REQUEST\t%u\t%s\t%u\n",
+                   tag, dec2str(source_process->process.pid), auth_id);
+       o_stream_send(server_process->auth_output, str_data(str), str_len(str));
+       return tag;
+}
+
+static int
+auth_read_request(struct service_process_auth_source *process,
+                 struct master_auth_request *req,
+                 unsigned char data[MASTER_AUTH_MAX_DATA_SIZE],
+                 int *client_fd_r)
+{
+       struct service *service = process->process.service;
+       struct stat st;
+       ssize_t ret;
+
+       *client_fd_r = -1;
+
+       ret = fd_read(process->auth_fd, req, sizeof(*req), client_fd_r);
+       if (ret != sizeof(*req)) {
+               if (ret == 0) {
+                       /* disconnected */
+               } else if (ret > 0) {
+                       /* request wasn't fully read */
+                       i_error("service(%s): fd_read() partial input (%d/%d)",
+                               service->name, (int)ret, (int)sizeof(*req));
+               } else {
+                       if (errno == EAGAIN)
+                               return 0;
+
+                       i_error("service(%s): fd_read() failed: %m",
+                               service->name);
+               }
+               return -1;
+       }
+
+       if (req->data_size != 0) {
+               if (req->data_size > MASTER_AUTH_MAX_DATA_SIZE) {
+                       i_error("service(%s): Too large auth data_size sent",
+                               service->name);
+                       return -1;
+               }
+               /* @UNSAFE */
+               ret = read(process->auth_fd, data, req->data_size);
+               if (ret != (ssize_t)req->data_size) {
+                       if (ret == 0) {
+                               /* disconnected */
+                       } else if (ret > 0) {
+                               /* request wasn't fully read */
+                               i_error("service(%s): Data read partially %d/%u",
+                                       service->name, (int)ret, req->data_size);
+                       } else {
+                               i_error("service(%s): read(data) failed: %m",
+                                       service->name);
+                       }
+                       return -1;
+               }
+       }
+
+       if (*client_fd_r == -1) {
+               i_error("service(%s): Auth request missing a file descriptor",
+                       service->name);
+               return -1;
+       }
+
+       if (fstat(*client_fd_r, &st) < 0) {
+               i_error("service(%s): fstat(auth dest fd) failed: %m",
+                       service->name);
+               return -1;
+       }
+       if (st.st_ino != req->ino) {
+               i_error("service(%s): Auth request inode mismatch: %s != %s",
+                       service->name, dec2str(st.st_ino), dec2str(req->ino));
+               return -1;
+       }
+       return 1;
+}
+
+static void
+service_process_auth_source_input(struct service_process_auth_source *process)
+{
+       struct service *service = process->process.service;
+       struct service_process_auth_server *auth_process;
+       struct service_process_auth_request *auth_req;
+       struct master_auth_request req;
+       unsigned char data[MASTER_AUTH_MAX_DATA_SIZE];
+       unsigned int tag;
+       ssize_t ret;
+       int client_fd;
+
+       ret = auth_read_request(process, &req, data, &client_fd);
+       if (ret == 0)
+               return;
+       if (ret < 0) {
+               if (client_fd != -1) {
+                       if (close(client_fd) < 0)
+                               i_error("login: close(mail client) failed: %m");
+               }
+               service_process_auth_source_close(process);
+               return;
+       }
+       fd_close_on_exec(client_fd, TRUE);
+
+       /* we have a request. check its validity. */
+       auth_process = hash_table_lookup(service->list->pids, &req.auth_pid);
+       if (auth_process == NULL) {
+               i_error("service(%s): authentication request for unknown "
+                       "auth server PID %s", service->name,
+                       dec2str(req.auth_pid));
+               service_process_auth_source_send_reply(process, req.tag, FALSE);
+               (void)close(client_fd);
+               return;
+       }
+
+       if (o_stream_get_buffer_used_size(auth_process->auth_output) >=
+           AUTH_SERVER_MAX_OUTBUF_SIZE) {
+               if (auth_process->auth_busy_stamp <=
+                   ioloop_time - AUTH_BUSY_LOG_INTERVAL) {
+                       i_warning("service(%s): authentication server PID "
+                                 "%s too busy",
+                                 auth_process->process.service->name,
+                                 dec2str(req.auth_pid));
+                        auth_process->auth_busy_stamp = ioloop_time;
+               }
+               service_process_auth_source_send_reply(process, req.tag, FALSE);
+               (void)close(client_fd);
+               return;
+       }
+
+       /* ok, we have a non-busy authentication server.
+          send a request to it. */
+       auth_req = i_malloc(sizeof(*auth_req) + req.data_size);
+       auth_req->process = process;
+       auth_req->process_tag = req.tag;
+       auth_req->fd = client_fd;
+       auth_req->local_ip = req.local_ip;
+       auth_req->remote_ip = req.remote_ip;
+       auth_req->data_size = req.data_size;
+       memcpy(auth_req->data, data, req.data_size);
+
+       tag = auth_server_send_request(auth_process, process, req.auth_id);
+
+       service_process_ref(&process->process);
+       hash_table_insert(auth_process->auth_requests,
+                         POINTER_CAST(tag), auth_req);
+}
+
+void service_process_auth_source_init(struct service_process *_process, int fd)
+{
+       struct service_process_auth_source *process =
+               (struct service_process_auth_source *)_process;
+
+       i_assert(_process->service->type == SERVICE_TYPE_AUTH_SOURCE);
+
+       process->auth_fd = fd;
+       process->io_auth = io_add(process->auth_fd, IO_READ,
+                                 service_process_auth_source_input, process);
+       process->auth_output =
+               o_stream_create_fd(fd, (size_t)-1, FALSE);
+}
+
+void service_process_auth_source_deinit(struct service_process *_process)
+{
+       struct service_process_auth_source *process =
+               (struct service_process_auth_source *)_process;
+
+       i_assert(_process->service->type == SERVICE_TYPE_AUTH_SOURCE);
+
+       service_process_auth_source_close(process);
+}
diff --git a/src/master/service-auth-source.h b/src/master/service-auth-source.h
new file mode 100644 (file)
index 0000000..dee27a2
--- /dev/null
@@ -0,0 +1,14 @@
+#ifndef SERVICE_AUTH_SOURCE_H
+#define SERVICE_AUTH_SOURCE_H
+
+struct service_process;
+struct service_process_auth_source;
+
+void service_process_auth_source_init(struct service_process *process, int fd);
+void service_process_auth_source_deinit(struct service_process *process);
+
+void service_process_auth_source_send_reply(struct service_process_auth_source *process,
+                                           unsigned int tag,
+                                           enum master_auth_status status);
+
+#endif
diff --git a/src/master/service-listen.c b/src/master/service-listen.c
new file mode 100644 (file)
index 0000000..2a07710
--- /dev/null
@@ -0,0 +1,262 @@
+/* Copyright (c) 2005-2009 Dovecot authors, see the included COPYING file */
+
+#include "common.h"
+#include "array.h"
+#include "fd-set-nonblock.h"
+#include "fd-close-on-exec.h"
+#include "network.h"
+#include "service.h"
+#include "service-listen.h"
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+static int service_unix_listener_listen(struct service_listener *l)
+{
+        struct service *service = l->service;
+       const struct file_listener_settings *set = l->set.fileset.set;
+       mode_t old_umask;
+       int fd, i;
+
+       old_umask = umask((set->mode ^ 0777) & 0777);
+       for (i = 0;; i++) {
+               fd = net_listen_unix(set->path, service->process_limit);
+               if (fd != -1)
+                       break;
+
+               if (errno == EISDIR || errno == ENOENT) {
+                       /* looks like the path doesn't exist. */
+                       return 0;
+               }
+
+               if (errno != EADDRINUSE) {
+                       i_error("service(%s): net_listen_unix(%s) failed: %m",
+                               service->name, set->path);
+                       return -1;
+               }
+
+               /* already in use - see if it really exists.
+                  after 3 times just fail here. */
+               fd = net_connect_unix(set->path);
+               if (fd != -1 || errno != ECONNREFUSED || i >= 3) {
+                       if (fd != -1)
+                               (void)close(fd);
+                       i_error("service(%s): Socket already exists: %s",
+                               service->name, set->path);
+                       return 0;
+               }
+
+               /* delete and try again */
+               if (unlink(set->path) < 0 && errno != ENOENT) {
+                       i_error("service(%s): unlink(%s) failed: %m",
+                               service->name, set->path);
+                       return -1;
+               }
+       }
+       umask(old_umask);
+
+       i_assert(fd != -1);
+
+       /* see if we need to change its owner/group */
+       if ((service->uid != (uid_t)-1 && service->uid != master_uid) ||
+           (service->gid != (gid_t)-1 && service->gid != master_gid)) {
+               if (chown(set->path, service->uid, service->gid) < 0) {
+                       i_error("chown(%s, %s, %s) failed: %m", set->path,
+                               dec2str(service->uid), dec2str(service->gid));
+                       (void)close(fd);
+                       return -1;
+               }
+       }
+
+       net_set_nonblock(fd, TRUE);
+       fd_close_on_exec(fd, TRUE);
+
+       l->fd = fd;
+       return 1;
+}
+
+static int service_fifo_listener_listen(struct service_listener *l)
+{
+        struct service *service = l->service;
+       const struct file_listener_settings *set = l->set.fileset.set;
+       mode_t old_umask;
+       int fd, ret;
+
+       old_umask = umask((set->mode ^ 0777) & 0777);
+       ret = mkfifo(set->path, set->mode);
+       umask(old_umask);
+
+       if (ret < 0 && errno != EEXIST) {
+               i_error("service(%s): mkfifo(%s) failed: %m",
+                       service->name, set->path);
+               return -1;
+       }
+
+       fd = open(set->path, O_RDONLY);
+       if (fd == -1) {
+               i_error("service(%s): open(%s) failed: %m",
+                       service->name, set->path);
+               return -1;
+       }
+
+       /* see if we need to change its owner/group */
+       if ((service->uid != (uid_t)-1 && service->uid != master_uid) ||
+           (service->gid != (gid_t)-1 && service->gid != master_gid)) {
+               if (chown(set->path, service->uid, service->gid) < 0) {
+                       i_error("chown(%s, %s, %s) failed: %m", set->path,
+                               dec2str(service->uid), dec2str(service->gid));
+                       (void)close(fd);
+                       return -1;
+               }
+       }
+
+       fd_set_nonblock(fd, TRUE);
+       fd_close_on_exec(fd, TRUE);
+
+       l->fd = fd;
+       return 1;
+}
+
+static int service_inet_listener_listen(struct service_listener *l)
+{
+        struct service *service = l->service;
+       const struct inet_listener_settings *set = l->set.inetset.set;
+       unsigned int port = set->port;
+       int fd;
+
+       fd = net_listen(&l->set.inetset.ip, &port, service->process_limit);
+       if (fd < 0) {
+               i_error("service(%s): listen(%s, %u) failed: %m",
+                       service->name, set->address, set->port);
+               return errno == EADDRINUSE ? 0 : -1;
+       }
+       net_set_nonblock(fd, TRUE);
+       fd_close_on_exec(fd, TRUE);
+
+       l->fd = fd;
+       return 1;
+}
+
+static int service_listen(struct service *service)
+{
+       struct service_listener *const *listeners;
+       unsigned int i, count;
+       int ret = 1, ret2 = 0;
+
+       listeners = array_get(&service->listeners, &count);
+       for (i = 0; i < count; i++) {
+               if (listeners[i]->fd != -1)
+                       continue;
+
+               switch (listeners[i]->type) {
+               case SERVICE_LISTENER_UNIX:
+                       ret2 = service_unix_listener_listen(listeners[i]);
+                       break;
+               case SERVICE_LISTENER_FIFO:
+                       ret2 = service_fifo_listener_listen(listeners[i]);
+                       break;
+               case SERVICE_LISTENER_INET:
+                       ret2 = service_inet_listener_listen(listeners[i]);
+                       break;
+               }
+
+               if (ret2 < ret)
+                       ret = ret2;
+       }
+
+       return ret;
+}
+
+int services_listen(struct service_list *service_list)
+{
+       struct service *const *services;
+       unsigned int i, count;
+       int ret = 1, ret2;
+
+       services = array_get(&service_list->services, &count);
+       for (i = 0; i < count; i++) {
+               ret2 = service_listen(services[i]);
+               if (ret2 < ret)
+                       ret = ret2;
+       }
+       return ret;
+}
+
+static int listener_equals(const struct service_listener *l1,
+                          const struct service_listener *l2)
+{
+       if (l1->type != l2->type)
+               return FALSE;
+
+       switch (l1->type) {
+       case SERVICE_LISTENER_UNIX:
+       case SERVICE_LISTENER_FIFO:
+               if (strcmp(l1->set.fileset.set->path,
+                          l2->set.fileset.set->path) != 0)
+                       return FALSE;
+               if (l1->set.fileset.set->mode != l2->set.fileset.set->mode)
+                       return FALSE;
+               if (l1->set.fileset.uid != l2->set.fileset.uid)
+                       return FALSE;
+               if (l1->set.fileset.gid != l2->set.fileset.gid)
+                       return FALSE;
+               return TRUE;
+       case SERVICE_LISTENER_INET:
+               if (memcmp(&l1->set.inetset.ip, &l2->set.inetset.ip,
+                          sizeof(l1->set.inetset.ip)) != 0)
+                       return FALSE;
+               if (l1->set.inetset.set->port != l2->set.inetset.set->port)
+                       return FALSE;
+               return TRUE;
+       }
+       return FALSE;
+}
+
+int services_listen_using(struct service_list *new_service_list,
+                         struct service_list *old_service_list)
+{
+       struct service *const *services;
+       ARRAY_DEFINE(new_listeners_arr, struct service_listener *);
+       ARRAY_DEFINE(old_listeners_arr, struct service_listener *);
+       struct service_listener *const *new_listeners, *const *old_listeners;
+       unsigned int i, j, count, new_count, old_count;
+
+       /* first create an arrays of all listeners to make things easier */
+       t_array_init(&new_listeners_arr, 64);
+       services = array_get(&new_service_list->services, &count);
+       for (i = 0; i < count; i++)
+               array_append_array(&new_listeners_arr, &services[i]->listeners);
+
+       t_array_init(&old_listeners_arr, 64);
+       services = array_get(&old_service_list->services, &count);
+       for (i = 0; i < count; i++)
+               array_append_array(&old_listeners_arr, &services[i]->listeners);
+
+       /* then start moving fds */
+       new_listeners = array_get(&new_listeners_arr, &new_count);
+       old_listeners = array_get(&old_listeners_arr, &old_count);
+
+       for (i = 0; i < new_count; i++) {
+               for (j = 0; j < old_count; j++) {
+                       if (old_listeners[j]->fd != -1 &&
+                           listener_equals(new_listeners[i],
+                                           old_listeners[j])) {
+                               new_listeners[i]->fd = old_listeners[j]->fd;
+                                old_listeners[j]->fd = -1;
+                               break;
+                       }
+               }
+       }
+
+       /* close what's left */
+       for (j = 0; j < old_count; j++) {
+               if (old_listeners[j]->fd != -1) {
+                       if (close(old_listeners[j]->fd) < 0)
+                               i_error("close(listener) failed: %m");
+               }
+       }
+
+       /* and let services_listen() deal with the remaining fds */
+       return services_listen(new_service_list);
+}
diff --git a/src/master/service-listen.h b/src/master/service-listen.h
new file mode 100644 (file)
index 0000000..dc792fa
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef SERVICE_LISTEN_H
+#define SERVICE_LISTEN_H
+
+/* Start listening in all services. Returns -1 for fatal failures,
+   0 if some of the addresses are already being used or path for
+   unix socket was lost, 1 if all is ok. It's safe to call this function
+   multiple times. */
+int services_listen(struct service_list *service_list);
+
+/* Move common listener fds from old_services to new_services, close those
+   that aren't needed anymore and finally call services_listen() to add
+   missing listeners. */
+int services_listen_using(struct service_list *new_service_list,
+                         struct service_list *old_service_list);
+
+#endif
diff --git a/src/master/service-log.c b/src/master/service-log.c
new file mode 100644 (file)
index 0000000..6d0abe2
--- /dev/null
@@ -0,0 +1,101 @@
+/* Copyright (c) 2005-2009 Dovecot authors, see the included COPYING file */
+
+#include "common.h"
+#include "fd-close-on-exec.h"
+#include "array.h"
+#include "service.h"
+#include "service-log.h"
+
+#include <unistd.h>
+
+int services_log_init(struct service_list *service_list)
+{
+       struct log_service_handshake handshake;
+       struct service *const *services;
+       unsigned int i, count;
+       buffer_t *handshake_buf;
+       ssize_t ret = 0;
+
+       memset(&handshake, 0, sizeof(handshake));
+       handshake.log_magic = MASTER_LOG_MAGIC;
+
+       handshake_buf = buffer_create_dynamic(default_pool, 256);
+       services = array_get(&service_list->services, &count);
+       for (i = 0; i < count; i++) {
+               if (services[i]->type == SERVICE_TYPE_LOG)
+                       continue;
+
+               i_assert(services[i]->log_fd[0] == -1);
+               if (pipe(services[i]->log_fd) < 0) {
+                       i_error("pipe() failed: %m");
+                       ret = -1;
+                       break;
+               }
+               fd_close_on_exec(services[i]->log_fd[0], TRUE);
+               fd_close_on_exec(services[i]->log_fd[1], TRUE);
+
+               handshake.prefix_len = strlen(services[i]->name) + 2;
+
+               buffer_set_used_size(handshake_buf, 0);
+               buffer_append(handshake_buf, &handshake, sizeof(handshake));
+               buffer_append(handshake_buf, services[i]->name,
+                             strlen(services[i]->name));
+               buffer_append(handshake_buf, ": ", 2);
+
+               ret = write(services[i]->log_fd[1],
+                           handshake_buf->data, handshake_buf->used);
+               if (ret < 0) {
+                       i_error("write(log handshake) failed: %m");
+                       break;
+               }
+               if ((size_t)ret != handshake_buf->used) {
+                       i_error("write(log handshake) didn't write everything");
+                       ret = -1;
+                       break;
+               }
+       }
+       buffer_free(&handshake_buf);
+       if (ret < 0) {
+               services_log_deinit(service_list);
+               return -1;
+       }
+       return 0;
+}
+
+void services_log_deinit(struct service_list *service_list)
+{
+       struct service *const *services;
+       unsigned int i, count;
+
+       services = array_get(&service_list->services, &count);
+       for (i = 0; i < count; i++) {
+               if (services[i]->log_fd[0] != -1) {
+                       if (close(services[i]->log_fd[0]) < 0) {
+                               i_error("service(%s): close(log_fd) failed: %m",
+                                       services[i]->name);
+                       }
+                       if (close(services[i]->log_fd[1]) < 0) {
+                               i_error("service(%s): close(log_fd) failed: %m",
+                                       services[i]->name);
+                       }
+                       services[i]->log_fd[0] = -1;
+                       services[i]->log_fd[1] = -1;
+               }
+       }
+}
+
+void services_log_dup2(ARRAY_TYPE(dup2) *dups,
+                      struct service_list *service_list,
+                      unsigned int first_fd, unsigned int *fd_count)
+{
+       struct service *const *services;
+       unsigned int i, n, count;
+
+       services = array_get(&service_list->services, &count);
+       for (i = n = 0; i < count; i++) {
+               if (services[i]->log_fd[1] != -1) {
+                       dup2_append(dups, services[i]->log_fd[0], first_fd + n);
+                       n++; *fd_count += 1;
+               }
+       }
+}
diff --git a/src/master/service-log.h b/src/master/service-log.h
new file mode 100644 (file)
index 0000000..475c91d
--- /dev/null
@@ -0,0 +1,13 @@
+#ifndef SERVICE_LOG_H
+#define SERVICE_LOG_H
+
+#include "dup2-array.h"
+
+int services_log_init(struct service_list *service_list);
+void services_log_deinit(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);
+
+#endif
diff --git a/src/master/service-monitor.c b/src/master/service-monitor.c
new file mode 100644 (file)
index 0000000..0ede21d
--- /dev/null
@@ -0,0 +1,274 @@
+/* Copyright (c) 2005-2009 Dovecot authors, see the included COPYING file */
+
+#include "common.h"
+#include "array.h"
+#include "ioloop.h"
+#include "fd-close-on-exec.h"
+#include "hash.h"
+#include "service.h"
+#include "service-process.h"
+#include "service-log.h"
+#include "service-monitor.h"
+
+#include <unistd.h>
+#include <sys/wait.h>
+#include <syslog.h>
+
+#define THROTTLE_TIMEOUT (1000*60)
+
+static void service_monitor_stop(struct service *service);
+static void service_monitor_listen_start(struct service *service);
+static void service_monitor_listen_stop(struct service *service);
+
+static void service_status_input(struct service *service)
+{
+        struct master_status status;
+        struct service_process *process;
+       ssize_t ret;
+
+       ret = read(service->status_fd[0], &status, sizeof(status));
+       switch (ret) {
+       case 0:
+               i_error("service(%s): read(status) failed: EOF", service->name);
+               service_monitor_stop(service);
+               return;
+       case -1:
+               i_error("service(%s): read(status) failed: %m", service->name);
+               service_monitor_stop(service);
+               return;
+       default:
+               i_error("service(%s): child %s sent partial status update "
+                       "(%d bytes)", service->name,
+                       dec2str(status.pid), (int)ret);
+               return;
+
+       case sizeof(status):
+               break;
+       }
+
+       process = hash_table_lookup(service->list->pids, &status.pid);
+       if (process == NULL) {
+               /* we've probably wait()ed it away already. ignore */
+               return;
+       }
+
+       if (process->uid != status.uid || process->service != service) {
+               /* a) Process was closed and another process was created with
+                  the same PID, but we're still receiving status update from
+                  the old process.
+
+                  b) Some process is trying to corrupt our internal state by
+                  trying to pretend to be someone else. We could use stronger
+                  randomness here, but the worst they can do is DoS and there
+                  are already more serious problems if someone is able to do
+                  this.. */
+               i_error("service(%s): Ignoring invalid update from child %s "
+                       "(UID=%u)", service->name, dec2str(status.pid),
+                       status.uid);
+               return;
+       }
+
+       if (process->to_status != NULL) {
+               /* first status notification */
+               timeout_remove(&process->to_status);
+       }
+
+       if (process->available_count == status.available_count)
+               return;
+
+       if (process->available_count > status.available_count) {
+               /* process started servicing requests */
+               process->total_count +=
+                       process->available_count - status.available_count;
+               if (status.available_count == 0) {
+                       i_assert(service->process_avail > 0);
+                       if (--service->process_avail == 0)
+                                service_monitor_listen_start(service);
+               }
+               process->idle_start = 0;
+       } else {
+               /* process finished servicing requests */
+               if (process->available_count == 0) {
+                       if (service->process_avail++ == 0)
+                                service_monitor_listen_stop(service);
+                       i_assert(service->process_avail <=
+                                service->process_count);
+               }
+               if (status.available_count == service->set->client_limit)
+                       process->idle_start = ioloop_time;
+       }
+       process->available_count = status.available_count;
+}
+
+static void service_throttle_timeout(struct service *service)
+{
+       timeout_remove(&service->to_throttle);
+       service_monitor_listen_start(service);
+}
+
+static void service_monitor_throttle(struct service *service)
+{
+       if (service->to_throttle != NULL)
+               return;
+
+       i_error("service(%s): command startup failed, throttling",
+               service->name);
+       service_monitor_listen_stop(service);
+
+       service->to_throttle = timeout_add(THROTTLE_TIMEOUT,
+                                          service_throttle_timeout, service);
+}
+
+static void service_accept(struct service *service)
+{
+       i_assert(service->process_avail == 0);
+
+       if (service->process_count == service->process_limit) {
+               /* we've reached our limits, new connections will have to
+                  wait until there are more processes available */
+               service->listen_pending = TRUE;
+                service_monitor_listen_stop(service);
+               return;
+       }
+
+       /* create a child process and let it accept() this connection */
+       if (service_process_create(service, NULL, -1, NULL, 0) == NULL)
+               service_monitor_throttle(service);
+       else
+               service_monitor_listen_stop(service);
+}
+
+static void service_monitor_listen_start(struct service *service)
+{
+       struct service_listener *const *listeners;
+       unsigned int i, count;
+
+       service->listen_pending = FALSE;
+
+       listeners = array_get(&service->listeners, &count);
+       for (i = 0; i < count; i++) {
+               if (listeners[i]->io == NULL && listeners[i]->fd != -1) {
+                       listeners[i]->io = io_add(listeners[i]->fd, IO_READ,
+                                                 service_accept, service);
+               }
+       }
+}
+
+static void service_monitor_listen_stop(struct service *service)
+{
+       struct service_listener *const *listeners;
+       unsigned int i, count;
+
+       listeners = array_get(&service->listeners, &count);
+       for (i = 0; i < count; i++) {
+               struct service_listener *l = listeners[i];
+
+               if (l->io != NULL)
+                       io_remove(&l->io);
+       }
+}
+
+void services_monitor_start(struct service_list *service_list)
+{
+       struct service *const *services;
+       unsigned int i, count;
+
+       services_log_init(service_list);
+
+       services = array_get(&service_list->services, &count);
+       for (i = 0; i < count; i++) {
+               if (services[i]->status_fd[0] == -1) {
+                       /* we haven't yet created status pipe */
+                       if (pipe(services[i]->status_fd) < 0) {
+                               i_error("service(%s): pipe() failed: %m",
+                                       services[i]->name);
+                               continue;
+                       }
+
+                       net_set_nonblock(services[i]->status_fd[0], TRUE);
+                       fd_close_on_exec(services[i]->status_fd[0], TRUE);
+                       net_set_nonblock(services[i]->status_fd[1], TRUE);
+                       fd_close_on_exec(services[i]->status_fd[1], TRUE);
+
+                       services[i]->io_status =
+                               io_add(services[i]->status_fd[0], IO_READ,
+                                      service_status_input, services[i]);
+               }
+
+               if (services[i]->status_fd[0] != -1)
+                       service_monitor_listen_start(services[i]);
+       }
+
+       if (service_process_create(service_list->log, NULL, -1, NULL, 0) != NULL)
+               service_monitor_listen_stop(service_list->log);
+       if (service_process_create(service_list->config, NULL, -1, NULL, 0) != NULL)
+               service_monitor_listen_stop(service_list->config);
+}
+
+static void service_monitor_stop(struct service *service)
+{
+       int i;
+
+       if (service->io_status != NULL)
+               io_remove(&service->io_status);
+
+       if (service->status_fd[0] != -1) {
+               for (i = 0; i < 2; i++) {
+                       if (close(service->status_fd[i]) < 0) {
+                               i_error("service(%s): close(%d) failed: %m",
+                                       service->name, i);
+                       }
+                        service->status_fd[i] = -1;
+               }
+       }
+       service_monitor_listen_stop(service);
+
+       if (service->to_throttle != NULL)
+               timeout_remove(&service->to_throttle);
+}
+
+void services_monitor_stop(struct service_list *service_list)
+{
+       struct service *const *services;
+       unsigned int i, count;
+
+       services = array_get(&service_list->services, &count);
+       for (i = 0; i < count; i++)
+               service_monitor_stop(services[i]);
+
+       services_log_deinit(service_list);
+}
+
+void services_monitor_reap_children(struct service_list *service_list)
+{
+       struct service_process *process;
+       struct service *service;
+       pid_t pid;
+       int status;
+
+       while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
+               process = hash_table_lookup(service_list->pids, &pid);
+               if (process == NULL) {
+                       i_error("waitpid() returned unknown PID %s",
+                               dec2str(pid));
+                       continue;
+               }
+
+               service = process->service;
+               if (status == 0) {
+                       /* success */
+                       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_destroy(process);
+
+                if (service->process_avail == 0 && service->to_throttle == NULL)
+                       service_monitor_listen_start(service);
+       }
+}
diff --git a/src/master/service-monitor.h b/src/master/service-monitor.h
new file mode 100644 (file)
index 0000000..70838a6
--- /dev/null
@@ -0,0 +1,13 @@
+#ifndef SERVICE_MONITOR_H
+#define SERVICE_MONITOR_H
+
+/* Start listening and monitoring services. */
+void services_monitor_start(struct service_list *service_list);
+
+/* Stop services. */
+void services_monitor_stop(struct service_list *service_list);
+
+/* Call after SIGCHLD has been detected */
+void services_monitor_reap_children(struct service_list *service_list);
+
+#endif
diff --git a/src/master/service-process.c b/src/master/service-process.c
new file mode 100644 (file)
index 0000000..92a96a6
--- /dev/null
@@ -0,0 +1,567 @@
+/* Copyright (c) 2005-2009 Dovecot authors, see the included COPYING file */
+
+#include "common.h"
+#include "array.h"
+#include "ioloop.h"
+#include "istream.h"
+#include "ostream.h"
+#include "write-full.h"
+#include "base64.h"
+#include "hash.h"
+#include "str.h"
+#include "hostpid.h"
+#include "env-util.h"
+#include "fd-close-on-exec.h"
+#include "restrict-access.h"
+#include "master-service-settings.h"
+#include "dup2-array.h"
+#include "service.h"
+#include "service-log.h"
+#include "service-auth-server.h"
+#include "service-auth-source.h"
+#include "service-process.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <signal.h>
+#include <sys/wait.h>
+
+static const char **
+service_dup_fds(struct service *service, int auth_fd, int std_fd)
+{
+       struct service_listener *const *listeners;
+       ARRAY_TYPE(dup2) dups;
+       unsigned int i, count, n, socket_listener_count;
+
+       /* stdin/stdout is already redirected to /dev/null. Other master fds
+          should have been opened with fd_close_on_exec() so we don't have to
+          worry about them.
+
+          because the destination fd might be another one's source fd we have
+          to be careful not to overwrite anything. dup() the fd when needed */
+
+        socket_listener_count = 0;
+       listeners = array_get(&service->listeners, &count);
+       t_array_init(&dups, count + 4);
+       for (i = n = 0; i < count; i++) {
+               if (listeners[i]->fd == -1)
+                       continue;
+
+               dup2_append(&dups, listeners[i]->fd,
+                           MASTER_LISTEN_FD_FIRST + n);
+               n++; socket_listener_count++;
+       }
+
+       dup2_append(&dups, null_fd, MASTER_RESERVED_FD);
+       dup2_append(&dups, service->status_fd[1], MASTER_STATUS_FD);
+
+       switch (service->type) {
+       case SERVICE_TYPE_AUTH_SOURCE:
+       case SERVICE_TYPE_AUTH_SERVER:
+               i_assert(auth_fd != -1);
+               dup2_append(&dups, auth_fd, MASTER_AUTH_FD);
+               env_put(t_strdup_printf("MASTER_AUTH_FD=%d", MASTER_AUTH_FD));
+               break;
+       case SERVICE_TYPE_LOG:
+               services_log_dup2(&dups, service->list,
+                                 MASTER_LISTEN_FD_FIRST + n,
+                                 &socket_listener_count);
+               /* fall through */
+       default:
+               i_assert(auth_fd == -1);
+               dup2_append(&dups, null_fd, MASTER_AUTH_FD);
+               break;
+       }
+
+       if (std_fd != -1) {
+               dup2_append(&dups, std_fd, STDIN_FILENO);
+               dup2_append(&dups, std_fd, STDOUT_FILENO);
+               env_put("LOGGED_IN=1");
+       }
+
+       if (service->type != SERVICE_TYPE_LOG) {
+               /* set log file to stderr. dup2() here immediately so that
+                  we can set up logging to it without causing any log messages
+                  to be lost. */
+               i_assert(service->log_fd[1] != -1);
+
+               env_put("LOG_SERVICE=1");
+               if (dup2(service->log_fd[1], STDERR_FILENO) < 0)
+                       i_fatal("dup2(log fd) failed: %m");
+               i_set_failure_internal();
+       } else {
+               dup2_append(&dups, null_fd, STDERR_FILENO);
+       }
+
+       /* make sure we don't leak syslog fd. try to do it as late as possible,
+          but also before dup2()s in case syslog fd is one of them. */
+       closelog();
+
+       if (dup2_array(&dups) < 0)
+               i_fatal("service(%s): dup2s failed", service->name);
+
+#ifdef DEBUG
+       env_put(t_strdup_printf("SOCKET_COUNT=%d", socket_listener_count));
+#endif
+
+       if (socket_listener_count == 1)
+               return NULL;
+       else {
+               const char **args = t_new(const char *, 3);
+               args[0] = "-s";
+               args[1] = dec2str(socket_listener_count);
+               return args;
+       }
+}
+
+static int validate_uid_gid(struct master_settings *set, uid_t uid, gid_t gid,
+                           const char *user)
+{
+       if (uid == 0) {
+               i_error("Logins with UID 0 not permitted (user %s)", user);
+               return FALSE;
+       }
+
+       if (uid < (uid_t)set->first_valid_uid ||
+           (set->last_valid_uid != 0 && uid > (uid_t)set->last_valid_uid)) {
+               i_error("Logins with UID %s (user %s) not permitted "
+                       "(see first_valid_uid in config file)",
+                       dec2str(uid), user);
+               return FALSE;
+       }
+
+       if (gid < (gid_t)set->first_valid_gid ||
+           (set->last_valid_gid != 0 && gid > (gid_t)set->last_valid_gid)) {
+               i_error("Logins for users with primary group ID %s (user %s) "
+                       "not permitted (see first_valid_gid in config file).",
+                       dec2str(gid), user);
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+static void auth_args_apply(const char *const *args,
+                           struct restrict_access_settings *rset,
+                           const char **home)
+{
+       for (; *args != NULL; args++) {
+               if (strncmp(*args, "uid=", 4) == 0)
+                       rset->uid = (uid_t)strtoul(*args + 4, NULL, 10);
+               else if (strncmp(*args, "gid=", 4) == 0)
+                       rset->gid = (gid_t)strtoul(*args + 4, NULL, 10);
+               else if (strncmp(*args, "home=", 5) == 0) {
+                       *home = *args + 5;
+                       env_put(t_strconcat("HOME=", *args + 5, NULL));
+               } else if (strncmp(*args, "chroot=", 7) == 0)
+                       rset->chroot_dir = *args + 7;
+               else if (strncmp(*args, "system_groups_user=", 19) == 0)
+                       rset->system_groups_user = *args + 19;
+               else if (strncmp(*args, "mail_access_groups=", 19) == 0) {
+                       rset->extra_groups =
+                               rset->extra_groups == NULL ? *args + 19 :
+                               t_strconcat(*args + 19, ",",
+                                           rset->extra_groups, NULL);
+               } else {
+                       /* unknown, set as environment */
+                       //FIXME
+                       env_put(t_strconcat("set_", *args, NULL));
+               }
+       }
+}
+
+static void drop_privileges(struct service *service,
+                           const char *const *auth_args)
+{
+       struct master_settings *master_set = service->set->master_set;
+       struct restrict_access_settings rset;
+       const char *user, *home = NULL;
+
+       restrict_access_init(&rset);
+       rset.uid = service->uid;
+       rset.gid = service->gid;
+       rset.privileged_gid = service->privileged_gid;
+       rset.chroot_dir = *service->set->chroot == '\0' ? NULL :
+               service->set->chroot;
+       rset.extra_groups = service->extra_gids;
+
+       if (auth_args == NULL) {
+               /* non-authenticating service. don't use *_valid_gid checks */
+       } else {
+               i_assert(auth_args[0] != NULL);
+
+               rset.first_valid_gid = master_set->first_valid_gid;
+               rset.last_valid_gid = master_set->last_valid_gid;
+
+               user = auth_args[0];
+               env_put(t_strconcat("USER=", user, NULL));
+
+               auth_args_apply(auth_args + 1, &rset, &home);
+
+               if (!validate_uid_gid(master_set, rset.uid, rset.gid, user))
+                       exit(FATAL_DEFAULT);
+       }
+
+       if (home != NULL) {
+               // FIXME: warn ENOENT if mail_debug=yes
+               if (chdir(home) < 0 && errno != ENOENT)
+                       i_error("chdir(%s) failed: %m", home);
+       }
+
+       if (service->set->drop_priv_before_exec)
+               restrict_access(&rset, home, FALSE); //FIXME: disallow_root?
+       else
+               restrict_access_set_env(&rset);
+}
+
+static void
+service_process_setup_environment(struct service *service, unsigned int uid)
+{
+       const struct master_service_settings *set;
+        struct service_listener *const *listeners;
+       const char *const *p;
+       unsigned int limit, count;
+
+       /* remove all environment, and put back what we need */
+       env_clean();
+       for (p = service->list->child_process_env; *p != NULL; p++)
+               env_put(*p);
+
+       switch (service->type) {
+       case SERVICE_TYPE_CONFIG:
+               env_put(t_strconcat(MASTER_CONFIG_FILE_ENV"=",
+                                   service->config_file_path, NULL));
+               break;
+       case SERVICE_TYPE_LOG:
+               /* give the log's configuration directly, so it won't depend
+                  on config process */
+               set = master_service_settings_get(master_service);
+               env_put("DOVECONF_ENV=1");
+               env_put(t_strconcat("LOG_PATH=", set->log_path, NULL));
+               env_put(t_strconcat("INFO_LOG_PATH=", set->info_log_path, NULL));
+               env_put(t_strconcat("LOG_TIMESTAMP=", set->log_timestamp, NULL));
+               env_put(t_strconcat("SYSLOG_FACILITY=", set->syslog_facility, NULL));
+               break;
+       default:
+               listeners = array_get(&service->list->config->listeners,
+                                     &count);
+               i_assert(count > 0);
+               env_put(t_strconcat(MASTER_CONFIG_FILE_ENV"=",
+                                   listeners[0]->set.fileset.set->path, NULL));
+               break;
+       }
+
+       limit = service->set->client_limit;
+       if (limit == 0) {
+               /* fallback to default limit */
+               limit = service->set->master_set->default_client_limit;
+       }
+
+       env_put(t_strdup_printf(MASTER_CLIENT_LIMIT_ENV"=%u", limit));
+       env_put(t_strdup_printf(MASTER_UID_ENV"=%u", uid));
+
+       if (!service->set->master_set->version_ignore)
+               env_put(MASTER_DOVECOT_VERSION_ENV"="PACKAGE_VERSION);
+}
+
+static void service_process_status_timeout(struct service_process *process)
+{
+       i_error("service(%s): Initial status notification not received in %d "
+               "seconds, killing the process", process->service->name,
+               SERVICE_FIRST_STATUS_TIMEOUT_SECS);
+       if (kill(process->pid, SIGKILL) < 0 && errno != ESRCH) {
+               i_error("service(%s): kill(%s, SIGKILL) failed: %m",
+                       process->service->name, dec2str(process->pid));
+       }
+       timeout_remove(&process->to_status);
+}
+
+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)
+{
+       static unsigned int uid_counter = 0;
+       struct service_process *process;
+       unsigned int uid = ++uid_counter;
+       string_t *str;
+       int fd[2];
+       pid_t pid;
+
+       switch (service->type) {
+       case SERVICE_TYPE_AUTH_SOURCE:
+       case SERVICE_TYPE_AUTH_SERVER:
+               if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd) < 0) {
+                       i_error("service(%s): socketpair() failed: %m",
+                               service->name);
+                       return NULL;
+               }
+               fd_close_on_exec(fd[0], TRUE);
+               fd_close_on_exec(fd[1], TRUE);
+               break;
+       default:
+               fd[0] = fd[1] = -1;
+               break;
+       }
+
+       pid = fork();
+       if (pid < 0) {
+               i_error("service(%s): fork() failed: %m", service->name);
+               if (fd[0] != -1) {
+                       (void)close(fd[0]);
+                       (void)close(fd[1]);
+               }
+               return NULL;
+       }
+       if (pid == 0) {
+               /* child */
+               const char **args;
+
+               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));
+               }
+               args = service_dup_fds(service, fd[1], std_fd);
+               drop_privileges(service, auth_args);
+               process_exec(service->executable, args);
+       }
+
+       switch (service->type) {
+       case SERVICE_TYPE_AUTH_SERVER:
+               process = i_malloc(sizeof(struct service_process_auth_server));
+               process->service = service;
+               service_process_auth_server_init(process, fd[0]);
+               (void)close(fd[1]);
+               break;
+       case SERVICE_TYPE_AUTH_SOURCE:
+               process = i_malloc(sizeof(struct service_process_auth_source));
+               process->service = service;
+               service_process_auth_source_init(process, fd[0]);
+               (void)close(fd[1]);
+               break;
+       default:
+               process = i_new(struct service_process, 1);
+               process->service = service;
+               i_assert(fd[0] == -1);
+               break;
+       }
+               
+       process->refcount = 1;
+       process->pid = pid;
+       process->uid = uid;
+       process->to_status =
+               timeout_add(SERVICE_FIRST_STATUS_TIMEOUT_SECS * 1000,
+                           service_process_status_timeout, process);
+
+       process->available_count = service->set->client_limit;
+       if (process->available_count == 0) {
+               /* fallback to default limit */
+               process->available_count =
+                       service->set->master_set->default_client_limit;
+       }
+
+       service->process_count++;
+       service->process_avail++;
+
+       hash_table_insert(service->list->pids, &process->pid, process);
+       return process;
+}
+
+void service_process_destroy(struct service_process *process)
+{
+       struct service *service = process->service;
+       const char *data;
+
+       hash_table_remove(service->list->pids, &process->pid);
+
+       if (process->available_count > 0)
+               service->process_avail--;
+       service->process_count--;
+       i_assert(service->process_avail <= service->process_count);
+
+       if (process->to_status != NULL)
+               timeout_remove(&process->to_status);
+
+       switch (process->service->type) {
+       case SERVICE_TYPE_AUTH_SERVER:
+               service_process_auth_server_deinit(process);
+               break;
+       case SERVICE_TYPE_AUTH_SOURCE:
+               service_process_auth_source_deinit(process);
+               break;
+       default:
+               break;
+       }
+
+       data = t_strdup_printf("\001%c%s bye\n",
+                              LOG_TYPE_OPTION+1, dec2str(process->pid));
+       if (write(process->service->log_fd[1], data, strlen(data)) < 0) {
+               if (errno != EAGAIN)
+                       i_error("write(log process) failed: %m");
+               else {
+                       //FIXME:process->io_log_write = io_add();
+                       //return;
+               }
+       }
+
+       process->destroyed = TRUE;
+       service_process_unref(process);
+}
+
+void service_process_ref(struct service_process *process)
+{
+       i_assert(process->refcount > 0);
+
+       process->refcount++;
+}
+
+int service_process_unref(struct service_process *process)
+{
+       i_assert(process->refcount > 0);
+
+       if (--process->refcount > 0)
+               return TRUE;
+
+       i_assert(process->destroyed);
+
+       i_free(process);
+       return FALSE;
+}
+
+static const char *
+get_exit_status_message(struct service *service, enum fatal_exit_status status)
+{
+       switch (status) {
+       case FATAL_LOGOPEN:
+               return "Can't open log file";
+       case FATAL_LOGWRITE:
+               return "Can't write to log file";
+       case FATAL_LOGERROR:
+               return "Internal logging error";
+       case FATAL_OUTOFMEM:
+               if (service->set->vsz_limit == 0)
+                       return "Out of memory";
+               return t_strdup_printf("Out of memory (vsz_limit=%u MB)",
+                                      service->set->vsz_limit);
+       case FATAL_EXEC:
+               return "exec() failed";
+
+       case FATAL_DEFAULT:
+               return "Fatal failure";
+       }
+
+       return NULL;
+}
+
+static void log_coredump(struct service *service, string_t *str, int status)
+{
+#ifdef WCOREDUMP
+       int signum = WTERMSIG(status);
+
+       if (WCOREDUMP(status)) {
+               str_append(str, " (core dumped)");
+               return;
+       }
+
+       if (signum != SIGABRT && signum != SIGSEGV && signum != SIGBUS)
+               return;
+
+       /* let's try to figure out why we didn't get a core dump */
+       if (core_dumps_disabled) {
+               str_printfa(str, " (core dumps disabled)");
+               return;
+       }
+
+#ifdef HAVE_PR_SET_DUMPABLE
+       if (!service->set->drop_priv_before_exec) {
+               str_append(str, " (core not dumped - set drop_priv_before_exec=yes)");
+               return;
+       }
+       if (*service->set->privileged_group != '\0') {
+               str_append(str, " (core not dumped - privileged_group prevented it)");
+               return;
+       }
+#endif
+
+       str_append(str, " (core not dumped)");
+#endif
+}
+
+static void
+service_process_get_status_error(string_t *str, struct service_process *process,
+                                int status, enum log_type *type_r)
+{
+       struct service *service = process->service;
+       const char *msg;
+
+       *type_r = LOG_TYPE_ERROR;
+
+       str_printfa(str, "service(%s): child %s ", service->name,
+                   dec2str(process->pid));
+       if (WIFSIGNALED(status)) {
+               str_printfa(str, "killed with signal %d", WTERMSIG(status));
+               log_coredump(service, str, status);
+               return;
+       }
+       if (!WIFEXITED(status)) {
+               str_printfa(str, "died with status %d", status);
+               return;
+       }
+
+       status = WEXITSTATUS(status);
+       if (status == 0) {
+               str_truncate(str, 0);
+               return;
+       }
+       str_printfa(str, "returned error %d", status);
+
+       msg = get_exit_status_message(service, status);
+       if (msg != NULL)
+               str_printfa(str, " (%s)", msg);
+
+       if (status == FATAL_DEFAULT)
+               *type_r = LOG_TYPE_ERROR_IGNORE_IF_SEEN_FATAL;
+}
+
+static void service_process_log(struct service_process *process,
+                               enum log_type type, const char *str)
+{
+       const char *data;
+
+       if (type != LOG_TYPE_ERROR_IGNORE_IF_SEEN_FATAL ||
+           process->service->log_fd[1] == -1) {
+               i_log_type(type, "%s", str);
+               return;
+       }
+
+       /* log it via the log process in charge of handling
+          this process's logging */
+       data = t_strdup_printf("\001%c%s %s %s\n",
+                              type+1, my_pid, dec2str(process->pid), str);
+       if (write_full(process->service->log_fd[1], data, strlen(data)) < 0) {
+               i_error("write(log process) failed: %m");
+               i_log_type(type, "%s", str);
+       }
+}
+
+void service_process_log_status_error(struct service_process *process,
+                                     int status)
+{
+       if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
+               /* fast path */
+               return;
+       }
+       T_BEGIN {
+               string_t *str = t_str_new(256);
+               enum log_type type;
+
+               service_process_get_status_error(str, process, status, &type);
+               if (str_len(str) > 0)
+                       service_process_log(process, type, str_c(str));
+       } T_END;
+}
diff --git a/src/master/service-process.h b/src/master/service-process.h
new file mode 100644 (file)
index 0000000..aca151e
--- /dev/null
@@ -0,0 +1,76 @@
+#ifndef SERVICE_PROCESS_H
+#define SERVICE_PROCESS_H
+
+struct service_process {
+       struct service *service;
+       int refcount;
+
+       pid_t pid;
+        /* uid is used to check for old/invalid status messages */
+       unsigned int uid;
+
+       /* number of new connections process is currently accepting */
+       unsigned int available_count;
+       /* number of connections process has ever accepted */
+       unsigned int total_count;
+
+       /* time when process started idling, or 0 if we're not idling */
+       time_t idle_start;
+
+       /* kill the process if it doesn't send initial status notification */
+       struct timeout *to_status;
+
+       unsigned int destroyed:1;
+};
+
+struct service_process_auth_server {
+       struct service_process process;
+
+       int auth_fd;
+       struct io *io_auth;
+       struct ostream *auth_output;
+       struct istream *auth_input;
+
+       /* pending authentication requests that are being verified from
+          auth server. */
+       struct hash_table *auth_requests;
+       /* Last time we wrote "authentication server is too busy" to log */
+       time_t auth_busy_stamp;
+       /* Tag counter for outgoing requests */
+       unsigned int auth_tag_counter;
+
+       unsigned int auth_version_sent:1;
+       unsigned int auth_version_received:1;
+};
+
+struct service_process_auth_source {
+       struct service_process process;
+
+       int auth_fd;
+       struct io *io_auth;
+       struct ostream *auth_output;
+};
+
+struct service_process_auth_request {
+       struct service_process_auth_source *process;
+
+       unsigned int process_tag;
+       int fd;
+
+       struct ip_addr local_ip, remote_ip;
+       unsigned int data_size;
+       unsigned char data[FLEXIBLE_ARRAY_MEMBER];
+};
+
+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);
+void service_process_destroy(struct service_process *process);
+
+void service_process_ref(struct service_process *process);
+int service_process_unref(struct service_process *process);
+
+void service_process_log_status_error(struct service_process *process,
+                                     int status);
+
+#endif
diff --git a/src/master/service.c b/src/master/service.c
new file mode 100644 (file)
index 0000000..f6b0656
--- /dev/null
@@ -0,0 +1,449 @@
+/* Copyright (c) 2005-2009 Dovecot authors, see the included COPYING file */
+
+#include "common.h"
+#include "array.h"
+#include "hash.h"
+#include "str.h"
+#include "service.h"
+#include "service-process.h"
+#include "service-monitor.h"
+
+#include <unistd.h>
+#include <signal.h>
+#include <pwd.h>
+#include <grp.h>
+
+static int get_uid(const char *user, uid_t *uid_r, const char **error_r)
+{
+       struct passwd *pw;
+
+       if (*user == '\0') {
+               *uid_r = (uid_t)-1;
+               return 0;
+       }
+
+       if ((pw = getpwnam(user)) == NULL) {
+               *error_r = t_strdup_printf("User doesn't exist: %s", user);
+               return -1;
+       }
+
+       *uid_r = pw->pw_uid;
+       return 0;
+}
+
+static int get_gid(const char *group, gid_t *gid_r, const char **error_r)
+{
+       struct group *gr;
+
+       if (*group == '\0') {
+               *gid_r = (uid_t)-1;
+               return 0;
+       }
+
+       if ((gr = getgrnam(group)) == NULL) {
+               *error_r = t_strdup_printf("Group doesn't exist: %s", group);
+               return -1;
+       }
+
+       *gid_r = gr->gr_gid;
+       return 0;
+}
+
+static struct service_listener *
+service_create_file_listener(struct service *service,
+                            enum service_listener_type type,
+                            const struct file_listener_settings *set,
+                            const char **error_r)
+{
+       struct service_listener *l;
+
+       l = p_new(service->list->pool, struct service_listener, 1);
+       l->service = service;
+       l->type = type;
+       l->fd = -1;
+       l->set.fileset.set = set;
+
+       if (get_uid(set->user, &l->set.fileset.uid, error_r) < 0)
+               return NULL;
+       if (get_gid(set->group, &l->set.fileset.gid, error_r) < 0)
+               return NULL;
+       return l;
+}
+
+static int
+resolve_ip(const char *address, struct ip_addr *ip_r, const char **error_r)
+{
+       struct ip_addr *ip_list;
+       unsigned int ips_count;
+       int ret;
+
+       if (address == NULL || strcmp(address, "*") == 0) {
+               /* IPv4 any */
+               net_get_ip_any4(ip_r);
+               return 0;
+       }
+
+       if (strcmp(address, "::") == 0) {
+               /* IPv6 any */
+               net_get_ip_any6(ip_r);
+               return 0;
+       }
+
+       /* Return the first IP if there happens to be multiple. */
+       ret = net_gethostbyname(address, &ip_list, &ips_count);
+       if (ret != 0) {
+               *error_r = t_strdup_printf("Can't resolve address %s: %s",
+                                          address, net_gethosterror(ret));
+               return -1;
+       }
+
+       if (ips_count < 1) {
+               *error_r = t_strdup_printf("No IPs for address: %s", address);
+               return -1;
+       }
+       if (ips_count > 1) {
+               *error_r = t_strdup_printf("Multiple IPs for address: %s",
+                                          address);
+               return -1;
+       }
+
+       *ip_r = ip_list[0];
+       return 0;
+}
+
+static struct service_listener *
+service_create_inet_listener(struct service *service,
+                            const struct inet_listener_settings *set,
+                            const char **error_r)
+{
+       struct service_listener *l;
+
+       l = p_new(service->list->pool, struct service_listener, 1);
+       l->service = service;
+       l->type = SERVICE_LISTENER_INET;
+       l->fd = -1;
+       l->set.inetset.set = set;
+
+       if (resolve_ip(set->address, &l->set.inetset.ip, error_r) < 0)
+               return NULL;
+
+       if (set->port == 0) {
+               *error_r = "Port not given";
+               return NULL;
+       }
+       if (set->port > 65535) {
+               *error_r = t_strdup_printf("Invalid port: %u", set->port);
+               return NULL;
+       }
+
+       return l;
+}
+
+static struct service *
+service_create(pool_t pool, const struct service_settings *set,
+              struct service_list *service_list, const char **error_r)
+{
+       struct file_listener_settings *const *unix_listeners;
+       struct file_listener_settings *const *fifo_listeners;
+       struct inet_listener_settings *const *inet_listeners;
+       struct service *service;
+        struct service_listener *l;
+       const char *p, *const *tmp;
+       string_t *str;
+       unsigned int i, unix_count, fifo_count, inet_count;
+
+       service = p_new(pool, struct service, 1);
+       service->list = service_list;
+       service->set = set;
+
+       service->type = SERVICE_TYPE_UNKNOWN;
+       if (*set->type != '\0') {
+               service->name = set->type;
+               if (strcmp(set->type, "log") == 0)
+                       service->type = SERVICE_TYPE_LOG;
+               else if (strcmp(set->type, "config") == 0)
+                       service->type = SERVICE_TYPE_CONFIG;
+               else if (strcmp(set->type, "auth") == 0)
+                       service->type = SERVICE_TYPE_AUTH_SERVER;
+               else
+                       service->name = NULL;
+       }
+
+       if (*set->auth_dest_service != '\0')
+               service->type = SERVICE_TYPE_AUTH_SOURCE;
+
+       if (set->process_limit == 0) {
+               /* unlimited */
+               service->process_limit = INT_MAX;
+       } else if (set->process_limit == (unsigned int)-1) {
+               /* use default */
+               service->process_limit =
+                       set->master_set->default_process_limit;
+       } else {
+               service->process_limit = set->process_limit;
+       }
+
+       if (set->executable == NULL) {
+               *error_r = "executable not given";
+               return NULL;
+       }
+
+       /* get service name from some of the unique types, fallback to
+          executable name without path and parameters */
+       if (service->name == NULL) {
+               p = strrchr(set->executable, '/');
+               if (p == NULL)
+                       service->name = t_strcut(set->executable, ' ');
+               else
+                       service->name = t_strcut(p + 1, ' ');
+               service->name = p_strdup(pool, service->name);
+       }
+
+       if (get_uid(set->user, &service->uid, error_r) < 0)
+               return NULL;
+       if (get_gid(set->group, &service->gid, error_r) < 0)
+               return NULL;
+       if (get_gid(set->privileged_group, &service->privileged_gid,
+                   error_r) < 0)
+               return NULL;
+
+       if (*set->extra_groups != '\0') {
+               str = t_str_new(64);
+               tmp = t_strsplit(set->extra_groups, ",");
+               for (; *tmp != NULL; tmp++) {
+                       gid_t gid;
+
+                       if (get_gid(*tmp, &gid, error_r) < 0)
+                               return NULL;
+
+                       if (str_len(str) > 0)
+                               str_append_c(str, ',');
+                       str_append(str, dec2str(gid));
+               }
+               service->extra_gids = p_strdup(pool, str_c(str));
+       }
+
+       if (*set->executable == '/')
+               service->executable = set->executable;
+       else {
+               service->executable =
+                       p_strconcat(pool, set->master_set->libexec_dir, "/",
+                                   set->executable, NULL);
+       }
+       if (access(t_strcut(service->executable, ' '), X_OK) < 0) {
+               *error_r = t_strdup_printf("access(%s) failed: %m",
+                                          t_strcut(service->executable, ' '));
+               return NULL;
+       }
+
+       /* set these later, so if something fails we don't have to worry about
+          closing them */
+       service->log_fd[0] = -1;
+       service->log_fd[1] = -1;
+       service->status_fd[0] = -1;
+       service->status_fd[1] = -1;
+
+       if (array_is_created(&set->unix_listeners))
+               unix_listeners = array_get(&set->unix_listeners, &unix_count);
+       else {
+               unix_listeners = NULL;
+               unix_count = 0;
+       }
+       if (array_is_created(&set->fifo_listeners))
+               fifo_listeners = array_get(&set->unix_listeners, &fifo_count);
+       else {
+               fifo_listeners = NULL;
+               fifo_count = 0;
+       }
+       if (array_is_created(&set->inet_listeners))
+               inet_listeners = array_get(&set->inet_listeners, &inet_count);
+       else {
+               inet_listeners = NULL;
+               inet_count = 0;
+       }
+
+       if (unix_count == 0 && service->type == SERVICE_TYPE_CONFIG) {
+               *error_r = "Service must have unix listeners";
+               return NULL;
+       }
+
+       p_array_init(&service->listeners, pool,
+                    unix_count + fifo_count + inet_count);
+                    
+       for (i = 0; i < unix_count; i++) {
+               l = service_create_file_listener(service, SERVICE_LISTENER_UNIX,
+                                                unix_listeners[i], error_r);
+               if (l == NULL)
+                       return NULL;
+               array_append(&service->listeners, &l, 1);
+       }
+       for (i = 0; i < fifo_count; i++) {
+               l = service_create_file_listener(service, SERVICE_LISTENER_UNIX,
+                                                fifo_listeners[i], error_r);
+               if (l == NULL)
+                       return NULL;
+               array_append(&service->listeners, &l, 1);
+       }
+       for (i = 0; i < inet_count; i++) {
+               l = service_create_inet_listener(service, inet_listeners[i],
+                                                error_r);
+               if (l == NULL)
+                       return NULL;
+               array_append(&service->listeners, &l, 1);
+       }
+
+       return service;
+}
+
+static unsigned int pid_hash(const void *p)
+{
+       const pid_t *pid = p;
+
+       return (unsigned int)*pid;
+}
+
+static int pid_hash_cmp(const void *p1, const void *p2)
+{
+       const pid_t *pid1 = p1, *pid2 = p2;
+
+       return *pid1 < *pid2 ? -1 :
+               *pid1 > *pid2 ? 1 : 0;
+}
+
+static struct service *
+service_lookup(struct service_list *service_list, const char *name)
+{
+       struct service *const *services;
+       unsigned int i, count;
+
+       services = array_get(&service_list->services, &count);
+       for (i = 0; i < count; i++) {
+               if (strcmp(services[i]->name, name) == 0)
+                       return services[i];
+       }
+       return NULL;
+}
+
+struct service_list *
+services_create(const struct master_settings *set,
+               const char *const *child_process_env, const char **error_r)
+{
+       struct service_list *service_list;
+       struct service *service, *const *services;
+       struct service_settings *const *service_settings;
+       pool_t pool;
+       const char *error;
+       unsigned int i, count;
+
+       pool = pool_alloconly_create("services pool", 4096);
+
+       service_list = p_new(pool, struct service_list, 1);
+       service_list->pool = pool;
+       service_list->child_process_env = child_process_env;
+
+       service_settings = array_get(&set->services, &count);
+       p_array_init(&service_list->services, pool, count);
+
+       for (i = 0; i < count; i++) {
+               service = service_create(pool, service_settings[i],
+                                        service_list, &error);
+               if (service == NULL) {
+                       *error_r = t_strdup_printf("service(%s) %s",
+                               service_settings[i]->type == NULL ? "unknown" :
+                               service_settings[i]->type, error);
+                       return NULL;
+               }
+
+               switch (service->type) {
+               case SERVICE_TYPE_LOG:
+                       if (service_list->log != NULL) {
+                               *error_r = "Multiple log services specified";
+                               return NULL;
+                       }
+                       service_list->log = service;
+                       break;
+               case SERVICE_TYPE_CONFIG:
+                       if (service_list->config != NULL) {
+                               *error_r = "Multiple config services specified";
+                               return NULL;
+                       }
+                       service_list->config = service;
+                       break;
+               default:
+                       break;
+               }
+
+               array_append(&service_list->services, &service, 1);
+       }
+
+       /* resolve service dependencies */
+       services = array_get(&service_list->services, &count);
+       for (i = 0; i < count; i++) {
+               if (services[i]->type == SERVICE_TYPE_AUTH_SOURCE) {
+                       const char *dest_service =
+                               services[i]->set->auth_dest_service;
+                       services[i]->auth_dest_service =
+                               service_lookup(service_list, dest_service);
+                       if (services[i]->auth_dest_service == NULL) {
+                               *error_r = t_strdup_printf(
+                                       "auth_dest_service doesn't exist: %s",
+                                       dest_service);
+                               return NULL;
+                       }
+               }
+       }
+
+       if (service_list->log == NULL) {
+               *error_r = "log service not specified";
+               return NULL;
+       }
+
+       if (service_list->config == NULL) {
+               *error_r = "config process not specified";
+               return NULL;
+       }
+
+       service_list->pids = hash_table_create(default_pool, pool, 0,
+                                              pid_hash, pid_hash_cmp);
+       return service_list;
+}
+
+void service_signal(struct service *service, int signo)
+{
+       struct hash_iterate_context *iter;
+       void *key, *value;
+
+       iter = hash_table_iterate_init(service->list->pids);
+       while (hash_table_iterate(iter, &key, &value)) {
+               struct service_process *process = value;
+
+               if (process->service != service)
+                       continue;
+
+               if (kill(process->pid, signo) < 0 && errno != ESRCH) {
+                       i_error("service(%s): kill(%s, %d) failed: %m",
+                               service->name, dec2str(process->pid), signo);
+               }
+       }
+       hash_table_iterate_deinit(&iter);
+}
+
+void services_destroy(struct service_list *service_list)
+{
+       struct hash_iterate_context *iter;
+       void *key, *value;
+
+       /* make sure we log if child processes died unexpectedly */
+        services_monitor_reap_children(service_list);
+
+       services_monitor_stop(service_list);
+
+       /* free all child process information */
+       iter = hash_table_iterate_init(service_list->pids);
+       while (hash_table_iterate(iter, &key, &value))
+               service_process_destroy(value);
+       hash_table_iterate_deinit(&iter);
+
+       hash_table_destroy(&service_list->pids);
+       pool_unref(&service_list->pool);
+}
diff --git a/src/master/service.h b/src/master/service.h
new file mode 100644 (file)
index 0000000..1dd71b9
--- /dev/null
@@ -0,0 +1,110 @@
+#ifndef SERVICE_H
+#define SERVICE_H
+
+#include "network.h"
+
+/* 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
+
+enum service_type {
+       SERVICE_TYPE_UNKNOWN,
+       SERVICE_TYPE_LOG,
+       SERVICE_TYPE_CONFIG,
+       SERVICE_TYPE_AUTH_SERVER,
+       SERVICE_TYPE_AUTH_SOURCE
+};
+
+enum service_listener_type {
+       SERVICE_LISTENER_UNIX,
+       SERVICE_LISTENER_FIFO,
+       SERVICE_LISTENER_INET
+};
+
+struct service_listener {
+       struct service *service;
+
+       enum service_listener_type type;
+       int fd; /* may be -1 */
+       struct io *io;
+
+       union {
+               struct {
+                       const struct file_listener_settings *set;
+                       uid_t uid;
+                       gid_t gid;
+               } fileset;
+               struct {
+                       const struct inet_listener_settings *set;
+                       struct ip_addr ip;
+               } inetset;
+       } set;
+};
+
+struct service {
+       struct service_list *list;
+
+       enum service_type type;
+       const char *name;
+
+       const struct service_settings *set;
+       const char *config_file_path;
+
+       const char *executable;
+       uid_t uid;
+       gid_t gid;
+       gid_t privileged_gid;
+       const char *extra_gids; /* comma-separated list */
+
+       /* all listeners, even those that aren't currently listening */
+       ARRAY_DEFINE(listeners, struct service_listener *);
+
+       /* number of processes currently created for this service */
+       unsigned int process_count;
+       /* number of processes currently accepting new connections */
+       unsigned int process_avail;
+       /* max number of processes allowed */
+       unsigned int process_limit;
+
+       /* log process pipe file descriptors */
+       int log_fd[2];
+
+       /* status report pipe file descriptors */
+       int status_fd[2];
+       struct io *io_status;
+
+       /* if a process fails before servicing its first request, assume it's
+          broken and start throtting new process creations */
+       struct timeout *to_throttle;
+
+       /* SERVICE_TYPE_AUTH_SOURCE: Destination service to run after
+          successful authentication. */
+       struct service *auth_dest_service;
+
+       /* all processes are in use and new connections are coming */
+       unsigned int listen_pending:1;
+};
+
+struct service_list {
+       pool_t pool;
+
+       struct service *config;
+       struct service *log;
+       struct hash_table *pids;
+       const char *const *child_process_env;
+
+       ARRAY_DEFINE(services, struct service *);
+};
+
+/* Create all services from settings */
+struct service_list *
+services_create(const struct master_settings *set,
+               const char *const *child_process_env, const char **error_r);
+
+/* Destroy services */
+void services_destroy(struct service_list *service_list);
+
+/* Send a signal to all processes in a given service */
+void service_signal(struct service *service, int signo);
+
+#endif
diff --git a/src/master/sysinfo-get.c b/src/master/sysinfo-get.c
deleted file mode 100644 (file)
index 49091e2..0000000
+++ /dev/null
@@ -1,127 +0,0 @@
-/* Copyright (c) 2008-2009 Dovecot authors, see the included COPYING file */
-
-#include "lib.h"
-#include "mountpoint.h"
-#include "strescape.h"
-#include "sysinfo-get.h"
-
-#include <unistd.h>
-#include <fcntl.h>
-#include <sys/stat.h>
-#ifdef HAVE_SYS_UTSNAME_H
-#  include <sys/utsname.h>
-#endif
-
-static bool readfile(const char *path, const char **data_r)
-{
-       char buf[1024];
-       int fd, ret;
-
-       fd = open(path, O_RDONLY);
-       if (fd == -1)
-               return FALSE;
-       ret = read(fd, buf, sizeof(buf));
-       (void)close(fd);
-       if (ret <= 0)
-               return FALSE;
-
-       *data_r = t_strndup(buf, ret);
-       return TRUE;
-}
-
-static bool lsb_distro_get(const char *path, const char **name_r)
-{
-       const char *data, *const *p, *str, *end;
-
-       if (!readfile(path, &data))
-               return FALSE;
-
-       for (p = t_strsplit(data, "\n"); *p != '\0'; p++) {
-               if (strncmp(*p, "DISTRIB_DESCRIPTION=", 20) == 0)
-                       break;
-       }
-       if (*p == '\0')
-               return FALSE;
-
-       str = t_strcut(*p + 20, '\n');
-       if (*str != '"')
-               *name_r = str;
-       else {
-               end = strrchr(++str, '"');
-               *name_r = str_unescape(p_strdup_until(unsafe_data_stack_pool,
-                                                     str, end));
-       }
-       return TRUE;
-}
-
-static const char *distro_get(void)
-{
-       static const char *files[] = {
-               "", "/etc/redhat-release",
-               "", "/etc/SuSE-release",
-               "", "/etc/mandriva-release",
-               "", "/etc/fedora-release",
-               "", "/etc/sourcemage-release",
-               "", "/etc/slackware-version",
-               "", "/etc/gentoo-release",
-               "Debian ", "/etc/debian_version",
-               NULL
-       };
-       const char *name;
-       unsigned int i;
-
-       if (lsb_distro_get("/etc/lsb-release", &name))
-               return name;
-       for (i = 0; files[i] != NULL; i += 2) {
-               if (readfile(files[i+1], &name)) {
-                       return t_strconcat(files[i], t_strcut(name, '\n'),
-                                          NULL);
-               }
-       }
-       return "";
-}
-
-static const char *filesystem_get(const char *mail_location)
-{
-       struct mountpoint mp;
-       const char *path;
-
-       path = strchr(mail_location, ':');
-       if (path == NULL)
-               path = mail_location;
-       else
-               path = t_strcut(path + 1, ':');
-       if (*path == '~') {
-               /* we don't know where users' home dirs are */
-               return "";
-       }
-       path = t_strcut(path, '%');
-       if (strlen(path) <= 1)
-               return "";
-
-       /* all in all it seems we can support only /<path>/%u style location */
-       if (mountpoint_get(path, pool_datastack_create(), &mp) < 0)
-               return "";
-       return mp.type == NULL ? "" : mp.type;
-}
-
-const char *sysinfo_get(const char *mail_location)
-{
-       const char *distro = "", *fs, *uname_info = "";
-#ifdef HAVE_SYS_UTSNAME_H
-       struct utsname u;
-
-       if (uname(&u) < 0)
-               i_error("uname() failed: %m");
-       else {
-               uname_info = t_strdup_printf("%s %s %s",
-                                            u.sysname, u.release, u.machine);
-       }
-       if (strcmp(u.sysname, "Linux") == 0)
-               distro = distro_get();
-#endif
-       fs = filesystem_get(mail_location);
-       if (*uname_info == '\0' && *distro == '\0' && *fs == '\0')
-               return "";
-       return t_strdup_printf("OS: %s %s %s %s %s", u.sysname, u.release, u.machine, distro, fs);
-}
diff --git a/src/master/sysinfo-get.h b/src/master/sysinfo-get.h
deleted file mode 100644 (file)
index 0f319cb..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef SYSINFO_GET_H
-#define SYSINFO_GET_H
-
-const char *sysinfo_get(const char *mail_location);
-
-#endif
index 341103f62f4262b7e7cbf77e65d045534c14907b..c6676bca17a1e8f1ca35d3ff7779f6a3840c04b4 100644 (file)
@@ -55,7 +55,7 @@ int main(int argc, char *argv[])
        input.username = argv[optind];
 
        master_service_init_log(service,
-               t_strdup_printf("convert-tool(%s): ", input.username));
+               t_strdup_printf("convert-tool(%s): ", input.username), 0);
        user = mail_storage_service_init_user(service, &input, NULL, 0);
 
        memset(&ns_set, 0, sizeof(ns_set));
index 7f8ae44f0053fe8df41b7bbb1c8b2cb4ab7f314a..7412b0f295366bdbb11d8af20c1bda1f6468c51d 100644 (file)
@@ -5,6 +5,7 @@ pkglibexec_PROGRAMS = pop3-login
 AM_CPPFLAGS = \
        -I$(top_srcdir)/src/lib \
        -I$(top_srcdir)/src/lib-auth \
+       -I$(top_srcdir)/src/lib-master \
        -I$(top_srcdir)/src/login-common
 
 pop3_login_LDADD = \
index 19ca4d2ac96407b3774a5174882cf3c0e0074824..2ffc0fee2854d618538fbd59fee34135e5f36969 100644 (file)
@@ -10,6 +10,8 @@
 #include "process-title.h"
 #include "safe-memset.h"
 #include "strescape.h"
+#include "master-service.h"
+#include "master-auth.h"
 #include "client.h"
 #include "client-authenticate.h"
 #include "auth-client.h"
@@ -34,6 +36,7 @@
 #endif
 
 const char *login_protocol = "POP3";
+const char *login_process_name = "pop3-login";
 
 static void client_set_title(struct pop3_client *client)
 {
@@ -63,7 +66,6 @@ static void client_start_tls(struct pop3_client *client)
        int fd_ssl;
 
        client_ref(client);
-       connection_queue_add(1);
        if (!client_unref(client) || client->destroyed)
                return;
 
@@ -76,6 +78,7 @@ static void client_start_tls(struct pop3_client *client)
                return;
        }
 
+       client->common.proxying = TRUE;
        client->common.tls = TRUE;
        client->common.secured = TRUE;
        client_set_title(client);
@@ -307,7 +310,11 @@ struct client *client_create(int fd, bool ssl, const struct ip_addr *local_ip,
 
        i_assert(fd != -1);
 
-       connection_queue_add(1);
+       if (clients_get_count() >= login_settings->login_max_connections) {
+               /* reached max. users count, kill few of the
+                  oldest connections */
+               client_destroy_oldest();
+       }
 
        /* always use nonblocking I/O */
        net_set_nonblock(fd, TRUE);
@@ -327,8 +334,6 @@ struct client *client_create(int fd, bool ssl, const struct ip_addr *local_ip,
        client_open_streams(client, fd);
        client_link(&client->common);
 
-       main_ref();
-
        client->auth_connected = auth_client_is_connected(auth_client);
        if (client->auth_connected)
                client_auth_ready(client);
@@ -367,10 +372,11 @@ void client_destroy(struct pop3_client *client, const char *reason)
        if (client->output != NULL)
                o_stream_close(client->output);
 
-       if (client->common.master_tag != 0)
-               master_request_abort(&client->common);
-
-       if (client->common.auth_request != NULL) {
+       if (client->common.master_tag != 0) {
+               i_assert(client->common.auth_request == NULL);
+               i_assert(client->common.authenticating);
+               master_auth_request_abort(service, client->common.master_tag);
+       } else if (client->common.auth_request != NULL) {
                i_assert(client->common.authenticating);
                sasl_server_auth_client_error(&client->common, NULL);
        } else {
@@ -407,9 +413,6 @@ void client_destroy(struct pop3_client *client, const char *reason)
                client->common.proxy = NULL;
        }
        client_unref(client);
-
-       main_listen_start();
-       main_unref();
 }
 
 void client_destroy_internal_failure(struct pop3_client *client)
@@ -437,12 +440,16 @@ bool client_unref(struct pop3_client *client)
        if (client->output != NULL)
                o_stream_unref(&client->output);
 
+       if (!client->common.proxying) {
+               i_assert(client->common.proxy == NULL);
+               master_service_client_connection_destroyed(service);
+       }
+
        i_free(client->last_user);
        i_free(client->apop_challenge);
        i_free(client->common.virtual_user);
        i_free(client->common.auth_mech_name);
        i_free(client);
-
        return FALSE;
 }
 
index 750eb03c3bde3f707a1cdfea031ce06ac7e5dc29..663398276654a45ed7b7f3c36871dc6936c7cd66 100644 (file)
@@ -2,7 +2,6 @@
 #define CLIENT_H
 
 #include "network.h"
-#include "master.h"
 #include "client-common.h"
 #include "auth-client.h"
 
index 9cb77e2026828af4205c96fa91fff43ae7198bc0..60a59ac3bd293f64a2ee35d66399560680f0d521 100644 (file)
@@ -133,6 +133,7 @@ static int proxy_input_line(struct pop3_client *client,
                client->common.input = NULL;
                client->output = NULL;
                client->common.fd = -1;
+               client->common.proxying = TRUE;
                client_destroy_success(client, str_c(str));
                return 1;
        }
@@ -240,7 +241,6 @@ int pop3_proxy_new(struct pop3_client *client, const char *host,
        }
 
        i_assert(client->refcount > 1);
-       connection_queue_add(1);
 
        if (client->destroyed) {
                /* connection_queue_add() decided that we were the oldest
index 1ff3db28b3d03f760930f83fdc6fd62625b70d6f..b63d638acc0ad6d0354c253680ff38931f9c99e3 100644 (file)
@@ -137,6 +137,12 @@ static void main_deinit(void)
        clients_deinit();
 }
 
+static void client_connected(const struct master_service_connection *conn)
+{
+       /* we can't handle this yet */
+       (void)close(conn->fd);
+}
+
 int main(int argc, char *argv[], char *envp[])
 {
        const struct setting_parser_info *set_roots[] = {
@@ -171,7 +177,7 @@ int main(int argc, char *argv[], char *envp[])
        service = master_service_init("pop3", service_flags, argc, argv);
        while ((c = getopt(argc, argv, master_service_getopt_string())) > 0) {
                if (!master_service_parse_option(service, c, optarg))
-                       i_fatal("Unknown argument: %c", c);
+                       exit(FATAL_DEFAULT);
        }
 
        memset(&input, 0, sizeof(input));
@@ -199,7 +205,7 @@ int main(int argc, char *argv[], char *envp[])
        io_loop_set_running(current_ioloop);
 
        if (main_init(set, mail_user))
-               master_service_run(service);
+               master_service_run(service, client_connected);
 
        main_deinit();
        mail_storage_service_deinit_user();