#!/bin/sh
# Filename: systemctl
# Location: /usr/sbin/
# Author: bgstack15@gmail.com
# Startdate: 2020-01-10 13:02:14
# Title: 
# Purpose: 
# Package: systemdtl
# History: 
# Usage: 
#    Should be mostly like systemctl from systemd.
# Reference: ftemplate.sh 2019-05-02a ; framework.sh 2018-05-02a
#    man 1 systemctl
# Improve:
#  x restart X(.service)?
#  x start
#  x stop
#  x enable
#  x status
#  x disable
#  x mask
#  x unmask
#  x is-enabled
#  x is-active
#  x list-unit-files --full
#  x reload-or-try-restart X
#  x condrestart X
#  x daemon-reload --system
#  x --now
fiversion="2019-05-02a"
systemctlversion="2020-01-10a"

usage() {
   ${PAGER:-/usr/bin/less -F} >&2 <<ENDUSAGE
usage: systemctl [-duV] [-c conffile]
Provides a systemctl-like interface to sysvinit. Simulates (poorly)
various actions on services: start, stop, restart, enable, disable,
status, mask, unmask, is-enabled, is-active, condrestart
version ${systemctlversion}
 -d debug   Show debugging info, including parsed variables.
 -u usage   Show this usage block.
 -V version Show script version number.
 -c conf    Read in this config file.
Return values:
 0 Normal
 1 Queried service is disabled/inactive
 2 Count or type of flaglessvals is incorrect
 3 Incorrect OS type
 4 Unable to find dependency
 5 Not run as root or sudo
ENDUSAGE
}

# DEFINE FUNCTIONS

# DEFINE TRAPS

clean_systemctl() {
   # use at end of entire script if you need to clean up tmpfiles
   # rm -f "${tmpfile1}" "${tmpfile2}" 2>/dev/null

   # Delayed cleanup
   if test -z "${SYSTEMCTL_NO_CLEAN}" ;
   then
#      nohup /bin/bash <<EOF 1>/dev/null 2>&1 &
#sleep "${SYSTEMCTL_CLEANUP_SEC:-300}" ; /bin/rm -r "${SYSTEMCTL_TMPDIR:-NOTHINGTODELETE}" 1>/dev/null 2>&1 ;
#EOF
   /bin/rm -r "${SYSTEMCTL_TMPDIR:-NOTHINGTODELETE}" 1>/dev/null 2>&1 ;
   fi
}

CTRLC() {
   # use with: trap "CTRLC" 2
   # useful for controlling the ctrl+c keystroke
   :
}

CTRLZ() {
   # use with: trap "CTRLZ" 18
   # useful for controlling the ctrl+z keystroke
   :
}

parseFlag() {
   flag="$1"
   hasval=0
   case ${flag} in
      # INSERT FLAGS HERE
      "d" | "debug" | "DEBUG" | "dd" ) setdebug ; ferror "debug level ${debug}" ; __debug_set_by_param=1 ;;
      "u" | "usage" | "help" | "h" ) usage ; exit 0 ;;
      "V" | "fcheck" | "version" ) ferror "${scriptfile} version ${systemctlversion}" ; exit 0 ;;
      #"i" | "infile" | "inputfile" ) getval ; infile1=${tempval} ;;
      "c" | "conf" | "conffile" | "config" ) getval ; conffile="${tempval}" ;;
      "now" ) export SYSTEMCTL_NOW=1 ;;
      "full") export SYSTEMCTL_FULL=1 ;;
      "system") export SYSTEMCTL_SYSTEM=1 ;;
   esac

   debuglev 10 && { test ${hasval} -eq 1 && ferror "flag: ${flag} = ${tempval}" || ferror "flag: ${flag}" ; }
}

