Browse Source

Add support for multiple instances, and native systemd support

Christoph Biedl 5 years ago
parent
commit
0a300a9ab3

+ 49 - 0
debian/README.Debian

@@ -0,0 +1,49 @@
+
+Multiple softflowd instances
+============================
+
+Starting with version 0.9.9-4, the Debian packaging of softflowd
+supports handling of multiple instances. One instance is already
+shipped as `/etc/softflowd/default.conf` (which is a re-used
+`/etc/default/softflowd` when upgrading from an older version).
+
+To add more instances, create a file `/etc/softflowd/<instance>.conf`
+where `<instance>` should be constructed using the "Portable Filename
+Character Set"[1] and must not start with a dot (i.e. hidden file).
+
+The file is a POSIX shell fragment and defines two variables:
+
+* interface: The interface to listen on, or `all`
+  Without an interface definition, the instance will be ignored.
+* options: Additional options to pass to softflowd.
+
+On systemd, the file is read as a key/value store, for details see
+`EnvironmentFile=` systemd.exec(5).
+
+On SysV init, the file is run as a shell script.
+
+
+The pidfile will be stored at `/run/softflowd/<instance>.pid`[2], do not
+attempt to override that. The control socket for softflowctl is at
+`/run/softflowd/<instance>.ctl`. You still may override these using the
+`-c` options in the options= string, last match wins.
+
+The softflowctl program will query the "default" instance by default.
+
+
+To add a new instance:
+
+Create an according configuration file first.
+
+On systemd, run `systemctl daemon-reload`, then
+`systemctl start softflowd@<instance>.service`.
+
+On SysV init, run `serivce softflowd start <instance>`.
+
+
+[1] Upper and lower case characters, digits, dot, underscore, dash.
+    For pure visual reasons, the dash should be avoided on systemd as
+    it's escaped in the service name.
+    http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_282
+[2] On SysV init systems: `/var/run/softflowd/<instance>.pid`, likewise
+    for the control socket.

+ 18 - 0
debian/default.conf

@@ -0,0 +1,18 @@
+
+#
+# configuration for a single softflowd instance
+#
+
+# See /usr/share/doc/softflowd/README.Debian
+
+# The interface softflowd listens on. You may also use "any" to listen
+# on all interfaces. Mandatory.
+interface=''
+
+# Further options for softflowd, see "man softflowd" for details.
+# You should at least define a host and a port where the accounting
+# datagrams should be sent to, e.g.
+# options="-n 127.0.0.1:9995"
+# You may override the control socket location (-c) if you really want to.
+# Do not override the pid file location (-p).
+options=''

+ 31 - 0
debian/softflowd-generator

@@ -0,0 +1,31 @@
+#!/bin/sh
+
+set -eu
+
+SERVICEFILE="/lib/systemd/system/softflowd@.service"
+WANTDIR="$1/softflowd.service.wants"
+
+CONFIG_DIR=/etc/softflowd/
+
+if [ -d "$CONFIG_DIR" ] ; then
+    mkdir -p "$WANTDIR"
+    cd "$CONFIG_DIR"
+    for CONFIG in *.conf ; do
+        [ -f "$CONFIG" ] || continue
+        INSTANCE="$(systemd-escape "${CONFIG%%.conf}")"
+        LINK="$WANTDIR/softflowd@$INSTANCE.service"
+
+        sh -n "$CONFIG_DIR$CONFIG" 2>/dev/null || continue
+
+        interface=
+        options=
+
+        . "$CONFIG_DIR$CONFIG"
+
+        [ "$interface" ] || continue
+
+        ln -s "$SERVICEFILE" "$LINK"
+    done
+fi
+
+exit 0

+ 0 - 14
debian/softflowd.default

@@ -1,14 +0,0 @@
-#
-# configuration for softflowd
-#
-# note: softflowd will not start without an interface configured.
-
-# The interface softflowd listens on. You may also use "any" to listen
-# on all interfaces.
-INTERFACE=""
-
-# Further options for softflowd, see "man softflowd" for details.
-# You should at least define a host and a port where the accounting
-# datagrams should be sent to, e.g.
-# OPTIONS="-n 127.0.0.1:9995"
-OPTIONS=""

+ 132 - 109
debian/softflowd.init

