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

@ -2,7 +2,7 @@
ngIRCd - Next Generation IRC Server
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
terms of the GNU General Public License.
@ -11,9 +11,12 @@
Debian/
- 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/
- Project files for XCode, the "project builder" of Apple Mac OS X.
- de.barton.ngircd.plist[.tmpl]: launchd(8) property list.
ngindent
- Script to indent the code of ngIRCd in the "standard way".
@ -24,6 +27,12 @@ ngircd-bsd.sh
ngircd-redhat.init
- 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
- 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.
;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
# server is allowed to accept (0: unlimited):
;MaxConnections = 0

View File

@ -1,7 +1,7 @@
.\"
.\" 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
ngircd.conf \- configuration file of ngIRCd
.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
(or no longer) connected servers. Default: 60.
.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)
Maximum number of simultaneous in- and outbound connections the server is
allowed to accept (0: unlimited). Default: 0.

View File

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

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
@ -211,6 +211,9 @@ GLOBAL bool Conf_ConnectIPv6;
/** Try to connect to remote systems using the IPv4 protocol (true) */
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 */
GLOBAL int Conf_MaxConnections;

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
@ -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 ));
@ -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_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
@ -494,10 +532,71 @@ Conn_InitListeners( void )
{
/* Initialize ports on which the server should accept connections */
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);
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 */
copy = strdup(Conf_ListenAddress);
if (!copy) {
@ -541,7 +640,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);
@ -836,6 +940,15 @@ Conn_Handler(void)
PACKAGE_NAME);
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)
@ -1197,6 +1310,8 @@ Conn_Close( CONN_ID Idx, const char *LogMsg, const char *FwdMsg, bool InformClie
NumConnections--;
LogDebug("Shutdown of connection %d completed, %ld connection%s left.",
Idx, NumConnections, NumConnections != 1 ? "s" : "");
idle_t = NumConnections > 0 ? 0 : time(NULL);
} /* Conn_Close */
@ -1568,6 +1683,7 @@ static void
Account_Connection(void)
{
NumConnections++;
idle_t = 0;
if (NumConnections > NumConnectionsMax)
NumConnectionsMax = NumConnections;
LogDebug("Total number of connections now %lu (max %lu).",

View File

@ -109,7 +109,7 @@ Log_ReInit(void)
GLOBAL 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(),
Conn_CountAccepted() == 1 ? "" : "s");
#ifdef SYSLOG