commit 3abfdb4bb044b6b0176ff987e92ca005810e25c7
Author: Cem Keylan <cem@ckyln.com>
Date:   Sun, 23 Feb 2020 15:17:33 +0300
initial commit
Diffstat:
| A | LICENSE |  |  | 21 | +++++++++++++++++++++ | 
| A | Makefile |  |  | 16 | ++++++++++++++++ | 
| A | sysmgr |  |  | 196 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ | 
3 files changed, 233 insertions(+), 0 deletions(-)
diff --git a/LICENSE b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2020 Cem Keylan
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/Makefile b/Makefile
@@ -0,0 +1,16 @@
+# See LICENSE for copyright information
+VERSION  = 0.01.0
+
+PREFIX   = /usr/local
+BINDIR   = ${PREFIX}/bin
+SHAREDIR = ${PREFIX}/share
+
+LINK = runsyssv svctl
+
+install:
+	install -Dm755 sysmgr ${DESTDIR}${BINDIR}/sysmgr
+	for link in ${LINK} ; do ln -sf sysmgr ${DESTDIR}${BINDIR}/$$link ; done
+
+uninstall:
+	rm -rf ${DESTDIR}${BINDIR}/sysmgr
+	for link in ${LINK} ; do unlink ${DESTDIR}${BINDIR}/$$link ; done
diff --git a/sysmgr b/sysmgr
@@ -0,0 +1,196 @@
+#!/bin/sh -e
+
+###########################################################
+# A modular system-supervisor written in POSIX shell      #
+# written with Carbs Linux[1] in mind.                    #
+#                                                         #
+# [1]: https://carbslinux.org                             #
+#                                                         #
+# Copyright (c) 2020 - Cem Keylan                         #
+# See LICENSE for copyright information                   #
+#                                                         #
+# Please report bugs to <cem at ckyln dot com>            #
+###########################################################
+
+
+# Shellcheck gives an error telling us that 'printf ->' is
+# undefined, but since we do want to print '->' this can
+# be safely ignored.
+# shellcheck disable=SC2039
+out()   { printf '-> %s\n' "$@" ;}
+error() { printf '-> error: %s\n' "$@" ;}
+die()   { printf '-> error: %s\n' "$@" "exiting..." ; exit 1 ;}
+
+RUNDIR="${RUNDIR:-/run/sysmgr}"
+SYSDIR="${SYSDIR:-/var/sysmgr}"
+
+cleanup() {
+
+    # Clean the service run directory so that it can be
+    # restarted. Do not remove the run directory if lock
+    # file exists.
+
+    [ -e "${RUNDIR:?}/${service##*/}/lock" ] && return 0
+    rm -rf "${RUNDIR:?}/${service##*/}"
+
+}
+
+term() {
+
+    # This function is executed when the sysmgr receives an
+    # interrupt or a hangup signal. It enters the termination
+    # state where it forwards SIGTERM to every other runsyssv
+    # process that have their process ids in the RUNDIR
+
+    for process in "${RUNDIR:?}"/*/syspid ; do
+        kill -SIGTERM "$(cat "$process")" 2>/dev/null
+    done
+
+    # Wait for the redirections to happen
+    sleep 1
+
+    # Remove the RUNDIR so we can do a fresh start when we
+    # are re-initiating the program.
+    rm -rf "${RUNDIR}"
+
+    exit 0
+}
+
+redirectsignal() {
+
+    # We redirect signal that was sent to runsyssv so that
+    # those programs are stopped with the exact kill command.
+    # Adding a lock file ensures that the directory is not
+    # cleaned up.
+    sig="$1"
+
+    printf '%s\n' "${sig:-15}" > "${RUNDIR:?}/${service##*/}/lock"
+    kill "-${sig:-15}" "$svpid"
+
+}
+
+fn_sysmgr() {
+    [ "$1" ] && { 
+        printf 'Usage: %s\n\nStarts a sysmgr instance.\n' "${0##*/}" 
+        exit 0
+    }
+    
+    # Start sanity checks. We first check that we have
+    # the "$SYSDIR" variable. We then check whether the
+    # given SYSDIR exists, and has service files installed.
+    [ "$SYSDIR" ] || die "Please specify service directory"
+    [ -d "$SYSDIR" ] || die "$SYSDIR does not exist."
+    [ "$(find "$SYSDIR" -type f)" ] || die "No service file is found"
+    mkdir -p "$RUNDIR" || die
+
+    # Add pid to $RUNDIR before starting loops
+    printf '%s\n' "$$" > "$RUNDIR/pid"
+
+    # We redirect hangup and interrupt signals to the
+    # 'term' function so that we send kill signals to
+    # all sysmgr processes
+    for sig in 1 2 ; do
+        trap term "$sig"
+    done
+
+    # Lots of loops here. The first while loop is to
+    # make sure that the sysmgr does not exist. The
+    # for loop is to run every single service on the
+    # $SYSDIR. We then fork the runsyssv function to
+    # the background. This ensures that we don't have
+    # to wait until runsyssv has finished, which is a
+    # program that is not supposed to exit.
+    while sleep 1 ; do
+        for service in "$SYSDIR"/* ; do
+            [ -x "$service" ] || error "$service is not an executable file"
+            ! [ -d "$RUNDIR/${service##*/}" ] && runsyssv "$service" &
+        done
+    done
+}
+
+fn_runsyssv() {
+
+    # This is a really hacky way to handle '--help'
+    # I just do not want to add 'usage' functions nor
+    # call the same command twice. Just remove the $1
+    # if it is the typical help flag.
+    case "$1" in -h|--help|help) shift ;; esac
+    [ "$1" ] || { printf 'Usage: %s <service-file>\n\nRuns the given service script.\n' "${0##*/}" ; exit 0 ;}
+
+    # Record service name in a variable
+    service="$1"
+
+    # This is the simplest way of checking whether a
+    # service is running (or killed by the user with
+    # ctl, so that it does not run again).
+    [ -e "/run/sysmgr/${service##*/}" ] && exit 1
+
+    # Create the run directory for the service where
+    # we will be adding the pid value when we start
+    # the process.
+    mkdir -p "$RUNDIR/${service##*/}"
+    
+    # Start the service script. If the service fails
+    # exit with failure code 1. If the service exits
+    # without a failure (which it probably shouldn't)
+    # exit with code 0
+    "$service" &
+    svpid="$!"
+    printf '%s\n' "$svpid" > "$RUNDIR/${service##*/}/pid"
+    printf '%s\n' "$$" > "$RUNDIR/${service##*/}/syspid"
+    
+    for sig in 1 2 3 6 15 ; do
+        # We want to trap every signal with their own
+        # value so that we kill the service with the
+        # requested signal.
+        # shellcheck disable=SC2064
+        trap "redirectsignal $sig" $sig
+    done
+
+    # check whether services are alive
+    while kill -0 "$svpid" >/dev/null 2>&1 ; do sleep 1 ; done
+
+    # Do a cleanup when the service is killed
+    cleanup
+}
+
+fn_svctl() {
+    [ "$2" ] || {
+        printf 'usage: %s <kill|restart|stop|start> <service>\n' "${0##*/}"
+        exit 0
+    }
+    [ -d "${RUNDIR:?}/$2" ] || die "service $2 could not be found."
+    case "$1" in
+        restart)
+            fn_svctl kill "$2"
+            fn_svctl start "$2"
+            ;;
+        kill)
+            printf '9\n' > "${RUNDIR:?}/$2/lock"
+            kill -9 "$(cat "${RUNDIR:?}/$2/pid")" 2>/dev/null
+            ;;
+        stop)
+            printf '15\n' > "${RUNDIR:?}/$2/lock"
+            kill -15 "$(cat "${RUNDIR:?}/$2/pid")" 2>/dev/null
+            ;;
+        start)
+            rm -rf "${RUNDIR:?}/${2}"
+            ;;
+        *)
+            exit 1
+            ;;
+    esac
+}
+
+main() {
+    # Call the appropriate function depending on the
+    # name of the program.
+    case "${0##*/}" in
+        sysmgr) fn_sysmgr "$@" ;;
+        runsyssv) fn_runsyssv "$@" ;;
+        svctl) fn_svctl "$@" ;;
+        *) printf '%s is not a function\n' "${0##*/}" ; exit 1 ;;
+    esac
+}
+
+main "$@"