@@ -1,4 +1,4 @@
-#! /bin/sh
+#!/bin/sh
 
 
 ### BEGIN INIT INFO
 ### BEGIN INIT INFO
 # Provides:          softflowd
 # Provides:          softflowd
@@ -6,136 +6,159 @@
 # Required-Stop:     $network $remote_fs $syslog
 # Required-Stop:     $network $remote_fs $syslog
 # Default-Start:     2 3 4 5
 # Default-Start:     2 3 4 5
 # Default-Stop:      0 1 6
 # Default-Stop:      0 1 6
-# Short-Description: Flow-based network traffic analyser.
+# Short-Description: Flow-based network traffic analyser
+# Description:       Manage all softflowd exports defined in
+#                    /etc/softflowd/*.conf
 ### END INIT INFO
 ### END INIT INFO
 
 
-# Author: Christoph Biedl <debian.axhn@manchmal.in-ulm.de>
-# Based on /etc/init.d/skeleton, written by
-# Miquel van Smoorenburg <miquels@cistron.nl>,
-# modified for Debian by Ian Murdock <imurdock@gnu.ai.mit.edu>.
-
 PATH=/sbin:/usr/sbin:/bin:/usr/bin
 PATH=/sbin:/usr/sbin:/bin:/usr/bin
-DAEMON=/usr/sbin/softflowd
 NAME=softflowd
 NAME=softflowd
-DESC="Flow-based network traffic analyser"
-DEFAULT=/etc/default/$NAME
-PIDFILE=/var/run/$NAME.pid
-PRIVDROP_CHROOT_DIR=/var/run/softflowd/chroot
+DESC='Flow-based network traffic analyser'
+SOFTFLOWD="/usr/sbin/$NAME"
+PIDDIR="/var/run/$NAME/"
+PRIVDROP_CHROOT_DIR='/var/run/softflowd/chroot'
 
 
-test -x "$DAEMON" || exit 0
+[ -x "$SOFTFLOWD" ] || exit 0
 
 
-# Include softflowd defaults if available
-INTERFACE=
-OPTIONS=
-[ -r "$DEFAULT" ] && . "$DEFAULT"
+if [ "$1" = 'start' ] ; then
+    mkdir -p "$PRIVDROP_CHROOT_DIR"
+    if [ -e /var/run/softflowd.ctl ] ; then
+        [ -L /var/run/softflowd.ctl ] || rm /var/run/softflowd.ctl
+    fi
+    [ -e /var/run/softflowd.ctl ] || ln -s /var/run/softflowd/default.ctl /var/run/softflowd.ctl
+fi
 
 
 # Load the VERBOSE setting and other rcS variables
 # Load the VERBOSE setting and other rcS variables
 . /lib/init/vars.sh
 . /lib/init/vars.sh
 
 
-# Define LSB log_* functions.
+# Define LSB functions
 . /lib/lsb/init-functions
 . /lib/lsb/init-functions
 
 
+# Start a softflowd instance
+#
+# Return
+#   0 if daemon has been started
+#   1 if daemon was already running
+#   2 if daemon could not be started
+do_start () {
+    local INSTANCE="$1"
+    local CONFIG="$2"
 
 
-case "$1" in
-start|restart|force-reload)
-    if [ -z "$INTERFACE" ] ; then
-	log_daemon_msg "NOT starting $DESC" "$NAME"
-	log_action_msg "\nEdit $DEFAULT and define the INTERFACE variable"
-	log_end_msg 1
-	exit 0
-    fi
-    ;;
-esac
+    sh -n "$CONFIG" 2>/dev/null || return 2
 
 
-[ -d "$PRIVDROP_CHROOT_DIR" ] || mkdir -p "$PRIVDROP_CHROOT_DIR"
+    interface=
+    options=
+
+    . "$CONFIG"
+
+    [ "$interface" ] || return 2
+
+    local PIDFILE="$PIDDIR$INSTANCE.pid"
+    local CTLSOCK="$PIDDIR$INSTANCE.ctl"
+    start-stop-daemon --start --quiet \
+        --pidfile "$PIDFILE" --exec "$VBLADE" --test > /dev/null \
+        || return 1
+    start-stop-daemon --start --quiet \
+        --pidfile "$PIDFILE" \
+        --exec $ionice "$SOFTFLOWD" -- \
+        -p "$PIDFILE" -c "$CTLSOCK" \
+        -i "$interface" $options \
+        || return 2
 
 
-#
-# Function that starts the daemon/service
-#
-do_start()
-{
-    # Return
-    #   0 if daemon has been started
-    #   1 if daemon was already running
-    #   2 if daemon could not be started
-    start-stop-daemon --start --quiet --pidfile $PIDFILE \
-	--exec $DAEMON --test > /dev/null || \
-	return 1
-    start-stop-daemon --start --quiet --pidfile $PIDFILE \
-	--exec $DAEMON -- -i "$INTERFACE" $OPTIONS || \
-	return 2
 }
 }
 
 
