Merge branch 'systemd'

* systemd:
  ngircd.sock: explicitely bind to IPv4 and IPv6 addresses
  Show address and port of sockets passed-in by systemd(8)
  Check type of sockets passed-in by systemd(8)
  Adjust severity levels of some log messages
  New configuration option "IdleTimeout": exit daemon when idle
  Implement support for systemd(8) "socket activation"
  contrib/README: add more files
This commit is contained in:
Alexander Barton 2013-02-10 20:43:56 +01:00
commit 628c14d656
9 changed files with 173 additions and 11 deletions

View File

@ -1,6 +1,6 @@
# #
# ngIRCd -- The Next Generation IRC Daemon # 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 # 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 # it under the terms of the GNU General Public License as published by
@ -17,6 +17,7 @@ EXTRA_DIST = README \
ngIRCd-Logo.gif \ ngIRCd-Logo.gif \
ngircd-redhat.init \ ngircd-redhat.init \
ngircd.service \ ngircd.service \
ngircd.socket \
ngircd.spec \ ngircd.spec \
platformtest.sh \ platformtest.sh \
systrace.policy systrace.policy

View File

@ -2,7 +2,7 @@
ngIRCd - Next Generation IRC Server ngIRCd - Next Generation IRC Server
http://ngircd.barton.de/ http://ngircd.barton.de/
(c)2001-2011 Alexander Barton and Contributors. (c)2001-2013 Alexander Barton and Contributors.
ngIRCd is free software and published under the ngIRCd is free software and published under the
terms of the GNU General Public License. terms of the GNU General Public License.
@ -11,9 +11,12 @@
Debian/ Debian/
- Various files for building Debian GNU/Linux packages (".deb's"). - Various files for building Debian GNU/Linux packages (".deb's").
- ngircd.init; ngircd.default: init script for Debian-based systems.
- ngircd.pam: example PAM configuraton.
MacOSX/ MacOSX/
- Project files for XCode, the "project builder" of Apple Mac OS X. - Project files for XCode, the "project builder" of Apple Mac OS X.
- de.barton.ngircd.plist[.tmpl]: launchd(8) property list.
ngindent ngindent
- Script to indent the code of ngIRCd in the "standard way". - Script to indent the code of ngIRCd in the "standard way".
@ -24,6 +27,12 @@ ngircd-bsd.sh
ngircd-redhat.init ngircd-redhat.init
- Start/stop script for RedHat-based distributions (like CentOS). - Start/stop script for RedHat-based distributions (like CentOS).
ngircd.service
- systemd(8) service unit configuration file.
ngircd.socket
- systemd(8) socket unit configuration file for "socket activation".
ngircd.spec ngircd.spec
- RPM "spec" file. - RPM "spec" file.

11
contrib/ngircd.socket Normal file
View File

@ -0,0 +1,11 @@
[Unit]
Description=Next Generation IRC Daemon (Socket)
[Socket]
BindIPv6Only=ipv6-only
ListenStream=0.0.0.0:6667
#ListenStream=[::]:6667
IPTOS=low-delay
[Install]
WantedBy=sockets.target

View File

@ -88,6 +88,13 @@
# to not yet (or no longer) connected servers. # to not yet (or no longer) connected servers.
;ConnectRetry = 60 ;ConnectRetry = 60
# Number of seconds after which the whole daemon should shutdown when
# no connections are left active after handling at least one client
# (0: never, which is the default).
# This can be useful for testing or when ngIRCd is started using
# "socket activation" with systemd(8), for example.
;IdleTimeout = 0
# Maximum number of simultaneous in- and outbound connections the # Maximum number of simultaneous in- and outbound connections the
# server is allowed to accept (0: unlimited): # server is allowed to accept (0: unlimited):
;MaxConnections = 0 ;MaxConnections = 0

View File

