]> arthur.barton.de Git - ngircd-alex.git/commitdiff
Merge branch 'systemd'
authorAlexander Barton <alex@barton.de>
Sun, 10 Feb 2013 19:43:56 +0000 (20:43 +0100)
committerAlexander Barton <alex@barton.de>
Sun, 10 Feb 2013 19:43:56 +0000 (20:43 +0100)
* 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

contrib/Makefile.am
contrib/README
contrib/ngircd.socket [new file with mode: 0644]
doc/sample-ngircd.conf.tmpl
man/ngircd.conf.5.tmpl
src/ngircd/conf.c
src/ngircd/conf.h
src/ngircd/conn.c
src/ngircd/log.c

index 09b43a68a8bb71ef5ba2fb3dd02f9cee1f04bf14..6d16f7cac50f8b177fb4d336201906733b0a3607 100644 (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
index 1aebd1ebf6fe0f0d9cd944a99f99b1e4cb5f3cdf..2d639e664076d19c8b20a23e50a4d6426ddd6953 100644 (file)
@@ -2,18 +2,21 @@
                      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".
@@ -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.
 
diff --git a/contrib/ngircd.socket b/contrib/ngircd.socket
new file mode 100644 (file)
index 0000000..1c0cc00
--- /dev/null
@@ -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
index 1c3998ad61368fca9448013206c2cd35e5187779..822fd5d4fb9dd5b99f2694b5be98ce88075c68c5 100644 (file)
        # 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
index 859c6a8ac0eb547b915e73fbed78cbf4e726c8a5..e5485dbf10edddc2c7c0532b79902bd696fedbc4 100644 (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.
index 929ab05403734b8cd43e851955828072c38a0e33..e46dcfee43669644091285586fd956b9c9a6c94c 100644 (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,8 +832,8 @@ 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",
-                                       Name, Filename, strerror(errno));
+               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"))
index c203b57032a9558a3d3e6efcf44dad9019e5a355..bbf4f36c24c4ff29d1c2b3dc99ba58897f658443 100644 (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;
 
index 14d337b9408265b1213a157cd36cf66a8efc057e..3a430428adc9832b8f06f16ddbe4052737548a33 100644 (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).",
index 375f4bc1f6ab605ecbfe659367c82812b981a1a0..e5bed7912f28401ab93884ee5e84ad4c0c5f7465 100644 (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