+# Stop a softflowd instance
 #
 #
-# Function that stops the daemon/service
-#
-do_stop()
-{
-    # Return
-    #   0 if daemon has been stopped
-    #   1 if daemon was already stopped
-    #   2 if daemon could not be stopped
-    #   other if a failure occurred
-    start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 \
-	--pidfile $PIDFILE --name $NAME
+# Return
+#   0 if daemon has been stopped
+#   1 if daemon was already stopped
+#   2 if daemon could not be stopped
+#   other if a failure occurred
+do_stop () {
+    local INSTANCE="$1"
+
+    local PIDFILE="$PIDDIR/$INSTANCE.pid"
+    start-stop-daemon --stop --quiet \
+        --retry=TERM/30/KILL/5 --pidfile "$PIDFILE" --name "$NAME"
     RETVAL="$?"
     RETVAL="$?"
     [ "$RETVAL" = 2 ] && return 2
     [ "$RETVAL" = 2 ] && return 2
-    # Wait for children to finish too if this is a daemon that forks
-    # and if the daemon is only ever run from this initscript.
-    # If the above conditions are not satisfied then add some other code
-    # that waits for the process to drop all resources that could be
-    # needed by services started subsequently.  A last resort is to
-    # sleep for some time.
-    start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 \
-	--exec $DAEMON
-    [ "$?" = 2 ] && return 2
     # Many daemons don't delete their pidfiles when they exit.
     # Many daemons don't delete their pidfiles when they exit.
-    rm -f $PIDFILE
+    rm -f "$PIDFILE"
     return "$RETVAL"
     return "$RETVAL"
 }
 }
 
 
-case "$1" in
-start)
-    [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME"
-    do_start
-    case "$?" in
-	0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
-	2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
-    esac
-    ;;
-stop)
-    [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
-    do_stop
-    case "$?" in
-	0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
-	2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
-    esac
-    ;;
-status)
-    status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $?
-    ;;
-restart|force-reload)
-    log_daemon_msg "Restarting $DESC" "$NAME"
-    do_stop
-    case "$?" in
-	0|1)
-	    do_start
-	    case "$?" in
-		0) log_end_msg 0 ;;
-		1) log_end_msg 1 ;; # Old process is still running
-		*) log_end_msg 1 ;; # Failed to start
-	    esac
-	    ;;
-	*)
-	    # Failed to stop
-	    log_end_msg 1
-	    ;;
+EXIT=0
+
+do_action () {
+    local CONFIG="$1"
+
+    INSTANCE="$(basename "${CONFIG%%.conf}")"
+
+    case "$ACTION" in
+        start)
+            [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$INSTANCE"
+            do_start "$INSTANCE" "$CONFIG"
+            case "$?" in
+                0|1)    [ "$VERBOSE" != no ] && log_end_msg 0 ;;
+                2)      [ "$VERBOSE" != no ] && log_end_msg 1 ;;
+            esac
+            ;;
+        stop)
+            [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$INSTANCE"
+            do_stop "$INSTANCE"
+            case "$?" in
+                0|1)    [ "$VERBOSE" != no ] && log_end_msg 0 ;;
+                2)      [ "$VERBOSE" != no ] && log_end_msg 1 ;;
+            esac
+            ;;
+        status)
+            status_of_proc -p "$PIDDIR/$INSTANCE.pid" "$SOFTFLOWD" "softflowd instance $INSTANCE" || EXIT=$?
+            ;;
+        restart|force-reload)
+            log_daemon_msg "Restarting $DESC" "$INSTANCE"
+            do_stop "$INSTANCE"
+            case "$?" in
+                0|1)
+                    do_start "$INSTANCE" "$CONFIG"
+                    case "$?" in
+                        0)  log_end_msg 0 ;;
+                        *)
+                            # Old process is still running or
+                            # failed to start
+                            log_end_msg 1 ;;
+                    esac
+                    ;;
+                *)
+                    # Failed to stop
+                    log_end_msg 1
+                    ;;
+                esac
+            ;;
+        *)
+            echo "Usage: /etc/init.d/softflowd {start|stop|status|restart|force-reload} [<export> ...]" >&2
+            exit 3
+            ;;
     esac
     esac
