/* <settings checks> */
static struct inet_listener_settings imap_login_inet_listeners_array[] = {
- { "imap", "", 143, FALSE },
- { "imaps", "", 993, TRUE }
+ { .name = "imap", .address = "", .port = 143 },
+ { .name = "imaps", .address = "", .port = 993, .ssl = TRUE }
};
static struct inet_listener_settings *imap_login_inet_listeners[] = {
&imap_login_inet_listeners_array[0],
const char *address;
unsigned int port;
bool ssl;
+ bool reuse_port;
};
ARRAY_DEFINE_TYPE(inet_listener_settings, struct inet_listener_settings *);
DEF(SET_STR, address),
DEF(SET_UINT, port),
DEF(SET_BOOL, ssl),
+ DEF(SET_BOOL, reuse_port),
SETTING_DEFINE_LIST_END
};
.name = "",
.address = "",
.port = 0,
- .ssl = FALSE
+ .ssl = FALSE,
+ .reuse_port = FALSE
};
static const struct setting_parser_info inet_listener_setting_parser_info = {
static int service_inet_listener_listen(struct service_listener *l)
{
struct service *service = l->service;
+ enum net_listen_flags flags = 0;
const struct inet_listener_settings *set = l->set.inetset.set;
unsigned int port = set->port;
int fd;
+
#ifdef HAVE_SYSTEMD
if (systemd_listen_fd(&l->set.inetset.ip, port, &fd) < 0)
return -1;
if (fd == -1)
#endif
{
- fd = net_listen(&l->set.inetset.ip, &port,
- service_get_backlog(service));
+ if (set->reuse_port)
+ flags |= NET_LISTEN_FLAG_REUSEPORT;
+ fd = net_listen_full(&l->set.inetset.ip, &port, &flags,
+ 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;
}
+ l->reuse_port = (flags & NET_LISTEN_FLAG_REUSEPORT) != 0;
}
net_set_nonblock(fd, TRUE);
fd_close_on_exec(fd, TRUE);
return 1;
}
+int service_listener_listen(struct service_listener *l)
+{
+ switch (l->type) {
+ case SERVICE_LISTENER_UNIX:
+ return service_unix_listener_listen(l);
+ case SERVICE_LISTENER_FIFO:
+ return service_fifo_listener_listen(l);
+ case SERVICE_LISTENER_INET:
+ return service_inet_listener_listen(l);
+ }
+ i_unreached();
+}
+
static int service_listen(struct service *service)
{
struct service_listener *const *listeners;
if (l->fd != -1)
continue;
- switch (l->type) {
- case SERVICE_LISTENER_UNIX:
- ret2 = service_unix_listener_listen(l);
- break;
- case SERVICE_LISTENER_FIFO:
- ret2 = service_fifo_listener_listen(l);
- break;
- case SERVICE_LISTENER_INET:
- ret2 = service_inet_listener_listen(l);
- break;
- }
-
+ ret2 = service_listener_listen(l);
if (ret2 < ret)
ret = ret2;
}
int services_listen_using(struct service_list *new_service_list,
struct service_list *old_service_list);
+int service_listener_listen(struct service_listener *l);
+
#endif
#include "dup2-array.h"
#include "service.h"
#include "service-anvil.h"
+#include "service-listen.h"
#include "service-log.h"
#include "service-process-notify.h"
#include "service-process.h"
#include <signal.h>
#include <sys/wait.h>
+static void service_reopen_inet_listeners(struct service *service)
+{
+ struct service_listener *const *listeners;
+ unsigned int i, count;
+ int old_fd;
+
+ listeners = array_get(&service->listeners, &count);
+ for (i = 0; i < count; i++) {
+ if (!listeners[i]->reuse_port || listeners[i]->fd == -1)
+ continue;
+
+ old_fd = listeners[i]->fd;
+ listeners[i]->fd = -1;
+ if (service_listener_listen(listeners[i]) < 0)
+ listeners[i]->fd = old_fd;
+ }
+}
+
static void
service_dup_fds(struct service *service)
{
if (pid == 0) {
/* child */
service_process_setup_environment(service, uid);
+ service_reopen_inet_listeners(service);
service_dup_fds(service);
drop_privileges(service);
process_exec(service->executable, NULL);
struct ip_addr ip;
} inetset;
} set;
+
+ bool reuse_port;
};
struct service {
/* <settings checks> */
static struct inet_listener_settings pop3_login_inet_listeners_array[] = {
- { "pop3", "", 110, FALSE },
- { "pop3s", "", 995, TRUE }
+ { .name = "pop3", .address = "", .port = 110 },
+ { .name = "pop3s", .address = "", .port = 995, .ssl = TRUE }
};
static struct inet_listener_settings *pop3_login_inet_listeners[] = {
&pop3_login_inet_listeners_array[0],