about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--contrib/Makefile.am3
-rw-r--r--contrib/README3
-rw-r--r--contrib/ngircd.socket10
-rw-r--r--src/ngircd/conn.c70
4 files changed, 85 insertions, 1 deletions
diff --git a/contrib/Makefile.am b/contrib/Makefile.am
index 09b43a68..6d16f7ca 100644
--- a/contrib/Makefile.am
+++ b/contrib/Makefile.am
@@ -1,6 +1,6 @@
 #
 # ngIRCd -- The Next Generation IRC Daemon
-# Copyright (c)2001-2012 Alexander Barton (alex@barton.de) and Contributors
+# Copyright (c)2001-2013 Alexander Barton (alex@barton.de) and Contributors
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -17,6 +17,7 @@ EXTRA_DIST = README \
 	ngIRCd-Logo.gif \
 	ngircd-redhat.init \
 	ngircd.service \
+	ngircd.socket \
 	ngircd.spec \
 	platformtest.sh \
 	systrace.policy
diff --git a/contrib/README b/contrib/README
index f3730a4e..2d639e66 100644
--- a/contrib/README
+++ b/contrib/README
@@ -30,6 +30,9 @@ ngircd-redhat.init
 ngircd.service
  - systemd(8) service unit configuration file.
 
+ngircd.socket
+ - systemd(8) socket unit configuration file for "socket activation".
+
 ngircd.spec
  - RPM "spec" file.
 
diff --git a/contrib/ngircd.socket b/contrib/ngircd.socket
new file mode 100644
index 00000000..3838efcc
--- /dev/null
+++ b/contrib/ngircd.socket
@@ -0,0 +1,10 @@
+[Unit]
+Description=Next Generation IRC Daemon (Socket)
+
+[Socket]
+ListenStream=6667
+#ListenStream=6668
+IPTOS=low-delay
+
+[Install]
+WantedBy=sockets.target
diff --git a/src/ngircd/conn.c b/src/ngircd/conn.c
index 14d337b9..378509f9 100644
--- a/src/ngircd/conn.c
+++ b/src/ngircd/conn.c
@@ -82,6 +82,8 @@
 #define MAX_COMMANDS_SERVER_MIN 10
 #define MAX_COMMANDS_SERVICE 10
 
+#define SD_LISTEN_FDS_START 3
+
 
 static bool Handle_Write PARAMS(( CONN_ID Idx ));
 static bool Conn_Write PARAMS(( CONN_ID Idx, char *Data, size_t Len ));
@@ -121,6 +123,40 @@ static void cb_clientserver PARAMS((int sock, short what));
 
 
 /**
+ * Get number of sockets available from systemd(8).
+ *
+ * ngIRCd needs to implement its own sd_listen_fds(3) function and can't
+ * use the one provided by systemd itself, becaus the sockets will be
+ * used in a forked child process with a new PID, and this would trigger
+ * an error in the standard implementation.
+ *
+ * @return Number of sockets available, -1 if sockets have already been
+ *         initialized, or 0 when no sockets have been passed.
+ */
+static int
+my_sd_listen_fds(void)
+{
+	const char *e;
+	long count;
+
+	/* Check if LISTEN_PID exists; but we ignore the result, because
+	 * normally ngircd forks a child before checking this, and therefore
+	 * the PID set in the environment is always wrong ... */
+	e = getenv("LISTEN_PID");
+	if (!e || !*e)
+		return 0;
+
+	e = getenv("LISTEN_FDS");
+	if (!e || !*e)
+		return -1;
+	count = atol(e);
+	unsetenv("LISTEN_FDS");
+
+	return count;
+}
+
+
+/**
  * IO callback for listening sockets: handle new connections. This callback
  * gets called when a new non-SSL connection should be accepted.
  *
@@ -495,9 +531,38 @@ Conn_InitListeners( void )
 	/* Initialize ports on which the server should accept connections */
 	unsigned int created = 0;
 	char *copy, *listen_addr;
+	int count, fd, i;
 
 	assert(Conf_ListenAddress);
 
+	count = my_sd_listen_fds();
+	if (count < 0) {
+		Log(LOG_INFO,
+		    "Not re-initializing listening sockets of systemd(8) ...");
+		return 0;
+	}
+	if (count > 0) {
+		/* systemd(8) passed sockets to us, so don't try to initialize
+		 * listening sockets on our own but use the passed ones */
+		LogDebug("Initializing %d systemd sockets ...", count);
+		for (i = 0; i < count; i++) {
+			fd = SD_LISTEN_FDS_START + i;
+			Init_Socket(fd);
+			if (!io_event_create(fd, IO_WANTREAD, cb_listen)) {
+				Log(LOG_ERR,
+				    "io_event_create(): Can't add fd %d: %s!",
+				    fd, strerror(errno));
+				continue;
+			}
+			Log(LOG_INFO,
+			    "Initialized socket %d from systemd.", fd);
+			created++;
+		}
+		return created;
+	}
+
+	/* not using systemd socket activation, initialize listening sockets: */
+
 	/* can't use Conf_ListenAddress directly, see below */
 	copy = strdup(Conf_ListenAddress);
 	if (!copy) {
@@ -541,7 +606,12 @@ Conn_ExitListeners( void )
 	int *fd;
 	size_t arraylen;
 
+	/* Get number of listening sockets to shut down. There can be none
+	 * if ngIRCd has been "socket activated" by systemd. */
 	arraylen = array_length(&My_Listeners, sizeof (int));
+	if (arraylen < 1)
+		return;
+
 	Log(LOG_INFO,
 	    "Shutting down all listening sockets (%d total) ...", arraylen);
 	fd = array_start(&My_Listeners);