commit - ac32d07aaff0e7a1c4a544353dadbf397859d8f9
commit + 628c14d65686c4c848a17381b8ef61c78dbcf405
blob - 09b43a68a8bb71ef5ba2fb3dd02f9cee1f04bf14
blob + 6d16f7cac50f8b177fb4d336201906733b0a3607
--- contrib/Makefile.am
+++ contrib/Makefile.am
#
# 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
ngIRCd-Logo.gif \
ngircd-redhat.init \
ngircd.service \
+ ngircd.socket \
ngircd.spec \
platformtest.sh \
systrace.policy
blob - 1aebd1ebf6fe0f0d9cd944a99f99b1e4cb5f3cdf
blob + 2d639e664076d19c8b20a23e50a4d6426ddd6953
--- contrib/README
+++ contrib/README
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.
- -- Contributions --
+ -- Contributions --
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".
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.
blob - /dev/null
blob + 1c0cc004b176cdd15063ed1493d9994c42bdd330 (mode 644)
--- /dev/null
+++ contrib/ngircd.socket
+[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
blob - 1c3998ad61368fca9448013206c2cd35e5187779
blob + 822fd5d4fb9dd5b99f2694b5be98ce88075c68c5
--- doc/sample-ngircd.conf.tmpl
+++ doc/sample-ngircd.conf.tmpl
# 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
blob - 859c6a8ac0eb547b915e73fbed78cbf4e726c8a5
blob + e5485dbf10edddc2c7c0532b79902bd696fedbc4
--- man/ngircd.conf.5.tmpl
+++ man/ngircd.conf.5.tmpl
.\"
.\" 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
\fBConnectRetry\fR (number)
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
blob - 929ab05403734b8cd43e851955828072c38a0e33
blob + e46dcfee43669644091285586fd956b9c9a6c94c
--- src/ngircd/conf.c
+++ src/ngircd/conf.c
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);
/* Limits */
Conf_ConnectRetry = 60;
+ Conf_IdleTimeout = 0;
Conf_MaxConnections = 0;
Conf_MaxConnectionsIP = 5;
Conf_MaxJoins = 10;
fp = fopen(Filename, "r");
if (!fp) {
- Config_Error(LOG_WARNING, "Can't read %s file \"%s\": %s",
- Name, Filename, strerror(errno));
+ Config_Error(LOG_ERR, "Can't read %s file \"%s\": %s",
+ Name, Filename, strerror(errno));
return false;
}
/* 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;
}
return "[Options]";
}
if (strcasecmp(Var, "ConnectRetry") == 0
+ || strcasecmp(Var, "IdleTimeout") == 0
|| strcasecmp(Var, "MaxConnections") == 0
|| strcasecmp(Var, "MaxConnectionsIP") == 0
|| strcasecmp(Var, "MaxJoins") == 0
}
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"))
blob - c203b57032a9558a3d3e6efcf44dad9019e5a355
blob + bbf4f36c24c4ff29d1c2b3dc99ba58897f658443
--- src/ngircd/conf.h
+++ src/ngircd/conf.h
/*
* 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
/** 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;
blob - 14d337b9408265b1213a157cd36cf66a8efc057e
blob + 3a430428adc9832b8f06f16ddbe4052737548a33
--- src/ngircd/conn.c
+++ src/ngircd/conn.c
/*
* 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
#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 ));
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
* gets called when a new non-SSL connection should be accepted.
{
/* 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) {
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);
Log(LOG_ALERT, "%s exiting due to fatal errors!",
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;
}
}
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 */
Account_Connection(void)
{
NumConnections++;
+ idle_t = 0;
if (NumConnections > NumConnectionsMax)
NumConnectionsMax = NumConnections;
LogDebug("Total number of connections now %lu (max %lu).",
blob - 375f4bc1f6ab605ecbfe659367c82812b981a1a0
blob + e5bed7912f28401ab93884ee5e84ad4c0c5f7465
--- src/ngircd/log.c
+++ src/ngircd/log.c
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