# DETERMINE LOCATION OF FRAMEWORK
f_needed=20181030
___frameworkpath="$( find $( echo "${FRAMEWORKPATH}" | tr ':' ' ' ) -maxdepth 1 -mindepth 0 -name 'framework.sh' 2>/dev/null )"
while read flocation ; do if test -e ${flocation} ; then __thisfver="$( sh ${flocation} --fcheck 2>/dev/null )" ; if test ${__thisfver:-0} -ge ${f_needed} ; then frameworkscript="${flocation}" ; break ; elif test -n "${___thisfver}" ; then printf "Obsolete: %s %s\n" "${flocation}" "${__thisfver}" 1>&2 ; fi ; fi ; done <<EOFLOCATIONS
${FRAMEWORKBIN:-/bin/false}
${___frameworkpath:-/bin/false}
./framework.sh
${scriptdir}/framework.sh
$HOME/bin/bgscripts/framework.sh
$HOME/bin/framework.sh
$HOME/bgscripts/framework.sh
$HOME/framework.sh
$HOME/.local/share/bgscripts/framework.sh
/usr/local/bin/bgscripts/framework.sh
/usr/local/bin/framework.sh
/usr/bin/bgscripts/framework.sh
/usr/bin/framework.sh
/bin/bgscripts/framework.sh
/usr/local/share/bgscripts/framework.sh
/usr/share/bgscripts/framework.sh
EOFLOCATIONS
test -z "${frameworkscript}" && echo "$0: framework ${f_needed} not found. Try setting FRAMEWORKPATH. Aborted." 1>&2 && exit 4

# INITIALIZE VARIABLES
# variables set in framework:
# today server thistty scriptdir scriptfile scripttrim
# is_cronjob stdin_piped stdout_piped stderr_piped sendsh sendopts
. ${frameworkscript} || echo "$0: framework did not run properly. Continuing..." 1>&2
infile1=
outfile1=
define_if_new logfile "/var/log/systemctl.log"
define_if_new interestedparties "bgstack15@gmail.com"
# SIMPLECONF
define_if_new default_conffile "/etc/systemdtl.conf"
#define_if_new defuser_conffile ~/.config/systemctl/systemctl.conf
#define_if_new SYSTEMCTL_TMPDIR "$( mktemp -d )"
#tmpfile1="$( TMPDIR="${SYSTEMCTL_TMPDIR}" mktemp )"
#tmpfile2="$( TMPDIR="${SYSTEMCTL_TMPDIR}" mktemp )"

# REACT TO OPERATING SYSTEM TYPE
case $( uname -s ) in
   Linux) : ;;
   FreeBSD) : ;;
   *) echo "${scriptfile}: 3. Indeterminate OS: $( uname -s )" 1>&2 && exit 3 ;;
esac

## REACT TO ROOT STATUS
#case ${is_root} in
#   1) # proper root
#      : ;;
#   sudo) # sudo to root
#      : ;;
#   "") # not root at all
#      #ferror "${scriptfile}: 5. Please run as root or sudo. Aborted."
#      #exit 5
#      :
#      ;;
#esac

# SET CUSTOM SCRIPT AND VALUES
#setval 1 sendsh sendopts<<EOFSENDSH     # if $1="1" then setvalout="critical-fail" on failure
#/usr/local/share/bgscripts/send.sh -hs  # setvalout maybe be "fail" otherwise
#/usr/share/bgscripts/send.sh -hs        # on success, setvalout="valid-sendsh"
#/usr/local/bin/send.sh -hs
#/usr/bin/mail -s
#EOFSENDSH
#test "${setvalout}" = "critical-fail" && ferror "${scriptfile}: 4. mailer not found. Aborted." && exit 4

# VALIDATE PARAMETERS
# objects before the dash are options, which get filled with the optvals
# to debug flags, use option DEBUG. Variables set in framework: fallopts
validateparams action - "$@"

# LEARN EX_DEBUG
test -z "${__debug_set_by_param}" && fisnum "${SYSTEMCTL_DEBUG}" && debug="${SYSTEMCTL_DEBUG}"

# CONFIRM TOTAL NUMBER OF FLAGLESSVALS IS CORRECT
#if test ${thiscount} -lt 2 ;
#then
#   ferror "${scriptfile}: 2. Fewer than 2 flaglessvals. Aborted."
#   exit 2
#fi

