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
 - <http://0pointer.de/blog/projects/socket-activation.html>
 - <http://0pointer.de/blog/projects/socket-activation2.html>
 - <http://www.freedesktop.org/software/systemd/man/systemd.socket.html>
This commit is contained in:
Alexander Barton 2013-02-04 21:46:20 +01:00
parent 84e24afd2f
commit 8ab097afb7
4 changed files with 85 additions and 1 deletions

View File

@ -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

View File

@ -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.

10
contrib/ngircd.socket Normal file
View File

@ -0,0 +1,10 @@
[Unit]
Description=Next Generation IRC Daemon (Socket)
[Socket]
ListenStream=6667
#ListenStream=6668
IPTOS=low-delay
[Install]
WantedBy=sockets.target

View File

@ -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);