@ -1,7 +1,7 @@
.\" .\"
.\" ngircd.conf(5) manual page template .\" ngircd.conf(5) manual page template
.\" .\"
.TH ngircd.conf 5 "Nov 2012" ngIRCd "ngIRCd Manual" .TH ngircd.conf 5 "Feb 2013" ngIRCd "ngIRCd Manual"
.SH NAME .SH NAME
ngircd.conf \- configuration file of ngIRCd ngircd.conf \- configuration file of ngIRCd
.SH SYNOPSIS .SH SYNOPSIS
@ -170,6 +170,12 @@ should be safe, but it is wise to double-check :-)
The server tries every <ConnectRetry> seconds to establish a link to not yet The server tries every <ConnectRetry> seconds to establish a link to not yet
(or no longer) connected servers. Default: 60. (or no longer) connected servers. Default: 60.
.TP .TP
\fBIdleTimeout\fR (number)
Number of seconds after which the whole daemon should shutdown when no
connections are left active after handling at least one client (0: never). This
can be useful for testing or when ngIRCd is started using "socket activation"
with systemd(8), for example. Default: 0.
.TP
\fBMaxConnections\fR (number) \fBMaxConnections\fR (number)
Maximum number of simultaneous in- and outbound connections the server is Maximum number of simultaneous in- and outbound connections the server is
allowed to accept (0: unlimited). Default: 0. allowed to accept (0: unlimited). Default: 0.

View File

