about summary refs log tree commit diff
diff options
context:
space:
mode:
authorAlexander Barton <alex@barton.de>2024-04-05 22:38:22 +0200
committerAlexander Barton <alex@barton.de>2024-04-06 00:12:58 +0200
commite4873b4d63d0bcd4914a1cee82599a13cfd77e47 (patch)
treecba6e5e959a6043602c1c425d6f45007dab40fa3
parent3e535a295523853963438eb94f9cfa24c998b52f (diff)
downloadngircd-e4873b4d63d0bcd4914a1cee82599a13cfd77e47.tar.gz
ngircd-e4873b4d63d0bcd4914a1cee82599a13cfd77e47.zip
Add support for the "sd_notify" protocol
This allows the "ngircd.service" systemd(8) unit to use the "notify"
service type, which allows for better status tracking by the service
manager.
-rw-r--r--configure.ng1
-rw-r--r--contrib/ngircd.service4
-rw-r--r--src/ngircd/conn.c9
-rw-r--r--src/ngircd/sighandlers.c100
-rw-r--r--src/ngircd/sighandlers.h3
5 files changed, 110 insertions, 7 deletions
diff --git a/configure.ng b/configure.ng
index ec7b6c35..0dccfbc5 100644
--- a/configure.ng
+++ b/configure.ng
@@ -193,6 +193,7 @@ AC_CHECK_HEADERS_ONCE([ \
 	stddef.h \
 	stdint.h \
 	sys/resource.h \
+	sys/un.h \
 	varargs.h \
 ])
 
diff --git a/contrib/ngircd.service b/contrib/ngircd.service
index fb3cf8a1..215f5052 100644
--- a/contrib/ngircd.service
+++ b/contrib/ngircd.service
@@ -11,7 +11,7 @@ Before=anope.service atheme.service irc-services.service
 Before=bopm.service hopm.service
 
 [Service]
-Type=forking
+Type=notify
 User=irc
 Group=irc
 # Settings & limits:
@@ -35,7 +35,7 @@ EnvironmentFile=-/etc/default/ngircd
 EnvironmentFile=-/etc/default/ngircd-full
 EnvironmentFile=-/etc/default/ngircd-full-dbg
 # Start ngIRCd. Note: systemd doesn't allow to use $DAEMON here!
-ExecStart=/usr/sbin/ngircd $PARAMS
+ExecStart=/usr/sbin/ngircd --nodaemon --syslog $PARAMS
 ExecReload=/bin/kill -HUP $MAINPID
 Restart=on-failure
 
diff --git a/src/ngircd/conn.c b/src/ngircd/conn.c
index 10042943..61f296ab 100644
--- a/src/ngircd/conn.c
+++ b/src/ngircd/conn.c
@@ -66,6 +66,7 @@
 #include "ng_ipaddr.h"
 #include "parse.h"
 #include "resolve.h"
+#include "sighandlers.h"
 
 #define SERVER_WAIT (NONE - 1)		/** "Wait for outgoing connection" flag */
 
@@ -673,6 +674,7 @@ Conn_Handler(void)
 
 	Log(LOG_NOTICE, "Server \"%s\" (on \"%s\") ready.",
 	    Client_ID(Client_ThisServer()), Client_Hostname(Client_ThisServer()));
+	Signal_NotifySvcMgr("READY=1\n");
 
 	while (!NGIRCd_SignalQuit && !NGIRCd_SignalRestart) {
 		t = time(NULL);
@@ -791,10 +793,13 @@ Conn_Handler(void)
 		}
 	}
 
-	if (NGIRCd_SignalQuit)
+	if (NGIRCd_SignalQuit) {
 		Log(LOG_NOTICE | LOG_snotice, "Server going down NOW!");
-	else if (NGIRCd_SignalRestart)
+		Signal_NotifySvcMgr("STOPPING=1\n");
+	} else if (NGIRCd_SignalRestart) {
 		Log(LOG_NOTICE | LOG_snotice, "Server restarting NOW!");
+		Signal_NotifySvcMgr("RELOADING=1\n");
+	}
 } /* Conn_Handler */
 
 /**
diff --git a/src/ngircd/sighandlers.c b/src/ngircd/sighandlers.c
index 4ed1a125..56fd8aea 100644
--- a/src/ngircd/sighandlers.c
+++ b/src/ngircd/sighandlers.c
@@ -1,6 +1,6 @@
 /*
  * ngIRCd -- The Next Generation IRC Daemon
- * Copyright (c)2001-2015 Alexander Barton (alex@barton.de) and Contributors.
+ * Copyright (c)2001-2024 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
@@ -26,6 +26,11 @@
 #include <sys/wait.h>
 #include <time.h>
 
+#ifdef HAVE_SYS_UN_H
+# include <sys/socket.h>
+# include <sys/un.h>
+#endif
+
 #include "conn.h"
 #include "channel.h"
 #include "conf.h"
@@ -100,14 +105,17 @@ Rehash(void)
 	unsigned old_nicklen;
 
 	Log( LOG_NOTICE|LOG_snotice, "Re-reading configuration NOW!" );
+	Signal_NotifySvcMgr("RELOADING=1\n");
 
 	/* Remember old server name and nickname length */
 	strlcpy( old_name, Conf_ServerName, sizeof old_name );
 	old_nicklen = Conf_MaxNickLength;
 
 	/* Re-read configuration ... */
