On 03/15/2012 03:05 PM, Timo Sirainen wrote:
On Thu, 2012-03-15 at 14:34 +0100, Michal Hlavinka wrote:
What exactly should happen when
dovecot.conf does not match dovecot.socket configuration?

Dovecot's systemd code was written by one of you Redhat guys. I had some
similar thoughts when I applied the patch, but didn't really know what
to do about it, so I didn't do anything. So: I don't know. Maybe some
other project has solved this somehow already?

Seems other projects did not solve this yet. Most projects provide just one service. In that case, they serve any connection they get, because they know what to do.

I've discussed this with systemd upstream and we've decided that the best solution is to log error message and close that socket.

Dovecot anyway needs its own internal UNIX listeners. Should all
internal inet listeners be disabled? Could Dovecot somehow talk to
systemd and ask what listeners it's using for Dovecot and log warnings
if they don't match?

I don't understand this question completely. What it does already is that during start up, when dovecot creates sockets, it checks what sockets already exist and creates only the missing ones.

Systemd provides following functions:
sd_is_fifo (3)       - Check the type of a file descriptor
sd_is_mq (3)         - Check the type of a file descriptor
sd_is_socket (3)     - Check the type of a file descriptor
sd_is_socket_inet (3) - Check the type of a file descriptor
sd_is_socket_unix (3) - Check the type of a file descriptor
sd_listen_fds (3)    - Check for file descriptors passed by the init system.

http://0pointer.de/public/systemd-man/sd_listen_fds.html

I wrote simple patch that close the extra sockets. It's tested and works fine. You'll maybe want to move that function to different place and/or change wording of error messages.

Michal
diff -up dovecot-2.0.20/src/master/service-listen.c.systemdfix dovecot-2.0.20/src/master/service-listen.c
--- dovecot-2.0.20/src/master/service-listen.c.systemdfix	2011-12-13 12:38:27.000000000 +0100
+++ dovecot-2.0.20/src/master/service-listen.c	2012-04-13 18:29:37.724290656 +0200
@@ -14,6 +14,7 @@
 #include <unistd.h>
 #include <fcntl.h>
 #include <sys/stat.h>
+#include <sys/socket.h>
 
 #define MIN_BACKLOG 4
 #define MAX_BACKLOG 511
@@ -231,16 +232,90 @@ static int service_listen(struct service
 	return ret;
 }
 
+static int get_socket_info(int fd, unsigned int *family, unsigned int *port)
+{
+	union sockaddr_union {
+		struct sockaddr sa;
+		struct sockaddr_in in4;
+		struct sockaddr_in6 in6;
+	} sockaddr;
+	socklen_t l;
+
+	if (port) *port = -1;
+	if (family) *family = -1;
+
+	memset(&sockaddr, 0, sizeof(sockaddr));
+	l = sizeof(sockaddr);
+
+	if (getsockname(fd, &sockaddr.sa, &l) < 0)
+	      return -errno;
+
+	if (family) *family = sockaddr.sa.sa_family;
+	if (port) {
+		if (sockaddr.sa.sa_family == AF_INET) {
+			if (l < sizeof(struct sockaddr_in))
+				return -EINVAL;
+
+			*port = ntohs(sockaddr.in4.sin_port);
+		} else {
+			if (l < sizeof(struct sockaddr_in6))
+				return -EINVAL;
+
+			*port = ntohs(sockaddr.in6.sin6_port);
+		}
+	}
+	return 0;
+}
+
 int services_listen(struct service_list *service_list)
 {
 	struct service *const *services;
 	int ret = 1, ret2;
 
 	array_foreach(&service_list->services, services) {
 		ret2 = service_listen(*services);
 		if (ret2 < ret)
 			ret = ret2;
 	}
+
+	static int sd_fds = -1;
+	int fd, fd_max;
+
+	if (sd_fds < 0) {
+		sd_fds = sd_listen_fds(0);
+		if (sd_fds == -1) {
+			i_error("sd_listen_fds() failed: %m");
+			return -1;
+		}
+	}
+
+	fd_max = SD_LISTEN_FDS_START + sd_fds - 1;
+	for (fd = SD_LISTEN_FDS_START; fd <= fd_max; fd++) {
+		if (sd_is_socket_inet(fd, 0, SOCK_STREAM, 1, 0) > 0) {
+			int found = FALSE;
+			unsigned int port, family;
+			get_socket_info(fd, &family, &port);
+			
+			array_foreach(&service_list->services, services) {
+			        struct service_listener *const *listeners;
+				array_foreach(&(*services)->listeners, listeners) {
+					struct service_listener *l = *listeners;
+					if (l->type != SERVICE_LISTENER_INET) continue;	
+					if (l->set.inetset.set->port == port && l->set.inetset.ip.family == family) {
+						found = TRUE;
+						break;
+					}
+				}
+				if (found) break;
+			}
+			if (!found) {
+				i_error("we've got socket that listens on port %d, but it's not configured. Closing.",port);
+				if (shutdown(fd,SHUT_RDWR) < 0 && errno != ENOTCONN) i_error("shutdown() failed: %m");
+				close(fd);
+			}
+		}
+	}
+
 	return ret;
 }
 

Reply via email to