218 lines
4.8 KiB
Bash
Executable File
218 lines
4.8 KiB
Bash
Executable File
#!/bin/bash
|
|
# Daemon for accepting TAME commands (compilers, linker, etc)
|
|
#
|
|
# Copyright (C) 2018 R-T Specialty, LLC.
|
|
#
|
|
# This program is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
##
|
|
|
|
set -euo pipefail
|
|
|
|
declare -r mypath=$( dirname "$( readlink -f "$0" )" )
|
|
|
|
declare -ri EX_RUNNING=1
|
|
declare -ri EX_USAGE=64 # incorrect usage; sysexits.h
|
|
declare -ri EX_CANTCREAT=73 # cannot create file; sysexits.h
|
|
|
|
# set by `main', global for `cleanup'
|
|
declare root=
|
|
|
|
|
|
# Create FIFOs for runner
|
|
#
|
|
# The FIFOs are intended to be attached to stderr and stdout
|
|
# of the runner and will be created relative to the given
|
|
# root path ROOT.
|
|
#
|
|
# If a FIFO cannot be created, exit with EX_CANTCREAT.
|
|
mkfifos()
|
|
{
|
|
local -r root="${1?Missing root path}"
|
|
|
|
mkdir -p "$root"
|
|
|
|
# note that there's no stderr; see `add-runner'
|
|
for n in 0 1; do
|
|
rm -f "$root-$n"
|
|
|
|
mkfifo -m 0600 "$root/$n" || {
|
|
echo "fatal: failed to create FIFO at $in"
|
|
exit $EX_CANTCREAT
|
|
}
|
|
done
|
|
}
|
|
|
|
|
|
# Spawn a runner
|
|
#
|
|
# A new runner is created by spawning dslc and attaching
|
|
# new FIFOs under the given id ID relative to the given
|
|
# run path ROOT. The PID of the runner will be stored
|
|
# alongside the FIFOs in a pidfile `pid'.
|
|
spawn-runner()
|
|
{
|
|
local -ri id="${1?Missing id}"
|
|
local -r root="${2?Missing root run path}"
|
|
|
|
local -r base="$root/$id"
|
|
|
|
mkfifos "$base"
|
|
|
|
# TODO: should we separate back out std{out,err}?
|
|
# XXX: why does dslc quit (with a 0 exit code) occsionally? stdin?
|
|
while true; do
|
|
"$mypath/dslc" < <( persistent-cat "$base/0" ) \
|
|
>"$base/1" \
|
|
2>&1
|
|
echo "warning: runner $id exited with code $?; restarting"
|
|
done &
|
|
|
|
echo "$!" > "$base/pid"
|
|
|
|
echo "runner $id ($!): $base"
|
|
}
|
|
|
|
|
|
# Persistently read commands from FIFO IN
|
|
#
|
|
# This will continue to read from the FIFO as long as it is
|
|
# readable. This is necessary since SIGPIPE gets sent to
|
|
# processes reading/writing from/to the FIFO whenever a
|
|
# process detaches from it.
|
|
persistent-cat()
|
|
{
|
|
local -r in="${1?Missing input path}"
|
|
|
|
while test -r "$in"; do
|
|
read -r < "$in" || return
|
|
echo "$REPLY"
|
|
done
|
|
}
|
|
|
|
|
|
# Exit if tamed is already running at path ROOT
|
|
#
|
|
# If tamed is already running at ROOT, exit with status
|
|
# EX_RUNNING; otherwise, do nothing except output a warning
|
|
# if a stale pid file exists.
|
|
abort-if-running()
|
|
{
|
|
local -r root="${1?Missing root rundir}"
|
|
|
|
local -ri pid=$( cat "$root/pid" 2>/dev/null )
|
|
|
|
test "$pid" -gt 0 || return 0
|
|
|
|
! ps "$pid" &>/dev/null || {
|
|
echo "fatal: tamed is already running at $root (pid $pid)!"
|
|
exit $EX_RUNNING
|
|
}
|
|
|
|
test -z "$pid" || {
|
|
echo "warning: clearing stale tamed (pid $pid)"
|
|
}
|
|
}
|
|
|
|
|
|
# Kill running tamed at path ROOT
|
|
#
|
|
# If no pidfile is found at ROOT, do nothing. This sends a
|
|
# signal only to the parent tamed process, _not_ individual
|
|
# runners; the target tamed is expected to clean up itself.
|
|
# Consequently, if a tamed terminated abnormally without
|
|
# cleaning up, this will not solve that problem.
|
|
kill-running()
|
|
{
|
|
local -r root="${1?Missing root}"
|
|
|
|
local -r pid=$( cat "$root"/pid 2>/dev/null )
|
|
|
|
test -n "$pid" || return 0
|
|
|
|
echo "killing tamed at $root ($pid)..."
|
|
kill "$pid"
|
|
}
|
|
|
|
|
|
# Clean up child processes before exit
|
|
#
|
|
# This should be called before exit (perhaps by a trap). Kills
|
|
# the entire process group.
|
|
#
|
|
# Do not attach this to a SIGTERM trap or it will infinitely
|
|
# recurse.
|
|
cleanup()
|
|
{
|
|
echo "killing remaining runners..."
|
|
|
|
rm -rf "$root"
|
|
kill 0
|
|
}
|
|
|
|
|
|
# Output usage information and exit
|
|
usage()
|
|
{
|
|
cat <<EOF
|
|
Usage: $0 [--kill] [runpath]
|
|
Start tamed and runners. Do not fork into background process.
|
|
|
|
The default value of RUNPATH is \`/run/user/$UID/tamed'.
|
|
|
|
Only one runner is currently supported.
|
|
|
|
Options:
|
|
--kill kill a runing tamed at path RUNPATH
|
|
--help show this message
|
|
EOF
|
|
|
|
exit $EX_USAGE
|
|
}
|
|
|
|
|
|
# Run tamed
|
|
main()
|
|
{
|
|
local kill=
|
|
case "${1:-}" in
|
|
--kill) kill=1; shift;;
|
|
--help) usage;;
|
|
esac
|
|
|
|
root="${1:-/run/user/$UID/tamed}"
|
|
|
|
# kill if requested
|
|
test -z "$kill" || {
|
|
kill-running "$root"
|
|
exit
|
|
}
|
|
|
|
abort-if-running "$root"
|
|
|
|
# clean up background processes before we exit
|
|
trap exit TERM
|
|
trap cleanup EXIT
|
|
|
|
# start fresh
|
|
rm -rf "$root"; mkdir -p "$root"
|
|
echo $$ > "$root/pid"
|
|
|
|
# only a single runner for now
|
|
spawn-runner 0 "$root"
|
|
|
|
wait -n
|
|
}
|
|
|
|
main "$@"
|