-    ;;
-*)
-    echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2
-    exit 3
-    ;;
-esac
-
-:
+}
+
+
+ACTION="$1"
+shift
+
+if [ "$1" ] ; then
+    while [ "$1" ] ; do
+        CONFIG="/etc/softflowd/$1.conf"
+        if [ -f "$CONFIG" ] ; then
+            do_action "$CONFIG"
+        fi
+        shift
+    done
+else
+    for CONFIG in /etc/softflowd/*.conf ; do
+        if [ -f "$CONFIG" ] ; then
+            do_action "$CONFIG"
+        fi
+    done
+fi
+
+exit $EXIT

+ 3 - 0
debian/softflowd.install

@@ -0,0 +1,3 @@
+
+debian/default.conf         etc/softflowd/
+debian/softflowd-generator  lib/systemd/system-generators/

+ 2 - 0
debian/softflowd.maintscript

@@ -0,0 +1,2 @@
+
+mv_conffile /etc/default/softflowd /etc/softflowd/default.conf 0.9.9-4~

+ 35 - 0
debian/softflowd.preinst

@@ -0,0 +1,35 @@
+#!/bin/sh
+
+set -e
+
+DEFAULT_CONF='/etc/softflowd/default.conf'
+LEGACY_CONF='/etc/default/softflowd'
+
+case "$1" in
+install)
+    ;;
+upgrade)
+    if \
+        dpkg --compare-versions "$2" lt 0.9.9-4~ &&
+        [ -f "$LEGACY_CONF" ] &&
+        [ ! -f "$DEFAULT_CONF" ]
+    then
+        # create /etc/softflowd/default.conf from /etc/default/softflowd
+        # lower-case key names
+        mkdir -p "$(dirname "$DEFAULT_CONF")"
+        sed  -e '
+            s/^INTERFACE=/interface=/ ;
+            s/^OPTIONS=/options=/ ;
+        ' <"$LEGACY_CONF" >"$DEFAULT_CONF"
+    fi
+    ;;
+abort-upgrade)
+    ;;
+*)
+    echo "preinst called with unknown argument '$1'"
+    exit 1
+    ;;
+esac
+
+#DEBHELPER#
+exit 0

+ 12 - 0
debian/softflowd.service

@@ -0,0 +1,12 @@
+[Unit]
+Description=Flow-based network traffic analyser
+Documentation=man:softflowd(8)
+
+[Service]
+Type=oneshot
+ExecStart=/bin/true
+ExecReload=/bin/true
+RemainAfterExit=on
+
+[Install]
+WantedBy=multi-user.target

+ 3 - 0
debian/softflowd.tmpfile

@@ -0,0 +1,3 @@
+
+d /run/softflowd/chroot      0755 root root
+L /run/softflowd/default.ctl -    -    -    - /var/run/softflowd.ctl

+ 17 - 0
debian/softflowd@.service

@@ -0,0 +1,17 @@
+[Unit]
+Description=softflowd %I instance
+SourcePath=/etc/softflowd/%I.conf
+Documentation=man:softflowd(8)
+PartOf=softflowd.service
+After=rc-local.service
+
+[Service]
+Type=forking
+EnvironmentFile=/etc/softflowd/%I.conf
+ExecStart=/usr/sbin/softflowd -i $interface -p /run/softflowd/%I.pid -c /run/softflowd/%I.ctl $options
+Restart=always
+RestartSec=10
+PIDFile=/run/softflowd/%I.pid
+
+[Install]
+WantedBy=multi-user.target