-	if (!Conf_Rehash( ))
+	if (!Conf_Rehash()) {
+		Signal_NotifySvcMgr("READY=1\n");
 		return;
+	}
 
 	/* Close down all listening sockets */
 	Conn_ExitListeners( );
@@ -139,6 +147,7 @@ Rehash(void)
 	Conn_SyncServerStruct( );
 
 	Log( LOG_NOTICE|LOG_snotice, "Re-reading of configuration done." );
+	Signal_NotifySvcMgr("READY=1\n");
 } /* Rehash */
 
 /**
@@ -339,4 +348,89 @@ Signals_Exit(void)
 	signalpipe[0] = signalpipe[1] = 0;
 }
 
-/* -eof- */
+/**
+ * Notify the service manager using the "sd_notify" protocol.
+ *
+ * This function is based on the example notify() function shown in the
+ * sd_notify(3) manual page, with one significant difference: we keep the file
+ * descriptor open to reduce overhead when called multiple times.
+ *
+ * @param message: The message to pass to the service manager including "\n".
+ */
+GLOBAL void
+#if !defined(HAVE_SYS_UN_H) || !defined(SOCK_CLOEXEC)
+Signal_NotifySvcMgr(UNUSED const char *message)
+{
+	return;
+#else
+Signal_NotifySvcMgr(const char *message)
+{
+	struct sockaddr_un socket_addr;
+	const char *socket_path;
+	size_t path_length, message_length;
+	static int fd = NONE;
+
+	assert(message != NULL);
+	assert(message[0] != '\0');
+
+	if (fd == NONE) {
+		/* No socket to the service manager open: Check if a path name
+		 * is given in the environment and try to open it! */
+		socket_path = getenv("NOTIFY_SOCKET");
+		if (!socket_path)
+			return; /* No socket specified, nothing to do. */
+
+		/* Only AF_UNIX is supported, with path or abstract sockets */
+		if (socket_path[0] != '/' && socket_path[0] != '@') {
+			Log(LOG_CRIT,
+			"Failed to notify service manager: Unsupported socket path!");
+			return;
+		}
+
+		path_length = strlen(socket_path);
+
+		/* Ensure there is room for NUL byte */
+		if (path_length >= sizeof(socket_addr.sun_path)) {
+			Log(LOG_CRIT,
+			"Failed to notify service manager: Socket path too long!");
+			return;
+		}
+
+		memset(&socket_addr, 0, sizeof(struct sockaddr_un));
+		socket_addr.sun_family = AF_UNIX;
+		memcpy(socket_addr.sun_path, socket_path, path_length);
+
+		/* Support for abstract socket */
+		if (socket_addr.sun_path[0] == '@')
+			socket_addr.sun_path[0] = 0;
+
+		fd = socket(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+		if (fd < 0) {
+			Log(LOG_CRIT,
+			    "Failed to notify service manager: %s [socket()]",
+			    strerror(errno));
+			return;
+		}
+
+		if (connect(fd, (struct sockaddr *)&socket_addr,
+			    sizeof(struct sockaddr_un)) != 0) {
+			Log(LOG_CRIT,
+			    "Failed to notify service manager: %s [connect()]",
+			    strerror(errno));
+			close(fd);
+			fd = NONE;
+			return;
+		}
+	}
+
+	message_length = strlen(message);
+	ssize_t written = write(fd, message, message_length);
+	if (written != (ssize_t)message_length) {
+		Log(LOG_CRIT,
+			"Failed to notify service manager: %s [write()]",
+			strerror(errno));
+		close(fd);
+		fd = NONE;
+	}
+#endif
+}
diff --git a/src/ngircd/sighandlers.h b/src/ngircd/sighandlers.h
index 68491d94..e03864a3 100644
--- a/src/ngircd/sighandlers.h
+++ b/src/ngircd/sighandlers.h
@@ -1,5 +1,6 @@
 /*
  * ngIRCd -- The Next Generation IRC Daemon
+ * Copyright (c)2001-2024 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
@@ -21,6 +22,8 @@
 bool Signals_Init PARAMS((void));
 void Signals_Exit PARAMS((void));
 
+GLOBAL void Signal_NotifySvcMgr PARAMS((const char *message));
+
 #endif
 
 /* -eof- */