# LOAD CONFIG FROM SIMPLECONF
# This section follows a simple hierarchy of precedence, with first being used:
#    1. parameters and flags
#    2. environment
#    3. config file
#    4. default user config: ~/.config/script/script.conf
#    5. default config: /etc/script/script.conf
if test -f "${conffile}" ;
then
   get_conf "${conffile}"
else
   if test "${conffile}" = "${default_conffile}" || test "${conffile}" = "${defuser_conffile}" ; then : ; else test -n "${conffile}" && ferror "${scriptfile}: Ignoring conf file which is not found: ${conffile}." ; fi
fi
test -f "${defuser_conffile}" && get_conf "${defuser_conffile}"
test -f "${default_conffile}" && get_conf "${default_conffile}"

# CONFIGURE VARIABLES AFTER PARAMETERS

## START READ CONFIG FILE TEMPLATE
#oIFS="${IFS}" ; IFS="$( printf '\n' )"
#infiledata=$( ${sed} ':loop;/^\/\*/{s/.//;:ccom;s,^.[^*]*,,;/^$/n;/^\*\//{s/..//;bloop;};bccom;}' "${infile1}") #the crazy sed removes c style multiline comments
#IFS="${oIFS}" ; infilelines=$( echo "${infiledata}" | wc -l )
#{ echo "${infiledata}" ; echo "ENDOFFILE" ; } | {
#   while read line ; do
#   # the crazy sed removes leading and trailing whitespace, blank lines, and comments
#   if test ! "${line}" = "ENDOFFILE" ;
#   then
#      line=$( echo "${line}" | sed -e 's/^\s*//;s/\s*$//;/^[#$]/d;s/\s*[^\]#.*$//;' )
#      if test -n "${line}" ;
#      then
#         debuglev 8 && ferror "line=\"${line}\""
#         if echo "${line}" | grep -qiE "\[.*\]" ;
#         then
#            # new zone
#            zone=$( echo "${line}" | tr -d '[]' )
#            debuglev 7 && ferror "zone=${zone}"
#         else
#            # directive
#            varname=$( echo "${line}" | awk -F= '{print $1}' )
#            varval=$( echo "${line}" | awk -F= '{$1="" ; printf "%s", $0}' | sed 's/^ //;' )
#            debuglev 7 && ferror "${zone}${varname}=\"${varval}\""
#            # simple define variable
#            eval "${zone}${varname}=\${varval}"
#         fi
#         ## this part is untested
#         #read -p "Please type something here:" response < ${thistty}
#         #echo "${response}"
#      fi
#   else

## REACT TO BEING A CRONJOB
#if test ${is_cronjob} -eq 1 ;
#then
#   :
#else
#   :
#fi

# SET TRAPS
#trap "CTRLC" 2
#trap "CTRLZ" 18
#trap '__ec=$? ; clean_systemctl ; trap "" 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 ; exit ${__ec} ;' 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20

# DEBUG SIMPLECONF
debuglev 5 && {
   ferror "Using values"
   # used values: EX_(OPT1|OPT2|VERBOSE)
   set | grep -iE "^SYSTEMCTL_" 1>&2
}

