diff --git a/Makefile.am b/Makefile.am index d037258..8a6000a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -49,6 +49,17 @@ dovecot-config: dovecot-config.in Makefile -e "s|^\(dovecot_moduledir\)=|\1=$(moduledir)|" \ > dovecot-config +if HAVE_SYSTEMD +%.service: %.service.in + $(AM_V_GEN)sed -e 's,@sbindir\@,$(sbindir),g' $< > $@ + +systemdsystemunit_DATA = \ + dovecot.socket \ + dovecot.service +else +EXTRA_DIST+= dovecot.socket dovecot.service.in +endif + install-exec-hook: rm $(DESTDIR)$(pkglibdir)/dovecot-config && \ grep -v '^LIBDOVECOT_.*_INCLUDE' dovecot-config | \ @@ -62,6 +73,9 @@ install-exec-hook: > $(DESTDIR)$(pkglibdir)/dovecot-config CLEANFILES = $(datafiles) +if HAVE_SYSTEMD +CLEANFILES += $systedmsystemunit_DATA +endif DISTCLEANFILES = $(top_builddir)/dovecot-version.h diff --git a/configure.in b/configure.in index 348f441..ecb3046 100644 --- a/configure.in +++ b/configure.in @@ -2628,6 +2628,16 @@ fi AC_SUBST(RUN_TEST) AC_SUBST(abs_top_builddir) +PKG_PROG_PKG_CONFIG +AC_ARG_WITH([systemdsystemunitdir], + AS_HELP_STRING([--with-systemdsystemunitdir=DIR], [Directory for systemd service files]), + [], [with_systemdsystemunitdir=$($PKG_CONFIG --variable=systemdsystemunitdir systemd)]) +if test "x$with_systemdsystemunitdir" != xno; then + AC_SUBST([systemdsystemunitdir], [$with_systemdsystemunitdir]) + AC_DEFINE(HAVE_SYSTEMD,, Define if you want to use systemd socket activation) +fi +AM_CONDITIONAL(HAVE_SYSTEMD, [test -n "$with_systemdsystemunitdir" -a "x$with_systemdsystemunitdir" != xno ]) + AC_CONFIG_HEADERS([config.h]) AC_CONFIG_FILES([ Makefile diff --git a/dovecot.service.in b/dovecot.service.in new file mode 100644 index 0000000..db03946 --- /dev/null +++ b/dovecot.service.in @@ -0,0 +1,8 @@ +[Unit] +Description=Dovecot IMAP/POP3 email server +After=local-fs.target network.target + +[Service] +Type=simple +ExecStart=@sbindir@/dovecot -F +NonBlocking=yes diff --git a/dovecot.socket b/dovecot.socket new file mode 100644 index 0000000..0fdef6f --- /dev/null +++ b/dovecot.socket @@ -0,0 +1,15 @@ +[Unit] +Description=Dovecot IMAP/POP3 email server activation socket + +[Socket] +#dovecot expects separate IPv4 and IPv6 sockets +BindIPv6Only=ipv6-only +ListenStream=0.0.0.0:143 +ListenStream=[::]:143 +ListenStream=0.0.0.0:993 +ListenStream=[::]:993 +KeepAlive=true + +[Install] +WantedBy=sockets.target + diff --git a/src/lib-master/master-service.c b/src/lib-master/master-service.c index e2dc98d..5925bb6 100644 --- a/src/lib-master/master-service.c +++ b/src/lib-master/master-service.c @@ -400,6 +400,10 @@ void master_service_env_clean(bool preserve_home) #ifdef DEBUG "GDB", #endif +#ifdef HAVE_SYSTEMD + "LISTEN_PID", + "LISTEN_FDS", +#endif NULL }; env_clean_except(preserve_envs + (preserve_home ? 0 : 1)); diff --git a/src/master/Makefile.am b/src/master/Makefile.am index 526b75b..9d3ad78 100644 --- a/src/master/Makefile.am +++ b/src/master/Makefile.am @@ -2,6 +2,11 @@ pkglibexecdir = $(libexecdir)/dovecot sbin_PROGRAMS = dovecot +if HAVE_SYSTEMD +SYSTEMD_SOURCES = sd-daemon.c +endif + + AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-settings \ @@ -27,13 +32,15 @@ dovecot_SOURCES = \ service-monitor.c \ service-process.c \ service-process-notify.c \ - service.c + service.c \ + $(SYSTEMD_SOURCES) noinst_HEADERS = \ capabilities.h \ common.h \ dup2-array.h \ master-settings.h \ + sd-daemon.h \ service-anvil.h \ service-listen.h \ service-log.h \ @@ -41,3 +48,4 @@ noinst_HEADERS = \ service-process.h \ service-process-notify.h \ service.h + diff --git a/src/master/main.c b/src/master/main.c index 5e390d6..2d862a0 100644 --- a/src/master/main.c +++ b/src/master/main.c @@ -601,6 +601,10 @@ int main(int argc, char *argv[]) static const char *preserve_envs[] = { /* AIX depends on TZ to get the timezone correctly. */ "TZ", +#ifdef HAVE_SYSTEMD + "LISTEN_PID", + "LISTEN_FDS", +#endif NULL }; struct master_settings *set; diff --git a/src/master/service-listen.c b/src/master/service-listen.c index 318c098..976036b 100644 --- a/src/master/service-listen.c +++ b/src/master/service-listen.c @@ -5,6 +5,9 @@ #include "fd-set-nonblock.h" #include "fd-close-on-exec.h" #include "network.h" +#ifdef HAVE_SYSTEMD +#include "sd-daemon.h" +#endif #include "service.h" #include "service-listen.h" @@ -147,20 +150,47 @@ 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_get_backlog(service)); - if (fd < 0) { - service_error(service, "listen(%s, %u) failed: %m", - l->inet_address, set->port); - return errno == EADDRINUSE ? 0 : -1; + int fd = -1; +#ifdef HAVE_SYSTEMD + int n; + + n = sd_listen_fds(0); + if (n < 0) { + errno = -n; + goto failure; + } else if (n > 0) { + for (fd = SD_LISTEN_FDS_START; + fd <= SD_LISTEN_FDS_START + n - 1; + fd++) { + if (sd_is_socket_inet(fd, l->set.inetset.ip.family, + SOCK_STREAM, 1, port)) + break; + } + if (fd > SD_LISTEN_FDS_START + n - 1) { + /* when systemd didn't provide a usable socket, + * fall back to the regular socket creation code + */ + fd = -1; + } + } +#endif + + if (fd == -1) { + fd = net_listen(&l->set.inetset.ip, &port, + service_get_backlog(service)); + if (fd < 0) + goto failure; + net_set_nonblock(fd, TRUE); + fd_close_on_exec(fd, TRUE); } - net_set_nonblock(fd, TRUE); - fd_close_on_exec(fd, TRUE); l->fd = fd; return 1; + +failure: + service_error(service, "listen(%s, %u) failed: %m", + l->inet_address, set->port); + return errno == EADDRINUSE ? 0 : -1; } static int service_listen(struct service *service)