Initial commit.

This commit is contained in:
Sergey Fedosov
2016-09-03 22:35:07 +03:00
commit 9177854a27
12 changed files with 1057 additions and 0 deletions

13
LICENSE Normal file
View File

@@ -0,0 +1,13 @@
Copyright (c) 2012-2014, Jack L. Frost <fbt@fleshless.org>
Permission to use, copy, modify, and/or distribute this software for any purpose with or without
fee is hereby granted, provided that the above copyright notice and this permission notice appear
in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
OF THIS SOFTWARE.

68
README.md Normal file
View File

@@ -0,0 +1,68 @@
watchman
========
Yet another service manager.
Installation
------------
### Arch Linux:
#### Latest PKGBUILDS:
* https://aur4.archlinux.org/packages/watchman-sm
* https://pkg.fleshless.org/watchman-sm/tree
#### Manual:
##### Linux:
git clone https://github.com/fbt/watchman.git
cd watchman
./make.sh
./make.sh install
##### FreeBSD:
FreeBSD doesn't have /run as a tmpfs (or anything), so you would need to tell watchman where to keep runtime files
git clone https://github.com/fbt/watchman.git
cd watchman
RUNDIR=/var/run/watchman ./make.sh
./make.sh install
##### Others:
I'e not personally tested watchman on any other systems. Someone told me it works on Solaris and OpenBSD. Test it and get in touch with me if you want.
Services
--------
A service is a script in the watchman's init.d directory.
By default it's /etc/watchman/init.d for system services and $HOME/.watchman/init.d for user ones.
Watchman creates relevant dirs if they don't exist.
Variables
---------
There are some variables you can use to alter a service's behaviour.
All of them are optional. Yes, I mean all of them.
$service_command # The service's executable. Watchman will try to find the service in $PATH by the service name, so yes,
even this is optional.
$service_args # Arguments to $service_command
$service_respawn # Set this to restart the service if it exists. Only works with services that don't daemonize.
$service_name # It is useful to set $service_name explicitly if you use a symlink to refer to a service and want to use
both the original script and the symlink.
$service_workdir # The directory to cd to before running $service_command
$service_pidfile # The service's pid file. Will not be overwritten by watchman. By specifying this, you basically tell
watchman that the service mantains its pidfile itself.
$service_logfile # The file with $service_command's stdout/stderr.
$service_actions # Array. Available service actions. Should not be overriden, only added to. If you need to remove
an action, just do unset $action.
Functions
---------
Standart functions:
start() # Start the service. Calls watchman.start().
stop() # Stop the service. Calls watchman.stop().
restart() # Restart the service. Calls stop(), then start().
reload() # Reload the service's configuration. Calls watchman.reload, which sends SIGHUP to the service.
status() # Show the service's status. Calls watchman.status().
depends() # Starts the specified services. Calls watchman.depends.
logs() # Shows the service stdout log. Calls watchman.logs.

15
bash/completion/watchman Normal file
View File

@@ -0,0 +1,15 @@
_service_module() {
local cur
COMPREPLY=()
cur=${COMP_WORDS[COMP_CWORD]}
if [[ "$UID" == '0' ]]; then
IFS=$'\n' words=( /etc/rc.d/* )
else
IFS=$'\n' words=( $HOME/.watchman/init.d/* )
fi
COMPREPLY=( $(compgen -W "${words[*]##*/}" -- "$cur") )
}
complete -F _service_module service watchman

22
config.mk.sh Normal file
View File