# MAIN LOOP
#{
   printf "%s\n" "${*}" >> "${logfile}"
   #echo "thiscount=${thiscount}"
   #x=1
   #while test ${x:-${thiscount}} -le $(( thiscount - 1 )) && test ${thiscount} -gt 1 ;
   #do
   #   eval thisopt="\${opt${x}}"
   #   echo "thisopt${x}=${thisopt}"
   #   x=$(( x + 1 ))
   #done

   # actions
   actionlist=""
   case "${action}" in

      restart|start|stop|status|reload|condrestart|try-restart|reload-or-try-restart)
         # re-map a few actions
         case "${action}" in
            "reload-or-try-restart") action=restart ;;
         esac
         x=1
         while test ${x:-${thiscount}} -le $(( thiscount - 1 )) && test ${thiscount} -gt 1 ;
         do
            eval thisopt="\${opt${x}}"
            thisopt="$( echo "${thisopt}" | sed -r -e 's/\.service$//;' )"
            actionstatement="$( printf "%s" "service ${thisopt} ${action};" )"
            actionlist="${actionlist:+${actionlist} }${actionstatement}"
            x=$(( x + 1 ))
         done
         ;;

      enable|disable|mask|unmask)
         case "${action}" in
            mask) action=disable ;;
            unmask) action=enable ;;
         esac
         x=1
         while test ${x:-${thiscount}} -le $(( thiscount - 1 )) && test ${thiscount} -gt 1 ;
         do
            eval thisopt="\${opt${x}}"
            thisopt="$( echo "${thisopt}" | sed -r -e 's/\.service$//;' )"
            actionstatement="$( printf "%s" "update-rc.d ${thisopt} ${action};" )"
            actionlist="${actionlist:+${actionlist} }${actionstatement}"
            test "${SYSTEMCTL_NOW}" = "1" && {
               case "${action}" in
                  enable)
                     nowaction=start
                     ;;
                  disable)
                     nowaction=stop
                     ;;
               esac
               actionstatement="$( printf "%s" "service ${thisopt} ${nowaction:-stop};" )"
               actionlist="${actionlist:+${actionlist} }${actionstatement}"
            }
            x=$(( x + 1 ))
         done
         ;;

      daemon-reload)
         debuglev 1 && echo "${action} is a NOP."
         ;;

      list-unit-files)
         # Future improvement: can consume --full, but I do not care enough to deal with it now.
         ls -Al /etc/init.d
         ;;

      is-enabled)
         currentrunlevel="$( who -r | grep -oE 'run-level\s+[[:digit:]]+' | awk '{print $NF}' )"
         responsenumber=1

         # loop through each service on the command line
         x=1
         while test ${x:-${thiscount}} -le $(( thiscount - 1 )) && test ${thiscount} -gt 1 ;
         do
            eval thisopt="\${opt${x}}"
            thisopt="$( echo "${thisopt}" | sed -r -e 's/\.service$//;' )"
            #actionstatement="$( printf "%s" "service ${thisopt} ${action};" )"
            scriptfile="$( find "/etc/rc${currentrunlevel}.d" -mindepth 1 -maxdepth 1 -name "S??${thisopt}" 2>/dev/null )"
            responsetext="disabled"
            # if file exists, let us return 0.
            if test -n "${scriptfile}" ;
            then
               debuglev 2 && echo "${scriptfile}"
               responsenumber=0 # any "enabled" response makes systemctl return 0
               responsetext="enabled"
            fi
            echo "${responsetext:-UNKNOWN}"
            x=$(( x + 1 ))
         done
         exit "${responsenumber}"
         ;;

      is-active)
         responsenumber=3
         x=1
         while test ${x:-${thiscount}} -le $(( thiscount - 1 )) && test ${thiscount} -gt 1 ;
         do
            eval thisopt="\${opt${x}}"
            thisopt="$( echo "${thisopt}" | sed -r -e 's/\.service$//;' )"
            #actionstatement="$( printf "%s" "service ${thisopt} ${action};" )"
            servicestatus="$( service "${thisopt}" status 1>/dev/null 2>&1 ; echo "${?}" )"
            responsetext="stopped"
            # if file exists, let us return 0.
            if test ${servicestatus:-1} -eq 0 ;
            then
               responsenumber=0
               responsetext="active"
            fi
            echo "${responsetext:-unknown}"
            x=$(( x + 1 ))
         done
         exit "${responsenumber}"
         ;;

      *)
         ferror "Fatal! 2. Unable to understand action ${action}. Aborted."
         exit 2
         ;;
   esac

   # list of actions
   if test -n "${actionlist}" ;
   then
      debuglev 1 && ferror "Full list: ${actionlist}"
      printf "%s" "${actionlist}" | tr ';' '\n' | while read thisaction ;
      do
         debuglev 5 && ferror "${thisaction}"
         eval "${thisaction}"
      done
   fi
#} | tee -a ${logfile}

# EMAIL LOGFILE
#${sendsh} ${sendopts} "${server} ${scriptfile} out" ${logfile} ${interestedparties}

## STOP THE READ CONFIG FILE
#return_code 0
#fi ; done ; }
