diff options
Diffstat (limited to 'systemctl')
-rwxr-xr-x | systemctl | 528 |
1 files changed, 528 insertions, 0 deletions
diff --git a/systemctl b/systemctl new file mode 100755 index 0000000..a4d8d54 --- /dev/null +++ b/systemctl @@ -0,0 +1,528 @@ +#!/bin/sh +# Filename: systemctl +# Location: /usr/sbin/systemctl +# /usr/bin/hostnamectl +# /usr/bin/systemd-detect-virt +# Author: bgstack15@gmail.com +# Startdate: 2020-01-10 13:02:14 +# SPDX-License-Identifier: CC-BY-SA-4.0 +# Title: +# Purpose: +# Package: systemctl-service-shim +# History: +# 2020-05-14 place framework.sh contents inline so as not to depend on it. +# 2021-01-10 adapted for inclusion in devuan-sanity +# Usage: +# Should be mostly like systemctl from systemd. +# Reference: ftemplate.sh 2019-05-02a ; framework.sh 2018-05-02a +# man 1 systemctl +# Improve: +# add preset command +# Return 1 if status output is failed +# Dependencies: +# req-devuan: moreutils +# Documentation: +# Be aware that real systemd systemctl is file /bin/systemctl but +# this systemdtl is file /usr/sbin/systemctl to prevent a recursive loop +# in some service scripts that look for /bin/systemctl +# vim: set sw=3 sts=3 ts=3 et: +fiversion="2019-05-02a" +systemctlversion="2021-06-11a" + +usage() { + ${PAGER:-/usr/bin/less -F} >&2 <<ENDUSAGE +usage: systemctl [-duV] [-c conffile] +Provides a systemctl-like interface to sysvinit. Simulates 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 +log_to_file() { + flecho "${@}" >> "${logfile}" +} + +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 ;; + "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}" ; } +} + +# INITIALIZE VARIABLES +# variables set in framework: +# server scriptdir scriptfile scripttrim + +### BEGIN IMPORT OF FRAMEWORK.SH +fversion="2020-04-24x" + +# DEFINE FUNCTIONS + +isflag() { + # input: $1=word to parse + case "$1" in + --*) retval=2 ;; + -*) retval=1 ;; + *) retval=0 ;; + esac + echo $retval +} + +parseParam() { + # determines if --longname or -shortflagS that need individual parsing + trimParam=$( printf '%s' "${param}" | sed -n 's/--//p' ) + _rest= + if test -n "$trimParam" ; + then + parseFlag $trimParam + else + #splitShortStrings + _i=2 + while test ${_i} -le ${#param} ; + do + _j=$( expr ${_i} + 1) + #_char=$(expr substr "$param" $_i 1) + #_rest=$(expr substr "$param" $_j 255) + _char=$( printf '%s' "${param}" | cut -c ${_i}) + _rest=$( printf '%s' "${param}" | cut -c ${_j}-255) + parseFlag $_char + _i=$( expr ${_i} + 1) + done + fi +} + +getval() { + tempval= + if test -n "${_rest}" ; + then + tempval="${_rest}" + hasval=1 + _i=255 # skip rest of splitShortStrings because found the value! + elif test -n "$nextparam" && test $(isflag "$nextparam") -eq 0 ; + then + tempval="$nextparam" + hasval=1 #DNE ; is affected by ftemplate! + paramnum=$nextparamnum + fi +} + +debuglev() { + # call: debuglev 5 && ferror "debug level is at least a five!" + # added 2015-11-17 + localdebug=0 ; localcheck=0 ; + fisnum ${debug} && localdebug=${debug} + fisnum ${1} && localcheck=${1} + test $localdebug -ge $localcheck && return 0 || return 1 +} + +debuglevoutput() { + # call: commandthatgeneratesstdout | debuglevoutput 8 + # output: output to standard error prepended with "debug8: " the contents of the pipe + ___dlo_threshold="${1}" + ___dlo_silent="${2}" + if debuglev "${___dlo_threshold}" ; + then + if test -n "${___dlo_silent}" ; + then + cat 1>&2 + else + sed -r -e "s/^/debug${___dlo_threshold}: /;" 1>&2 + fi + else + cat 1>/dev/null 2>&1 + fi +} + +fisnum() { + # call: fisnum $1 && debug=$1 || debug=10 + fisnum= ; + case $1 in + ''|*[!0-9]*) fisnum=1 ;; # invalid + *) fisnum=0 ;; # valid number + esac + return ${fisnum} +} + +fistruthy() { + # call: if fistruthy "$val" ; then + local _return= + case "$( echo "${1}" | tr '[:upper:]' '[:lower:]' )" in + yes|1|y|true|always) _return=true ;; + esac + test -n "${_return}" ; return $? +} + +setval() { + # call: setval 0 value1 value2 value3 ... <<EOFOPTIONS + # /bin/foo1 --optforfoo1 + # /usr/bin/foo2 --optforfoo2 + # EOFOPTIONS + # ^ 0 = soft fail, 1 = critical-fail + quitonfail="${1}" ; shift + _vars="${@}" + #echo "_vars=${_vars}" + _varcount=0 + for _word in ${_vars} ; do _varcount=$( expr $_varcount + 1 ) ; eval "_var${_varcount}=${_word}" ; done + _usethis=0 + while read line ; + do + _varcount=0 + if test ! "${_usethis}x" = "0x" ; then break ; fi + #echo "line=${line}" ; + for _word in ${line} ; + do + _varcount=$( expr $_varcount + 1 ) + #echo "word ${_varcount}=${_word}" ; + case "${_varcount}" in + 1) + #echo "Testing for existence of file ${_word}" + if test -f "${_word}" ; + then + _usethis=1 + #echo "${_var1}=${_word}" + eval "${_var1}=${_word}" + fi + ;; + *) + #echo "just an option: ${_word}" + if test "${_usethis}x" = "1x" ; + then + #eval echo "\${_var${_varcount}}=${_word}" + eval eval "\${_var${_varcount}}=${_word}" + fi + ;; + esac + done + done + #eval echo "testfile=\$${_var1}" + eval _testfile=\$${_var1} + if test ! -f "${_testfile}" ; + then + case "${quitonfail}" in 1) _failval="critical-fail" ;; *) _failval="fail" ;; esac + eval "${_var1}=${_failval}" + setvalout=${_failval} + else + eval setvalout="valid-${_var1}" + fi +} + +flecho() { + # requires moreutils + if echo "${@}" | grep -qiE '.' ; + then + printf "%s\n" "${@}" | TZ=UTC ts "[%FT%TZ]${USER}@${server}:" + else + printf '' | TZ=UTC ts "[%FT%TZ]${USER}@${server}" + fi +} + +ferror() { + # call: ferror "$scriptfile: 2. Something bad happened-- error message 2." + echo "$@" 1>&2 +} + +setdebug() { + # call: setdebug + debug=10 + getval + if test $hasval -eq 1 ; + then + if fisnum ${tempval} ; + then + debug=${tempval} + else + #test paramnum -le paramcount && paramnum=$( expr ${paramnum} - 1 ) + hasval=0 + fi + elif fisnum ${_rest} ; + then + debug=${_rest} + _i=255 + else + test $paramnum -le $paramcount && test -z ${nextparam} && paramnum=$( expr ${paramnum} - 1 ) + fi +} + +define_if_new() { + # call: define_if_new IFW_IN_LOG_FILE "/var/log/messages" + eval thisval="\${${1}}" + test -z "${thisval}" && eval "$1"=\"$2\" +} + +# INITIALIZE VARIABLES +server=$( hostname -s ) +thisos="$( uname -s )" +# get thisflavor and thisflavorversion. Examples: centos, ubuntu, redhat +if test -f /etc/os-release ; +then + eval thisflavor=$( grep -iE "^\s*ID=" /etc/os-release 2>/dev/null | sed 's/^.*=//;' | tr 'A-Z' 'a-z' ) + eval thisflavorversion=$( grep -iE "^\s*PRETTY_NAME=" /etc/os-release 2>/dev/null | sed -e 's/^.*=//;' | tr -dc '0-9.' ) +elif test -f /etc/system-release && test $( wc -l < /etc/system-release 2>/dev/null ) -eq 1 ; +then + eval thisflavor=$( awk '{print $1}' < /etc/system-release 2>/dev/null | tr 'A-Z' 'a-z' ) + eval thisflavorversion=$( </etc/system-release sed -e 's/^.*=//;' 2>/dev/null | tr -dc '0-9.' ) +else + if test "${thisos}" = "FreeBSD" ; then + thisflavor="$( uname -i )" ; thisflavorversion="$( uname -r )" ; + else + thisflavor="other" + thisflavorversion="unknown" + fi +fi +case "${thisos}" in FreeBSD) sed=gsed ;; *) sed=sed ;; esac + +# if framework is dot sourced then $0 will be "-bash" and screw things up +case ${0} in + "-bash") + scriptdir="$( pwd )" + scriptfile="dot-sourced" ;; + *) + scriptdir="$( cd $( dirname ${0} ) ; pwd )" + scriptfile="$( basename ${0} | sed 's!/./!/!g;s!\./!!g' )" + scripttrim="${scriptfile%%.sh}" + ;; +esac + +# SPECIAL RUNTIME-RELATED VARIABLES +{ test "$USER" = "root" || test "$( stat -c '%u' /proc/$$/exe 2>/dev/null )" = 0 ; } && is_root=1 +test -n "$SUDO_USER" && is_root="sudo" + +nullflagcount=0 +validateparams() { + # VALIDATE PARAMETERS + # scroll through all parameters and check for isflag. + # if isflag, get all flags listed. Also grab param#. + paramcount=$# + thiscount=0 ;thisopt=0 ;freeopt=0 ; + varsyet=0 + paramnum=0 + debug=0 + fallopts= + while test $paramnum -lt $paramcount ; + do + paramnum=$( expr ${paramnum} + 1 ) + eval param=\${$paramnum} + nextparamnum=$( expr ${paramnum} + 1 ) + eval nextparam=\${$nextparamnum} + case $param in + "-") + if test "$varsyet" = "0" ; + then + # first instance marks beginning of flags and parameters. + #Until then it was the names of variables to fill. + varsyet=1 + else + nullflagcount=$( expr ${nullflagcount} + 1 ) #useful for separating flags from something else? + debuglev 10 && ferror "null flag!" # second instance is null flag. + fi + ;; + esac + if test -n "$param" ; + then + # parameter $param exists. + if test $(isflag $param) -gt 0 ; + then + # IS FLAG + parseParam + else + # IS VALUE + if test "$varsyet" = "0" ; + then + thisopt=$( expr ${thisopt} + 1 ) + test "${param}" = "DEBUG" && debug=10 && thisopt=$( expr ${thisopt} - 1 ) || \ + eval "varname${thisopt}=${param}" + #varname[${thisopt}]="${param}" + debuglev 10 && ferror "var \"${param}\" named" + else + thiscount=$( expr ${thiscount} + 1 ) + test $thiscount -gt $thisopt && freeopt=$( expr ${freeopt} + 1 ) + #eval ${varname[${thiscount}]:-opt${freeopt}}="\"${param}\"" + eval "thisvarname=\${varname${thiscount}}" + test -z "${thisvarname}" && eval "thisvarname=opt${freeopt}" + eval "${thisvarname}=\"${param}\"" + eval fallopts=\"${fallopts} ${param}\" + debuglev 10 && ferror "${thisvarname} value: ${param}" + fi + fi + fi + done + fallopts="${fallopts# }" + if debuglev 10 ; + then + ferror "thiscount=$thiscount" + ferror "fallopts=$fallopts" + ferror "Framework $fversion" + ferror "Finput $fiversion" + fi +} +### END IMPORT OF FRAMEWORK.SH + +define_if_new logfile "/var/log/systemctl.log" + +# 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}" +debug=10 + +# CONFIGURE VARIABLES AFTER PARAMETERS + +_trimmed="$( echo "${@}" | tr -d '\r\n' )" +log_to_file "${0} ${_trimmed}" +case "${0}" in + *hostnamectl|*systemd-detect-virt) exit 0 ;; # always just short-circuit +esac + +# MAIN LOOP + +# 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 + log_to_file "ACTION: ${thisaction}" + debuglev 5 && ferror "${thisaction}" + eval "${thisaction}" + done +fi + +# exit cleanly +: |