@@ -0,0 +1,22 @@
# build.sh config
VERSION=v1.11.2
PREFIX=${PREFIX:-/opt}
USR=${USR:-"${PREFIX}usr"}
BINDIR=${BINDIR:-"${DESTDIR}${USR}/bin"}
CFGDIR=${CFGDIR:-"${PREFIX}etc/watchman"}
LOGDIR=${LOGDIR:-"/share/MD0_DATA/Public/logs/watchman"}
RUNDIR=${RUNDIR:-"${PREFIX}run/watchman"}
CFGDIR_USER=${CFGDIR_USER:-"\$XDG_CONFIG_HOME/watchman"}
LOGDIR_USER=${LOGDIR_USER:-"\$HOME/.local/var/log/watchman"}
RUNDIR_USER=${RUNDIR_USER:-"\$XDG_RUNTIME_DIR/watchman/run"}
INITDIRS="\"${CFGDIR}/init.d\" \"${CFGDIR_USER}/init.d\""
TOOLS=${TOOLS-"runas service svc"}
BASH_PATH=${BASH_PATH:-"/bin/bash"}

106
make.sh Executable file
View File

@@ -0,0 +1,106 @@
#!/bin/sh
set -e
# Functions
set_vars() {
printf 'Setting vars...\n' >&2
sed -r \
-e "s%@VERSION@%${VERSION}%" \
-e "s%@CFGDIR_USER@%${CFGDIR_USER}%" \
-e "s%@RUNDIR_USER@%${RUNDIR_USER}%" \
-e "s%@LOGDIR_USER@%${LOGDIR_USER}%" \
-e "s%@CFGDIR@%${CFGDIR}%" \
-e "s%@RUNDIR@%${RUNDIR}%" \
-e "s%@LOGDIR@%${LOGDIR}%" \
-e "s%@INITDIRS@%${INITDIRS}%" \
-e "s%@BASH_PATH@%${BASH_PATH}%"
}
do_tell() {
printf 'Executing: %s\n' "$*"
"$@"
}
# Targets
target_watchman() {
target='watchman'
printf '%s\n' "Building watchman..."
set_vars < "$target".bash > "$target"
do_tell chmod 755 "$target"
ls -l "$target"
}
target_tools() {
if [ "$TOOLS" ]; then
do_tell mkdir -p tools
for i in $TOOLS; do
printf '%s\n' "Building ${i}..."
set_vars < "watchman-${i}.bash" > "tools/watchman-$i"
do_tell chmod 755 "tools/watchman-$i"
done
ls -l tools/*
fi
}
target_clean() {
do_tell rm -f tools/* watchman build
if [ -d tools ]; then
do_tell rmdir tools
fi
}
target_install() {
mkdir -p "$BINDIR"
do_tell cp watchman "${BINDIR}/watchman"
do_tell chmod 755 "${BINDIR}/watchman"
for i in $TOOLS; do
do_tell cp "tools/watchman-$i" "$BINDIR/watchman-$i"
do_tell chmod 755 "$BINDIR/watchman-$i"
done
}
target_all() {
target_watchman
target_tools
}
gitver() {
if last_tag=$( git describe --long --tags 2>/dev/null ); then
printf '%s\n' "$last_tag" | sed 's/\([^-]*-g\)/r\1/;s/-/./g'
else
printf "git-r%s.%s" "$(git rev-list --count HEAD)" "$(git rev-parse --short HEAD)"
fi
}
main() {
while [ -n "$1" ]; do
case "$1" in
*=*) export "${1%%=*}=${1#*=}";;
--) shift; break;;
*) break;;
esac
shift
done
target="${1:-all}"
# Build configuration
. './config.mk.sh'
printf '%s\n' "Building target: $target"
if [ -d .git ]; then
VERSION=$(gitver)
printf '%s\n' "Building from git, overriding version: $VERSION"
fi
"target_$target"
}
main "$@"

14
services/init.d/cronpl Executable file
View File

@@ -0,0 +1,14 @@
#!/opt/bin/watchman
# named init script for watchman
ROOTDIR='/share/MD0_DATA/.qpkg/Optware'
service_command="${ROOTDIR}/etc/cron.pl"
service_respawn=true
service_logfile='/share/Public/logs/cron.pl.log'
unset reload status
start() {
while [ ! -x /opt/bin/perl ]; do sleep 1; done
watchman.start
}

18
services/init.d/named Executable file
View File

@@ -0,0 +1,18 @@
#!/opt/bin/watchman
# named init script for watchman
ROOTDIR='/share/MD0_DATA/.qpkg/Optware'
service_command="${ROOTDIR}/etc/named/named.fucked"
service_respawn=true
service_logfile='/share/Public/logs/named/named.log'
unset reload
start() {
while [ ! -x /opt/sbin/named ]; do sleep 1; done
watchman.start
}
status() {
/opt/sbin/rndc -s 127.0.0.1 status
}

24
services/init.d/openvpn Executable file
View File

@@ -0,0 +1,24 @@
#!/opt/bin/watchman
# openvpn init script for watchman
ROOTDIR='/share/MD0_DATA/.qpkg/Optware'
service_command="/usr/sbin/openvpn"
service_respawn=true
service_args=(${ROOTDIR}/etc/openvpn/dia/dia.ovpn)
#service_pidfile="${ROOTDIR}/var/run/openvpndia.pid"
service_logfile='/share/Public/logs/openvpn/openvpndia.log'
unset reload
start(){
while [ ! -e /lib/modules/others/tun.ko ]; do sleep 1; done
while [ ! -x /usr/sbin/openvpn ]; do sleep 1; done
while [ ! -e ${ROOTDIR}/etc/openvpn/dia/dia.ovpn ]; do sleep 1; done
while [ $(lsmod|grep tun|wc -l) != 1 ]; do insmod /lib/modules/others/tun.ko; sleep 1; done
watchman.start
}
status() {
kill -USR2 $(< $service_pidfile)
}

45
watchman-runas.bash Normal file
View File

@@ -0,0 +1,45 @@
#!@BASH_PATH@
watchman_version='@VERSION@'
err() { printf '%s\n' "$1" >&2; }
runas::main() {
local job_username
while [[ "$1" ]]; do
case "$1" in
(-u) job_username="$2"; shift;;
(-h) runas::usage;;
(-v)
printf '%s\n' "$watchman_version"
exit 0
;;
(-*)
err "Unknown option: $1"
runas::usage
return 1
;;
(*) break;;
esac
shift
done
[[ "$UID" -eq 0 ]] || {
err "Only root is allowed to runas!"
return 1
}
(( $# )) || {
err "No command supplied"
runas::usage
return 1
}
exec su "$job_username" -s "$BASH" -c "$*"
}
runas::main "$@"

29
watchman-service.bash Normal file
View File

@@ -0,0 +1,29 @@
#!@BASH_PATH@
watchman_version='@VERSION@'
# Check if XDG_ stuff is set
XDG_RUNTIME_DIR=${XDG_RUNTIME_DIR:-"/run/user/$UID"}
XDG_CONFIG_HOME=${XDG_CONFIG_HOME:-"$HOME/.config"}
[[ "$1" == '-v' ]] && {
printf '%s\n' "$watchman_version"
exit 0
}
cfg_init_dirs=( @INITDIRS@ )
service="$1"; shift
for i in "${cfg_init_dirs[@]}"; do
[[ -f "${i}/${service}" ]] && {
service_script="${i}/${service}"
}
done
[[ "$service_script" ]] || {
printf '%s\n' "Service $service not found."
exit 1
}
"$service_script" "$@"

150
watchman-svc.bash Normal file
View File

@@ -0,0 +1,150 @@
#!@BASH_PATH@
watchman_version='@VERSION@'
msg() { printf '%s\n' "$*"; }
err() { printf '%s\n' "$*" >&2; }
usage() {
while read; do msg "$REPLY"; done <<- 'EOF'
Usage: watchman-svc <-p pidfile> [options] command
Flags:
-p <pidfile>
-l <logfile>
-r # Respawn the process if it dies.
-h # Show this message.
-v # Show version.
EOF
}
cleanup() {
for i in "$respawn_pid" "$job_pid"; do
kill_wait "$i" || {
err "Failed to stop child: $i"
exit 13
}
done
rm -f "$job_pidfile"
}
respawn_cleanup() {
kill_wait "$job_pid" || {
err "Failed to stop child: $i"
exit 13
}
return 0
}
respawn_passthrough() {
kill -"$1" "$job_pid"
}
kill_wait() {
kill "$1" &>/dev/null
counter=0
while kill -0 "$1" &>/dev/null; do
sleep 1
(( counter >= 10 )) && { return 1; }
(( counter++ ))
done
return 0
}
job_run() {
"${cmd[@]}" >&3 2>&3 & job_pid=$!
export job_pid
return 0
}
job_respawn() {
trap 'respawn_cleanup; return' INT TERM
trap 'respawn_passthrough 1' HUP
trap 'respawn_passthrough 10' USR1
trap 'respawn_passthrough 12' USR2
while job_run; do
last_run=$SECONDS
if kill -0 "$job_pid"; then
wait "$job_pid"
fi
curtime=$SECONDS
if (( last_run + 1 > ( curtime - 2 ) )); then
err "The job exists right as we start it. Bailing."
return 1
fi
done
}
job_wait() {
trap cleanup INT TERM
if (( flag_respawn )); then
job_respawn & respawn_pid=$!
printf '%s' "$respawn_pid" > "$job_pidfile"
wait "$respawn_pid"
else
job_run
printf '%s' "$job_pid" > "$job_pidfile"
wait "$job_pid"
fi
cleanup
}
main() {
while (( $# )); do
case "$1" in
(-h) usage; return 0;;
(-p) job_pidfile="$2"; shift;;
(-l) job_logfile="$2"; shift;;
(-r) flag_respawn=1;;
(-v)
printf '%s\n' "$watchman_version"
exit 0
;;
(--) shift; break;;
(-*)
err "Unknown key: $1"
usage
return 1
;;
(*) break;;
esac
shift
done
if [[ -z "$job_pidfile" ]]; then
usage
return 1
fi
cmd=( "$@" )
if [[ "$job_logfile" ]]; then
exec 3>"$job_logfile"
else
exec 3>&1
fi
job_wait &
return 0
}
main "$@"

553
watchman.bash Normal file
View File

@@ -0,0 +1,553 @@
#!@BASH_PATH@
# Config
watchman_version='@VERSION@'
# Functions
watchman.usage() {
while read -r; do printf '%s\n' "$REPLY"; done <<- EOF
Usage: watchman [flags] <service> <action>
Flags:
-h, --help Show this message.
-v, --version Show watchman's version.
-d, --verbose Verbose output.
EOF
}
watchman.msg() { printf '%s\n' "[watchman] $1"; }
watchman.err() { watchman.msg "(error) $1"; }
watchman.debug() {
[[ "$flag_verbose" ]] && {
watchman.msg "{v} $1"
}
}
watchman.init() {
# Check if XDG_ stuff is set
XDG_RUNTIME_DIR=${XDG_RUNTIME_DIR:-"/run/user/$UID"}
XDG_CONFIG_HOME=${XDG_CONFIG_HOME:-"$HOME/.config"}
# Check if the user is root.
if (( UID )); then
cfg_dir="@CFGDIR_USER@"
cfg_rundir="@RUNDIR_USER@"
cfg_logdir="@LOGDIR_USER@"
else
cfg_dir="@CFGDIR@"
cfg_rundir="@RUNDIR@"
cfg_logdir="@LOGDIR@"
fi
[[ -f "${cfg_dir}/watchman.conf" ]] && {
source "${cfg_dir}/watchman.conf" || {
watchman.err "Failed to source config: ${cfg_dir}/watchman.conf. Fix it to proceed."
return 1
}
}
cfg_initdir="${cfg_dir}/init.d"
cfg_init_dirs+=( "$cfg_dir"/{init.d,conf.d} "$cfg_rundir" "$cfg_logdir" )
for i in "${cfg_init_dirs[@]}"; do
[[ -e "$i" ]] || {
watchman.debug "$i does not exist, creating..."
mkdir -p "$i" || {
watchman.err "Can't create $i!"
return 1
}
}
done
return 0
}
watchman.done() {
>"$cfg_rundir/${service_name}.done" || {
watchman.msg "Warning! Can't mark ${service_name} as enabled."
}
}
watchman.spawn() {
[[ "$service_command" ]] || {
watchman.err "Please specify what to run with \$service_command"
}
if [[ "$service_respawn" == 'true' ]]; then
[[ "$service_type" == 'oneshot' ]] && {
watchman.err "A oneshot service can not be respawned. Choose one or the other."
return 1
}
if [[ "${service_pidfile}" ]]; then
watchman.err "Respawning forking services is not supported!"
return 1
fi
watchman-svc -p "$service_pidfile_int" -l "$service_logfile" -r "$service_command" "${service_args[@]}"
else
if [[ "$service_type" == 'oneshot' ]]; then
"${service_command}" "${service_args[@]}" 1>>"$service_logfile" 2>>"$service_logfile" || { return 1; }
else
watchman-svc -p "$service_pidfile_int" -l "$service_logfile" "$service_command" "${service_args[@]}"
fi
fi
}
watchman.tempfiles() {
# Create temporary files
for f in "${service_tmpfiles[@]}"; do
IFS=':' read -r f_path f_type f_args <<< "$f"
if ! [[ -e $f_path ]]; then
case "$f_type" in
symlink)
f_target=$f_args
ln -s "$f_target" "$f_path"
;;
file|dir)
IFS=':' read -r f_perms f_owner f_group <<< "$f_args"
if [[ $f_type == 'file' ]]; then
> "$f_path"
chmod "${f_perms:-644}" "$f_path"
else
mkdir -p -m "${f_perms:-755}" "$f_path"
fi
if [[ "$f_owner" || "$f_group" ]]; then
chown "${f_owner:-root}:${f_group:-root}" "$f_path"
fi
;;
esac
fi
done
}
watchman.start() {
local try
watchman.status && {
if [[ "$service_type" == 'oneshot' ]]; then
watchman.err "$service_name is already enabled"
else
watchman.err "$service_name is already running"
fi
return 0
}
# Start dependencies
for d in "${service_depends[@]}"; do
IFS=':' read -r d_name d_type <<< "$d"
"$0" "$d_name" start || {
err "Failed to start service dependency: $d_name"
if ! [[ $d_type == 'soft' ]]; then
return 1
fi
}
done
# pre-start
run_if_function pre_start || { return $?; }
[[ "$service_workdir" ]] && { cd "$service_workdir"; }
>"$service_logfile" 2>/dev/null || {
watchman.msg "Warning! Logfile is not writeable! Defaulting to /dev/null"
service_logfile='/dev/null'
}
watchman.tempfiles
spawn 1>>"$service_logfile" 2>>"$service_logfile"
spawn_result="$?"
case "$service_type" in
(oneshot)
(( spawn_result )) && {
watchman.err "Failed to enable ${service_name}!"
return 1
}
watchman.done
watchman.msg "$service_name enabled"
;;
(*)
if [[ "$service_pidfile" ]]; then
if [[ ! -f "$service_pidfile" ]]; then
watchman.msg "Making sure the process had time to wite its pidfile..."
try=0; while [[ ! -f "$service_pidfile" && "$try" -lt "${service_spawn_delay:-3}" ]]; do
((try++))
sleep 1
done
fi
if ! [[ -f "$service_pidfile" ]]; then
return 3
fi
fi
watchman.msg "$service_name started"
run_if_function post_start || { return $?; }
;;
esac
}
watchman.depends() {
[[ "$action" == 'start' ]] || { return 0; }
for s in "$@"; do
"$0" "$s" status || {
watchman.msg "Starting ${s} as ${service_name} depends on it."
"$0" "$s" start || {
watchman.err "Starting $s failed, aborting."
return 31
}
}
done
return 0
}
watchman.pid_check() { kill -0 "$1" 2>/dev/null; }
watchman.pid_wait() {
counter='0'
printf "[watchman] Waiting for PID to exit: %s..." "$1"
while watchman.pid_check "$1"; do
((counter++))
[[ "$counter" -ge "${service_stop_timeout:-30}" ]] && {
printf 'timeout\n'
return 1
}
sleep 1
printf '.'
done
printf '\n'
return 0
}
watchman.stop() {
watchman.status || {
watchman.err "$service_name doesn't seem to be running"
return 1
}
run_if_function pre_stop || { return $?; }
case "$service_type" in
(oneshot)
watchman.msg "Removing $cfg_rundir/${service_name}.done file..."
rm "$cfg_rundir/${service_name}.done"
;;
(*)
kill "$service_pid"
watchman.pid_wait "$service_pid" || { return 3; }
rm -f "$service_pidfile" || {
watchman.err "Failed to remove pidfile: $service_pidfile"
}
;;
esac
run_if_function post_stop || { return $?; }
}
watchman.status() {
case "$service_type" in
(oneshot)
if [[ -e "$cfg_rundir/${service_name}.done" ]]; then
return 0
fi
;;
(*)
if [[ "$service_pid" ]]; then
if kill -0 "$service_pid" &>/dev/null; then
return 0
fi
fi
;;
esac
return 1
}
watchman.service_usage() {
local valid_actions
for i in "${service_actions[@]}"; do
if is_function "$i"; then
valid_actions+="$i|";
fi
done
watchman.msg "Usage: $service_name <${valid_actions%|*}>"
}
watchman.service_get_pid() {
local _pid
if [[ "$service_pidfile" ]]; then
if [[ -f "$service_pidfile" ]]; then
_pid=$(<"$service_pidfile")
fi
else
if [[ -f "$service_pidfile_int" ]]; then
_pid=$(<"$service_pidfile_int")
fi
fi
printf '%s' "$_pid"
}
watchman.reload() {
watchman.status || {
watchman.err "$service_name is not running"
return 1
}
watchman.msg "Reloading ${service_name}..."
kill -1 "$service_pid"
}
watchman.logs() {
if [[ -f "$service_logfile" ]]; then
watchman.msg "Output for ${service_name}:"
while read -r; do
printf '%s\n' "$REPLY"
done < "$service_logfile"
else
watchman.err "No log available for ${service_name}"
fi
if [[ "$service_respawn" == 'true' ]]; then
if [[ -f "$cfg_logdir/${service_name}_respawn.log" ]]; then
watchman.msg "Output for ${service_name} (watchdog):"
while read -r; do
printf '%s\n' "$REPLY"
done < "$cfg_logdir/${service_name}_respawn.log"
fi
fi
}
watchman.edit() {
if ! [[ "$EDITOR" ]]; then
EDITOR='vim'
fi
"$EDITOR" "$service_script"
}
watchman.args() {
while [[ "$1" ]]; do
case "$1" in
(-h) watchman.usage; exit;;
(-d) flag_verbose='1';;
(-v) printf '%s\n' "$watchman_version"; exit;;
(--) shift; break;;
(-*)
watchman.err "Invalid key: $1"
watchman.usage
return 1
;;
(*) args+=( "$1" );;
esac; shift
done
}
nullerr() {
"$@" 2>/dev/null
}
is_variable() {
declare param_args param=$1
read -r _ param_args _ <<< $( nullerr declare -p "$param" )
if (( $? )); then
return 1
fi
if [[ "$param_args" == '--' ]]; then
return 0
fi
return 1
}
is_function() {
declare word=$1 word_type
word_type=$( type -t "$word" )
if [[ $word_type == 'function' ]]; then
return 0
fi
return 1
}
run_if_function() {
declare func=$1; shift
if is_function "$func"; then
"$func" "$@"
fi
return 0
}
spawn() { watchman.spawn; }
start() { watchman.start; }
stop() { watchman.stop; }
restart() { stop; start; }
reload() { watchman.reload; }
edit() { watchman.edit; }
status() {
local service_status
watchman.status && {
service_status='running'
}
if [[ "$service_status" ]]; then
[[ "$service_type" == 'oneshot' ]] && {
watchman.msg "$service_name is enabled"
} || {
watchman.msg "$service_name is running ($service_pid)"
}
return 0
else
watchman.msg "$service_name is down"
fi
return 1
}
logs() { watchman.logs; }
depends() { watchman.depends "$@"; }
watchman.main() {
# Parse arguments
watchman.args "$@" || { return "$?"; }
set -- "${args[@]}"
watchman.init "$@" || { return "$?"; }
# Exit on no arguments
(( $# )) || {
watchman.usage
return 9
}
# Set default service actions.
service_actions=( 'start' 'stop' 'restart' 'reload' 'status' )
# Set $action and exit immediately if it's not set.
action="$2"
[[ "$action" ]] || {
watchman.err 'Syntax error: no action specified.'
watchman.service_usage
return 1
}
# Basename for poor people.
service_name="${1##*/}"
# Check if the first argument is an existing file,
# set $service_script to it if it is. Otherwise, search
# for the service in the initdir.
if [[ -f "$1" ]]; then
service_script="$1"
else
if [[ -f "${cfg_initdir}/${service_name}" ]]; then
service_script="${cfg_initdir}/${service_name}"
else
watchman.err "$service_name doesn't exist in $cfg_initdir"
return 1
fi
fi
# Source the script
source "$service_script" || { return 13; }
# check if the command exists and is an executable file.
[[ "$service_command" ]] && {
[[ -e "${service_command}" ]] || {
watchman.err "Service command for ${service_name} is set but does not exist: ${service_command}"
return 1
}
[[ -f "${service_command}" ]] || {
watchman.err "Service command for ${service_name} is set but is not a file: ${service_command}"
return 1
}
[[ -x "${service_command}" ]] || {
watchman.err "Service command for ${service_name} is set but is not executable: ${service_command}"
return 1
}
}
# Source the additional service configuration if that exists.
[[ -f "${cfg_dir}/conf.d/${service_name}.sh" ]] && {
source "${cfg_dir}/conf.d/${service_name}.sh" || {
return $?
}
}
# If $service_args is a variable, convert it into an array
if is_variable service_args; then
watchman.err "\$service_args for the $service_name service is a variable."
watchman.err "Since watchman 1.11.1, \$service_args is supposed to be an array."
watchman.err "Please fix it in the service definition."
read -r -a service_args <<< "$service_args"
fi
# Set default service_logfile and internal pidfile
# These need to be here so that services behave as expected if $service_name is explicitly set.
[[ "$service_logfile" ]] || { service_logfile="$cfg_logdir/${service_name}.log"; }
service_pidfile_int="$cfg_rundir/${service_name}.pid" # Does not need to be reassignable. Seriously. Don't.
# Try to get the service's pid.
service_pid="$(watchman.service_get_pid)"
# If the action is defined, do it. If not, fail gracefully.
if is_function "$action"; then
"$action"
action_rcode="$?"
(( action_rcode > 1 )) && {
watchman.err "Function $action failed for ${service_name}."
}
return "$action_rcode"
else
watchman.err "Function $action is not defined for ${service_name}."
return 17
fi
}
# Main
watchman.main "$@"