From 8ab097afb743061c6c9b865bdb401ba51285c347 Mon Sep 17 00:00:00 2001 From: Alexander Barton Date: Mon, 4 Feb 2013 21:46:20 +0100 Subject: [PATCH] Implement support for systemd(8) "socket activation" This patch enables ngIRCd to work with listening sockets already initialized and passed-in by systemd(8) and hereby to support on-demand "socket activation". systemd(8) uses two environment variables to pass information about the sockets to ngIRCd, LISTEN_PID and LISTEN_FDS, and this mechanism only kicks in when both variables are set. In all other cases, and therefore in most installations out there, nothing changes at all. Please note: If socket activation is in effect, ngIRCd will not initialize any (other) soeckets on its own! All sockets must be configured in the systemd(8) socket unit configuration file in this case, see ./contrib/ngircd.socket for example. Probably it would be interesting to match passed-in sockets to configured listening sockets and to initialize all the remaining ones not already set up by systemd(8), but this is kept back for an other patch ... See - - - --- contrib/Makefile.am | 3 +- contrib/README | 3 ++ contrib/ngircd.socket | 10 +++++++ src/ngircd/conn.c | 70 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 contrib/ngircd.socket 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 )); @@ -120,6 +122,40 @@ static void cb_Connect_to_Server PARAMS((int sock, UNUSED short what)); 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); -- 2.39.2