@ -370,6 +370,7 @@ Conf_Test( void )
puts("[LIMITS]"); puts("[LIMITS]");
printf(" ConnectRetry = %d\n", Conf_ConnectRetry); printf(" ConnectRetry = %d\n", Conf_ConnectRetry);
printf(" IdleTimeout = %d\n", Conf_IdleTimeout);
printf(" MaxConnections = %d\n", Conf_MaxConnections); printf(" MaxConnections = %d\n", Conf_MaxConnections);
printf(" MaxConnectionsIP = %d\n", Conf_MaxConnectionsIP); printf(" MaxConnectionsIP = %d\n", Conf_MaxConnectionsIP);
printf(" MaxJoins = %d\n", Conf_MaxJoins > 0 ? Conf_MaxJoins : -1); printf(" MaxJoins = %d\n", Conf_MaxJoins > 0 ? Conf_MaxJoins : -1);
@ -736,6 +737,7 @@ Set_Defaults(bool InitServers)
/* Limits */ /* Limits */
Conf_ConnectRetry = 60; Conf_ConnectRetry = 60;
Conf_IdleTimeout = 0;
Conf_MaxConnections = 0; Conf_MaxConnections = 0;
Conf_MaxConnectionsIP = 5; Conf_MaxConnectionsIP = 5;
Conf_MaxJoins = 10; Conf_MaxJoins = 10;
@ -830,7 +832,7 @@ Read_TextFile(const char *Filename, const char *Name, array *Destination)
fp = fopen(Filename, "r"); fp = fopen(Filename, "r");
if (!fp) { if (!fp) {
Config_Error(LOG_WARNING, "Can't read %s file \"%s\": %s", Config_Error(LOG_ERR, "Can't read %s file \"%s\": %s",
Name, Filename, strerror(errno)); Name, Filename, strerror(errno));
return false; return false;
} }
@ -841,7 +843,7 @@ Read_TextFile(const char *Filename, const char *Name, array *Destination)
/* add text including \0 */ /* add text including \0 */
if (!array_catb(Destination, line, strlen(line) + 1)) { if (!array_catb(Destination, line, strlen(line) + 1)) {
Log(LOG_WARNING, "Cannot read/add \"%s\", line %d: %s", Log(LOG_ERR, "Cannot read/add \"%s\", line %d: %s",
Filename, line_no, strerror(errno)); Filename, line_no, strerror(errno));
break; break;
} }
@ -1241,6 +1243,7 @@ CheckLegacyGlobalOption(int Line, char *Var, char *Arg)
return "[Options]"; return "[Options]";
} }
if (strcasecmp(Var, "ConnectRetry") == 0 if (strcasecmp(Var, "ConnectRetry") == 0
|| strcasecmp(Var, "IdleTimeout") == 0
|| strcasecmp(Var, "MaxConnections") == 0 || strcasecmp(Var, "MaxConnections") == 0
|| strcasecmp(Var, "MaxConnectionsIP") == 0 || strcasecmp(Var, "MaxConnectionsIP") == 0
|| strcasecmp(Var, "MaxJoins") == 0 || strcasecmp(Var, "MaxJoins") == 0
@ -1490,6 +1493,12 @@ Handle_LIMITS(int Line, char *Var, char *Arg)
} }
return; return;
} }
if (strcasecmp(Var, "IdleTimeout") == 0) {
Conf_IdleTimeout = atoi(Arg);
if (!Conf_IdleTimeout && strcmp(Arg, "0"))
Config_Error_NaN(Line, Var);
return;
}
if (strcasecmp(Var, "MaxConnections") == 0) { if (strcasecmp(Var, "MaxConnections") == 0) {
Conf_MaxConnections = atoi(Arg); Conf_MaxConnections = atoi(Arg);
if (!Conf_MaxConnections && strcmp(Arg, "0")) if (!Conf_MaxConnections && strcmp(Arg, "0"))

View File

@ -1,6 +1,6 @@
/* /*
* ngIRCd -- The Next Generation IRC Daemon * 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 * 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 * it under the terms of the GNU General Public License as published by
@ -211,6 +211,9 @@ GLOBAL bool Conf_ConnectIPv6;
/** Try to connect to remote systems using the IPv4 protocol (true) */ /** Try to connect to remote systems using the IPv4 protocol (true) */
GLOBAL bool Conf_ConnectIPv4; GLOBAL bool Conf_ConnectIPv4;
/** Idle timout (seconds), after which the daemon should exit */
GLOBAL int Conf_IdleTimeout;
/** Maximum number of simultaneous connections to this server */ /** Maximum number of simultaneous connections to this server */
GLOBAL int Conf_MaxConnections; GLOBAL int Conf_MaxConnections;

View File

@ -1,6 +1,6 @@
/* /*
* ngIRCd -- The Next Generation IRC Daemon * 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 * 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 * it under the terms of the GNU General Public License as published by
@ -82,6 +82,8 @@
#define MAX_COMMANDS_SERVER_MIN 10 #define MAX_COMMANDS_SERVER_MIN 10
#define MAX_COMMANDS_SERVICE 10 #define MAX_COMMANDS_SERVICE 10
#define SD_LISTEN_FDS_START 3
static bool Handle_Write PARAMS(( CONN_ID Idx )); static bool Handle_Write PARAMS(( CONN_ID Idx ));
static bool Conn_Write PARAMS(( CONN_ID Idx, char *Data, size_t Len )); static bool Conn_Write PARAMS(( CONN_ID Idx, char *Data, size_t Len ));
@ -119,6 +121,42 @@ 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_Connect_to_Server PARAMS((int sock, UNUSED short what));
static void cb_clientserver PARAMS((int sock, short what)); static void cb_clientserver PARAMS((int sock, short what));
time_t idle_t = 0;
/**
* 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 * IO callback for listening sockets: handle new connections. This callback
@ -494,10 +532,71 @@ Conn_InitListeners( void )
{ {
/* Initialize ports on which the server should accept connections */ /* Initialize ports on which the server should accept connections */
unsigned int created = 0; unsigned int created = 0;
char *copy, *listen_addr; char *af_str, *copy, *listen_addr;
int count, fd, i, addr_len;
ng_ipaddr_t addr;
assert(Conf_ListenAddress); 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;
addr_len = (int)sizeof(addr);
getsockname(fd, (struct sockaddr *)&addr, (socklen_t*)&addr_len);
#ifdef WANT_IPV6
if (addr.sin4.sin_family != AF_INET && addr.sin4.sin_family != AF_INET6)
#else
if (addr.sin4.sin_family != AF_INET)
#endif
{
/* Socket is of unsupported type! For example, systemd passed in
* an IPv6 socket but ngIRCd isn't compiled with IPv6 support. */
switch (addr.sin4.sin_family)
{
case AF_UNSPEC: af_str = "AF_UNSPEC"; break;
case AF_UNIX: af_str = "AF_UNIX"; break;
case AF_INET: af_str = "AF_INET"; break;
#ifdef AF_INET6
case AF_INET6: af_str = "AF_INET6"; break;
#endif
#ifdef AF_NETLINK
case AF_NETLINK: af_str = "AF_NETLINK"; break;
#endif
default: af_str = "unknown"; break;
}
Log(LOG_CRIT,
"Socket %d is of unsupported type \"%s\" (%d), have to ignore it!",
fd, af_str, addr.sin4.sin_family);
close(fd);
continue;
}
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(8): %s:%d.", fd,
ng_ipaddr_tostr(&addr), ng_ipaddr_getport(&addr));
created++;
}
return created;
}
/* not using systemd socket activation, initialize listening sockets: */
/* can't use Conf_ListenAddress directly, see below */ /* can't use Conf_ListenAddress directly, see below */
copy = strdup(Conf_ListenAddress); copy = strdup(Conf_ListenAddress);
if (!copy) { if (!copy) {
@ -541,7 +640,12 @@ Conn_ExitListeners( void )
int *fd; int *fd;
size_t arraylen; 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)); arraylen = array_length(&My_Listeners, sizeof (int));
if (arraylen < 1)
return;
Log(LOG_INFO, Log(LOG_INFO,
"Shutting down all listening sockets (%d total) ...", arraylen); "Shutting down all listening sockets (%d total) ...", arraylen);
fd = array_start(&My_Listeners); fd = array_start(&My_Listeners);
@ -836,6 +940,15 @@ Conn_Handler(void)
PACKAGE_NAME); PACKAGE_NAME);
exit(1); exit(1);
} }
/* Should ngIRCd timeout when idle? */
if (Conf_IdleTimeout > 0 && NumConnectionsAccepted > 0
&& idle_t > 0 && time(NULL) - idle_t >= Conf_IdleTimeout) {
LogDebug("Server idle timeout reached: %d second%s. Initiating shutdown ...",
Conf_IdleTimeout,
Conf_IdleTimeout == 1 ? "" : "s");
NGIRCd_SignalQuit = true;
}
} }
if (NGIRCd_SignalQuit) if (NGIRCd_SignalQuit)
@ -1197,6 +1310,8 @@ Conn_Close( CONN_ID Idx, const char *LogMsg, const char *FwdMsg, bool InformClie
NumConnections--; NumConnections--;
LogDebug("Shutdown of connection %d completed, %ld connection%s left.", LogDebug("Shutdown of connection %d completed, %ld connection%s left.",
Idx, NumConnections, NumConnections != 1 ? "s" : ""); Idx, NumConnections, NumConnections != 1 ? "s" : "");
idle_t = NumConnections > 0 ? 0 : time(NULL);
} /* Conn_Close */ } /* Conn_Close */
@ -1568,6 +1683,7 @@ static void
Account_Connection(void) Account_Connection(void)
{ {
NumConnections++; NumConnections++;
idle_t = 0;
if (NumConnections > NumConnectionsMax) if (NumConnections > NumConnectionsMax)
NumConnectionsMax = NumConnections; NumConnectionsMax = NumConnections;
LogDebug("Total number of connections now %lu (max %lu).", LogDebug("Total number of connections now %lu (max %lu).",

View File

@ -109,7 +109,7 @@ Log_ReInit(void)
GLOBAL void GLOBAL void
Log_Exit( void ) Log_Exit( void )
{ {
Log(LOG_NOTICE, "%s done%s, served %lu connection%s.", PACKAGE_NAME, Log(LOG_INFO, "%s done%s, served %lu connection%s.", PACKAGE_NAME,
NGIRCd_SignalRestart ? " (restarting)" : "", Conn_CountAccepted(), NGIRCd_SignalRestart ? " (restarting)" : "", Conn_CountAccepted(),
Conn_CountAccepted() == 1 ? "" : "s"); Conn_CountAccepted() == 1 ? "" : "s");
#ifdef SYSLOG #ifdef SYSLOG