commit 8ab097afb743061c6c9b865bdb401ba51285c347 from: Alexander Barton date: Mon Feb 04 20:52:27 2013 UTC 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 - - - commit - 84e24afd2f6607a2345c4df2b2f9ad81e9dd4bbc commit + 8ab097afb743061c6c9b865bdb401ba51285c347 blob - 09b43a68a8bb71ef5ba2fb3dd02f9cee1f04bf14 blob + 6d16f7cac50f8b177fb4d336201906733b0a3607 --- contrib/Makefile.am +++ 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 blob - f3730a4e969c69aed07bbfdfb737a7901b382fa8 blob + 2d639e664076d19c8b20a23e50a4d6426ddd6953 --- contrib/README +++ 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. blob - /dev/null blob + 3838efcc1c278e315693d307013c4e8b2be41b09 (mode 644) --- /dev/null +++ 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 blob - 14d337b9408265b1213a157cd36cf66a8efc057e blob + 378509f96f09bc35eb9e3ebc2eb12140c24509fb --- src/ngircd/conn.c +++ src/ngircd/conn.c @@ -82,7 +82,9 @@ #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 )); static int New_Connection PARAMS(( int Sock, bool IsSSL )); @@ -118,6 +120,40 @@ static void cb_clientserver_ssl PARAMS((int sock, shor static void cb_Read_Resolver_Result PARAMS((int sock, UNUSED short what)); 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; +} /** @@ -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);