|
- #!/bin/bash
-
- set -euo pipefail
-
- test -z "${B_DIR:-}" && export B_DIR="$HOME/.beacon"
- export B_REPOS_DIR="$B_DIR/repos"
- export B_INDEX_DIR="$B_DIR/index"
- export B_STAGING_DIR="$B_DIR/staging"
-
- export B_HOOK_DIR="$HOME/.config/beacon/hooks"
- export B_SVC_DIR="$HOME/.config/beacon/svc"
-
- export B_CP_OPTS=(--no-dereference "--preserve=mode,links" --no-target-directory --remove-destination)
- export B_RSYNC_OPTS=(--quiet --recursive --links --perms --executability --exclude=.git/ --exclude=.gitignore --exclude=.beacon/)
- export B_DIFF_OPTS=(-r --no-dereference --unidirectional-new-file --unified=10)
- if diff --color /dev/null /dev/null >/dev/null 2>&1; then
- B_DIFF_OPTS+=(--color)
- fi
-
- test -f "$HOME/.beaconrc" && source "$HOME/.beaconrc"
-
- export B_LOG_DIR="$B_DIR/log"
- export B_NEEDED_DIR="$B_STAGING_DIR/needed"
- export B_PROVIDED_DIR="$B_STAGING_DIR/provided"
- export B_RUN_DIR="$B_DIR/run"
-
-
- if tput sgr0 2>/dev/null; then
- red="$(tput setaf 1)" ; export red
- green="$(tput setaf 2)" ; export green
- yellow="$(tput setaf 3)"; export yellow
- blue="$(tput setaf 4)" ; export blue
- cyan="$(tput setaf 6)" ; export cyan
- reset="$(tput sgr0)" ; export reset
- defwidth="$(tput cols)" ; export defwidth
-
- else
- export red=""
- export green=""
- export yellow=""
- export blue=""
- export cyan=""
- export reset=""
- export defwidth="80"
-
- fi
-
- export lvl
- test -z "${lvl:-}" && lvl=0
-
- function prefix {
- for i in $(seq 1 "$lvl"); do
- echo -n " "
- done
- }
- export -f prefix
-
- function status {
- (
- prefix
- [ -n "${1:-}" ] && echo -n "$blue$1$reset "
- shift
- echo "$@" | sed "2,$ s:^: $(prefix):g"
- ) | xargs -d '\n' echo
- }
- export -f status
-
- function sbegin {
- prefix
- [ -n "${1:-}" ] && echo -n "$blue$1$reset "
- shift
- echo -n "$@" "..."
- }
- export -f sbegin
-
- function send {
- local width; width="$(tput cols)" || width="$defwidth"
- local pos; pos=$((width-8))
- echo -ne "\033[999D\033[${pos}C"
-
- if [ "$1" -eq 0 ]; then
- echo "[ ${green}OK$reset ]"
- else
- echo "[ ${red}ERR$reset ]"
- fi
- }
- export -f send
-
- function sidle {
- local width; width="$(tput cols)" || width="$defwidth"
- local pos; pos=$((width-8))
- echo -ne "\033[999D\033[${pos}C"
-
- if [ "$1" -eq 0 ]; then
- echo "[ $blue-$reset ]"
- else
- echo "[ ${red}ERR$reset ]"
- fi
- }
- export -f sidle
-
- function send_ {
- local width; width="$(tput cols)" || width="$defwidth"
- local pos; pos=$((width-8))
- echo -ne "\033[999D\033[${pos}C"
-
- case "$1" in
- 0) echo "[ ${green}OK$reset ]" ;;
- 202) echo "[ $blue-$reset ]" ;;
- *) echo "[ ${red}ERR$reset ]" ;;
- esac
- }
- export -f send_
-
- function swait {
- local n="$1"
- shift
-
- if [ "$1" == "!" ]; then
- local inv="1"
- shift
- fi
-
- while [ "$n" -gt 0 ]; do
- if "$@" >/dev/null 2>&1; then
- test -n "${inv:-}" || break
- else
- test -z "${inv:-}" || break
- fi
-
- local width; width="$(tput cols)" || width="$defwidth"
- local pos; pos=$((width-8))
- echo -ne "\033[999D\033[${pos}C"
- printf "[ $cyan%2d$reset ]" "$n"
- sleep 1
- n=$((n-1))
- done
-
- set +e
- "$@" >/dev/null 2>&1
- local ret="$?"
- set -e
-
- if [ -n "${inv:-}" ]; then
- if [ "$ret" -eq 0 ]; then
- send 1
- return 1
- else
- send 0
- return 0
- fi
- else
- send "$ret"
- return "$ret"
- fi
- }
- export -f swait
-
- function swarn {
- echo
- prefix
- echo -n "$yellow$1$reset: "
- shift
- echo "$@" | sed "2,$ s:^: $(prefix):g"
- echo
- }
- export -f swarn
-
- function serr {
- echo
- prefix
- echo -n "$red$1$reset: "
- shift
- echo "$@" | sed "2,$ s:^: $(prefix):g"
- echo
- }
- export -f serr
-
- function sin {
- lvl=$((lvl + 1))
- }
- export -f sin
-
- function sout {
- lvl=$((lvl - 1))
- }
- export -f sout
-
-
-
- # setup
- # Make sure the beacon environment is correctly set up.
- function setup {
-
- mkdir -p \
- "$B_DIR" \
- "$B_REPOS_DIR" \
- "$B_INDEX_DIR" \
- "$B_STAGING_DIR" \
- "$B_NEEDED_DIR" \
- "$B_PROVIDED_DIR" \
- "$B_HOOK_DIR" \
- "$B_SVC_DIR" \
- "$B_LOG_DIR" \
- "$B_RUN_DIR"
-
- chmod ugo+rwxt "$B_INDEX_DIR" 2>/dev/null || true
- return 0
- }
-
-
-
- # update_ repo
- # Update the specified repo.
- function update_() (
- local src="$B_GIT_PREFIX$1$B_GIT_SUFFIX"
- local dst="$1"
- cd "$B_REPOS_DIR"
-
- if [ -f "$dst/.beacon/visited" ]; then
- return 0
- fi
-
- sbegin Repo "$dst"
- if [ -d "$dst" ]; then
- (
- cd "$dst"
- local before; before="$(git log -n 1 --pretty=format:%H 2>/dev/null || true)"
- git pull -q
- local e="$?"
- local after; after="$(git log -n 1 --pretty=format:%H 2>/dev/null || true)"
-
- if [ "$before" != "$after" ]; then
- send "$e"
- else
- sidle "$e"
- fi
- )
- else
- git clone -q "$src" "$dst"
- send $?
-
- fi
-
- # Fetch script
- if [ -x "$dst/.beacon/fetch" ]; then
- (
- cd "$dst"
- sin
- sbegin "Fetching"
- if ! .beacon/fetch >"$B_LOG_DIR/$dst.fetch.log" 2>&1; then
- send 1
- serr "Fetch failure" "$1"
- exit 1
- else
- send 0
- fi
- sout
- )
- fi
-
- grep -F -q "visited" "$dst/.gitignore" 2>/dev/null || echo "visited" >> "$dst/.gitignore"
-
- touch "$dst/.beacon/visited"
- sin
- if [ -f "$dst/.beacon/depend" ]; then
- while read -r dep; do
- if [ -n "${dep:-}" ]; then
- update_ "$dep"
- fi
- done < "$dst/.beacon/depend"
- fi
- sout
- )
-
- # update
- # Update all repos from $B_DIR/sources
- function update {
- status "Update"
- sin
- rm "$B_REPOS_DIR/"*"/.beacon/visited" 2>/dev/null || true
- update_ "$rootpkg"
- for theme in "${themes[@]}"; do
- update_ "$theme"
- done
- rm "$B_REPOS_DIR/"*"/.beacon/visited" 2>/dev/null || true
- sout
- }
-
-
-
- # clear
- # Clear the staging area and sed directory
- function clear {
- rm -rf "$B_STAGING_DIR" 2>/dev/null || true
- mkdir -p "$B_STAGING_DIR" "$B_NEEDED_DIR" "$B_PROVIDED_DIR"
- rm "$B_DIR/sedfile"{,.pre,.post} 2>/dev/null || true
- touch "$B_DIR/sedfile"{,.pre,.post}
- rm "$B_REPOS_DIR/"*"/.beacon/visited" 2>/dev/null || true
- }
-
-
-
- # stage <reponame> ...
- # Add the repo to the staging area according to the rules in `$1/.beacon/index`.
- # Append sed rules from `$1/.beacon/sed`
- # Touch needed / provided files.
- function stage() (
- while [ -n "${1:-}" ]; do
- # Basic sanity checks
- if [ ! -d "$B_REPOS_DIR/$1" ]; then
- serr "Staging error" "repo $1 not found."
- return 1
-
- elif [ ! -d "$B_REPOS_DIR/$1/.beacon" ]; then
- serr "Staging error" "repo $1 does not contain a .beacon folder."
- return 1
-
- elif [ ! -f "$B_REPOS_DIR/$1/.beacon/index" ]; then
- swarn "No index file" "$1"
- shift
- continue
-
- elif [ -f "$B_REPOS_DIR/$1/.beacon/visited" ]; then
- shift
- continue
-
- fi
-
- status "Staging" "$1"
- sin
- cd "$B_REPOS_DIR/$1"
-
- # Sed rules
- for s in .pre "" .post; do
- touch "$B_DIR/sedfile$s"
- if [ -f ".beacon/sed$s" ]; then
- sbegin "Checking" ".beacon/sed$s"
-
- set +e
- local err; err="$(sed -f ".beacon/sed$s" /dev/null 2>&1)"
- if [ "$?" -ne "0" ]; then
- set -e
- send 1
- serr "Sed error" "$err"
- exit 1
-
- else
- set -e
- echo -e "\n\n#=-=# Repo $1 #=-=#" >>"$B_DIR/sedfile$s"
- cat ".beacon/sed$s" >> "$B_DIR/sedfile$s" 2>/dev/null || true
- send 0
-
- fi
- fi
- done
-
- # Recursion handling
- touch ".beacon/visited"
- if [ -f ".beacon/depend" ]; then
- stagelist ".beacon/depend"
- fi
-
- cd "$B_REPOS_DIR/$1"
-
- # Build script
- if [ -x ".beacon/build" ]; then
- sbegin "Building"
- if .beacon/build >"$B_LOG_DIR/$1.build.log" 2>&1; then
- send 0
- else
- send 1
- serr "Build failure" "$1"
- exit 1
- fi
- fi
-
- # File staging
- while read -r line; do
- if [ "$line" != "" ]; then
- local mode; mode="$(cut -f 1 - <<< "$line")"
- local src; src="$(cut -f 2 - <<< "$line")"
- local destprefix; destprefix="$(cut -f 3 - <<< "$line")"
-
- case "$mode" in
- "@") dest="$B_STAGING_DIR/prim" ;;
- "$") dest="$B_STAGING_DIR/home" ;;
- "/") dest="$B_STAGING_DIR/root" ;;
- *)
- serr "Staging error in repo $1" "invalid mode: $mode ."
- return 1
- ;;
-
- esac
-
- if [ -n "${destprefix:-}" ]; then
- dest="$dest/$destprefix"
- fi
-
- mkdir -p "$dest"
- sbegin "rsync" "$src -> $dest"
- rsync "${B_RSYNC_OPTS[@]}" "$src/" "$dest" >/dev/null 2>&1
- send $?
- fi
- done < ".beacon/index"
-
- # Need / Provide
- if [ -f ".beacon/need" ]; then
- while read -r n; do
- if [ -n "${n:-}" ]; then
- echo "$1" >> "$B_NEEDED_DIR/$n"
- fi
- done < ".beacon/need"
- fi
-
- if [ -f ".beacon/provide" ]; then
- while read -r p; do
- if [ -n "${p:-}" ]; then
- echo "$1" >> "$B_PROVIDED_DIR/$p"
- fi
- done < ".beacon/provide"
- fi
-
- sout
-
- shift
- done
- )
-
-
-
- # dosed
- # Apply sed rules from all staged repos
- function dosed {
- status "Checking" "needs"
- sin
-
- # Needs checking
- for f in "$B_NEEDED_DIR/"*; do
- if [ -f "$f" ]; then
- sbegin "Need" "${f##*/}"
- if [ ! -f "$B_PROVIDED_DIR/${f#$B_NEEDED_DIR}" ]; then
- send 1
- serr "Deploy error" "${f#$B_NEEDED_DIR/} not provided, but needed by the following repos:"
- sed 's:^:'"$(prefix)"' - :g' "$f"
- return 1
- else
- send 0
- fi
- fi
- done
-
- sout
-
- sbegin "Sed"
- cat "$B_DIR/sedfile"{.pre,,.post} >"$B_DIR/sedfile.all"
- find -O3 "$B_STAGING_DIR" -type f -size -64k -exec sed -i -f "$B_DIR/sedfile.all" '{}' '+'
- send $?
- rm "$B_DIR/sedfile.all"
- }
-
-
-
- # deploy <src> <dest> <quiet>
- # Deploy all staged files from `$B_STAGING_DIR/$1` to `$2`
- # If "$3" != "" don't confirm each write
- function deploy {
- status "Deploy" "${2:-/}"
- local entire_dir=""
-
- local idx; idx="$B_INDEX_DIR/$1-$(whoami)"
- for f in $(find "$B_STAGING_DIR/$1" \( -type f -o -type l \) -print | LC_COLLATE=C sort); do
- # Get the destination file name
- local dest="$2${f#$B_STAGING_DIR/$1}"
-
- # Check for differences
- if ! diff "${B_DIFF_OPTS[@]}" "$f" "$dest" >/dev/null 2>&1 \
- || [ "$(stat -c %A "$f" 2>/dev/null)" != "$(stat -c %A "$dest" 2>/dev/null)" ]; then
- echo "${yellow}--------------------------------------------------------------------------------${reset}"
- echo "${yellow}$f ${dest}${reset}"
-
- # Diff both files
- diff "${B_DIFF_OPTS[@]}" "$dest" "$f"
-
- sedopts=(-e "s/r/${green}r$reset/g" -e "s/w/${yellow}w$reset/g" -e "s/x/${blue}x$reset/g" -e "s/s/${red}s$reset/g")
- echo "${yellow}--------------------------------------------------------------------------------${reset}"
- echo -n "old: "
- { stat -c %A "$dest" 2>/dev/null || echo; } | sed "${sedopts[@]}"
- echo -n "new: "
- { stat -c %A "$f" 2>/dev/null || echo; } | sed "${sedopts[@]}"
-
- while true; do
- echo -n "Write $yellow$dest$reset ? [ ${green}(y)es${reset} | ${red}(n)o${reset} | ${blue}yes to (d)irectory${reset} ] "
- if [ -n "${3:-}" ] || [ "$entire_dir" == "$(dirname "$dest")" ]; then
- echo "y"
- answer="y"
- break
- else
- read -rn 1 answer
- echo
- case "${answer,,}" in
- y|n|d)
- break
- ;;
- esac
- fi
- done
-
- if [ "${answer,,}" == "d" ]; then
- entire_dir="$(dirname "$dest")"
- answer="y"
- elif [ "$(dirname "$dest")" != "$entire_dir" ]; then
- entire_dir=""
- fi
-
- # Copy the file
- if [ "${answer,,}" == "y" ]; then
- mkdir -p "$(dirname "$dest")"
- cp "${B_CP_OPTS[@]}" "$f" "$dest"
- echo "$dest" >> "$idx"
- echo "${green}Ok$reset"
-
- else
- echo "${red}Not written$reset"
-
- fi
-
- else
- echo "$dest" >> "$idx"
-
- fi
- done
-
- # Clean up index file
- sort "$idx" > "$idx.tmp"
- uniq "$idx.tmp" > "$idx"
- rm "$idx.tmp"
-
- # Check for deleted files
- mapfile -t buf <"$idx"
- for f in "${buf[@]}"; do
-
- # If the indexed file is under the current deploy directory
- # If the indexed file doesn't exist in the staging dir
- grep -q "^$2" <<<"$f" && if [ ! -e "$B_STAGING_DIR/$1/${f#$2}" ] && [ ! -L "$B_STAGING_DIR/$1/${f#$2}" ]; then
-
- # but exists in the destination dir
- if [ -e "$f" ]; then
- echo -n "Delete $yellow$f$reset ? [ ${green}(y)es$reset | ${red}(N)o$reset ] "
- read -rn 1 answer
- echo
-
- if [ "${answer,,}" == "y" ]; then
- rm -f "$f"
-
- # Recursively remove containing directories when empty
- local d; d="$(dirname "$f")"
- while rmdir "$d" 2>/dev/null; do
- echo "Also removing empty directory $yellow$d$reset ..."
- d="$(dirname "$d")"
- done
-
- # Remove index entry
- grep -v "$f" "$idx" > "$idx.tmp"
- mv "$idx.tmp" "$idx"
- fi
-
- # and doesn't exist in the destination dir either
- else
-
- # Remove index entry
- grep -v "$f" "$idx" > "$idx.tmp"
- mv "$idx.tmp" "$idx"
- fi
- fi
- done
- }
- export -f deploy
-
-
- # deployhome <username>
- # equal to `deploy home $HOME` as user `$1`
- function deployhome {
- if [ "$(whoami)" != "$1" ]; then
- status "su" "$1"
- sin
- sudo sudo -u "$1" B_DIR="$B_DIR" lvl="$lvl" "$(realpath "$0")" deployhome "$1" || exit 1
- sout
-
- else
- status "Deploy" "home"
- sin
- deploy home "$HOME" "${2:-}" || exit 1
- sout
-
- if [ "$1" == "${users[0]:-}" ]; then
- status "Deploy" "prim"
- sin
- deploy prim "$HOME" "${2:-}" || exit 1
- sout
- fi
-
- fi
- }
-
-
- # deployroot
- # equal to `deploy root /` as user root
- function deployroot {
- if [ -d "$B_STAGING_DIR/root" ]; then
- if [ "$(whoami)" != root ]; then
- status "su" "root"
- sin
- sudo B_DIR="$B_DIR" lvl="$lvl" "$(realpath "$0")" deployroot || exit 1
- sout
-
- else
- status "Deploy" "root"
- sin
- deploy root "" || exit 1
- sout
-
- fi
- fi
- }
-
-
-
- # upgrade
- function upgrade {
- clear
- stage "$rootpkg" || exit 1
- if [ -f "$B_DIR/current-theme" ]; then
- stage "$(cat "$B_DIR/current-theme")" || exit 1
- else
- stage "${themes[0]}" || exit 1
- fi
-
- dosed || exit 1
- for u in "${users[@]}"; do
- deployhome "$u" || exit 1
- done
- if [ -n "${rootfiles:-}" ]; then
- deployroot || exit 1
- fi
- hook reload || exit 1
- clear
- }
-
- # theme <name>
- function theme {
- clear
- echo "$1" >"$B_DIR/current-theme"
- stage "$rootpkg" "$1" || exit 1
- dosed || exit 1
- deployhome "$(whoami)" quiet || exit 1
- hook theme || exit 1
- clear
- }
-
-
- # stagelist <listfile>
- # Stage all repos listed, one per line.
- function stagelist {
- while read -r repo; do
- if [ -n "${repo:-}" ]; then
- if ! stage "$repo"; then
- echo "In file $1"
- return 1
- fi
- fi
- done <"$1"
- }
-
-
- # hook <event> [args] ...
- function hook() (
- cd "$HOME"
- status "Hook" "$@"
- sin
-
- export sgn="$1"
- function on {
- local found=
- while [ -n "${1:-}" ]; do
- if [ "$1" == "$sgn" ]; then
- found=1
- export did_something=1
- break
- fi
- shift
- done
- if [ -z "${found:-}" ]; then
- if [ -z "${did_something:-}" ]; then
- echo "Skipped, not running for: $sgn"
- exit 202
- else
- exit 0
- fi
- fi
- }
- export -f on
-
- function check {
- if [ "$1" == "!" ]; then
- shift
- if "$@"; then
- echo "Skipped, command: !" "$@"
- exit 202
- fi
- else
- if ! "$@"; then
- echo "Skipped, command:" "$@"
- exit 202
- fi
- fi
- }
- export -f check
-
- sbegin "Checking" "internet connectivity"
- if swait 10 ping -c 1 8.8.4.4; then
- sbegin "Checking" "DNS availability"
- if swait 3 nslookup "en.wikipedia.org"; then
- export online=1
- else
- unset online 2>/dev/null || true
- fi
- fi
-
- rm "$B_LOG_DIR/"* 2>/dev/null || true
-
- if [ -n "${parallel_hooks:-}" ]; then
- function _do_hook {
- local f="$1"
- shift
- status "$@" "$(basename "$f")"
- local logfile; logfile="$B_LOG_DIR/$(basename "$f")"
- set +e
- "$f" "$@" >"$logfile" 2>&1
- local s="$?"
- if [ "$s" -ne 0 ] && [ "$s" -ne 202 ]; then
- swarn "Warning" "$f returned $s"
- notify-send "$f returned $s" "$(cat "$logfile")"
- fi
- set -e
- }
-
- for i in $(seq 1 99); do
- local n; n="$(printf '%02d' "$i")"
- for f in "$B_HOOK_DIR/$n-"*; do
- test -x "$f" || continue
- _do_hook "$f" "$@" &
- done
- wait
- done
- else
- for f in "$B_HOOK_DIR/"*; do
- test -x "$f" || continue
- sbegin "$@" "$(basename "$f")"
- local logfile; logfile="$B_LOG_DIR/$(basename "$f")"
- set +e
- "$f" "$@" </dev/null >"$logfile" 2>&1
- local s="$?"
- set -e
- send_ "$s"
- if [ "$s" != 0 ]; then
- notify-send "$f returned $s" "$(cat "$logfile")"
- fi
- done
- fi
- sout
- unset online
- )
-
- function repos() (
- cd "$B_REPOS_DIR"
-
- for d in *; do
- test -d "$d" || continue
-
- (
- cd "$d"
- local s; s="$(git status --porcelain=v2 --branch)"
-
- status "$d"
- sin
- if echo "$s" | grep -vq "^#"; then
- status "" "Uncomitted changes in repo $d"
- fi
-
- if echo "$s" | grep "^# branch.ab" | grep -vq "^# branch.ab +0 -0"; then
- status "" "Unsynchronized changes in repo $d"
- fi
- sout
- )
- done
- )
-
- function start (
- local name="$1"
- if [ ! -x "$B_SVC_DIR/$name" ]; then
- echo "start: $name: not found"
- return 1
- fi
- if ! running "$name" >/dev/null; then
- cd
- setsid "$B_SVC_DIR/$name" </dev/null >"$B_LOG_DIR/$name" 2>&1 &
- local pid="$!"
- disown "$pid"
- echo "$pid" > "$B_RUN_DIR/$name"
- echo "start: command started with pid $pid"
- echo -n " "
- cat "/proc/$pid/cmdline" | xargs -0 -n 1 printf " '%s'"
- echo
- else
- echo "start: $name: already running"
- fi
- )
-
- function stop {
- local timer=10
- if [ "$1" == "-t" ]; then
- timer="${2}0"
- shift 2
- fi
- local name="$1"
- if [ ! -f "$B_RUN_DIR/$name" ]; then
- echo "stop: $name: not started"
- return 1
- fi
-
- function killproc {
- if kill -0 "$1" 2>/dev/null; then
- echo "stop: found pid $1"
- echo -n " "
- cat "/proc/$1/cmdline" | xargs -0 -n 1 printf " '%s'"
- echo
- echo "stop: TERM $1"
- kill -TERM "$1" 2>/dev/null || return 0
- kill -CONT "$1"
- for i in $(seq 0 "$timer"); do
- sleep 0.1
- kill -0 "$1" 2>/dev/null || return 0
- echo "stop: still alive after ${i}00ms ..."
- done
- echo "stop: KILL $1"
- kill -9 "$1"
- fi
- }
-
- function ptree {
- local children; children="$(ps -o pid= --ppid "$1")"
- for pid in $children; do
- ptree "$pid"
- done
- echo "$children"
- }
-
- local pid; pid="$(cat "$B_RUN_DIR/$name")"
- rm "$B_RUN_DIR/$name"
-
- if [ -z "$pid" ]; then
- echo "stop: $name: not started"
- return 1
- fi
-
- local children; children="$(ptree "$pid")"
- killproc "$pid"
- echo "stop: checking children"
- for c in $children; do
- killproc "$c"
- done
- }
-
- function signal {
- local name="$1"
- local sig="$2"
- if [ ! -f "$B_RUN_DIR/$name" ]; then
- echo "signal: $name: not started"
- return 1
- fi
- local pid; pid="$(cat "$B_RUN_DIR/$name")"
- kill -s "$sig" "$pid"
- }
-
- function running {
- if [ -n "${1:-}" ]; then
- local name="$1"
- if [ -f "$B_RUN_DIR/$name" ]; then
- local pid; pid="$(cat "$B_RUN_DIR/$name")"
- if kill -0 "$pid" 2>/dev/null; then
- printf "%5d %s%-8s${reset} %s :" "$pid" "${green}" "Running" "$name"
- cat "/proc/$pid/cmdline" | xargs -0 -n 1 printf " '%s'"
- echo
- return 0
- else
- printf "%5d %s%-8s${reset} %s\n" "$pid" "${red}" "Crashed" "$name"
- test ! -e "$B_LOG_DIR/$name" || ( tail -n 10 "$B_LOG_DIR/$name" | sed 's:^:'$'\t'':' )
- return 2
- fi
- else
- printf " - %s%-8s${reset} %s\n" "${blue}" "Inactive" "$name"
- test ! -e "$B_LOG_DIR/$name" || ( tail -n 10 "$B_LOG_DIR/$name" | sed 's:^:'$'\t'':' )
- return 1
- fi
- else
- local name; for name in $(find "$B_SVC_DIR" -type f -printf "%f\n" | sort); do
- running "$name" || true
- done
- fi
- }
-
- function toggle {
- local timer=""
- if [ "$1" == "-t" ]; then
- timer="$2"
- shift 2
- fi
- local name="$1"
- if [ -f "$B_RUN_DIR/$name" ]; then
- if [ -n "${timer:-}" ]; then
- stop -t "$timer" "$name"
- else
- stop "$name"
- fi
- else
- start "$name"
- fi
- }
-
- function reapd {
- cd "$B_RUN_DIR"
- while true; do
- for f in *; do
- if ! kill -0 "$(cat "$f")" 2>/dev/null; then
- local logfile; logfile="$B_LOG_DIR/$(basename "$f")"
- notify-send "reapd: $f died" "$(tail -n 20 "$logfile")"
- rm "$f"
- fi
- done
- sleep 5
- done
- }
-
- function log {
- tail -f -n +1 "$B_LOG_DIR/"*"$1"*
- }
-
- function run {
- local fails=0
- local cmd="$(cat)"
- local input=
- local timing=60
- local failtiming=1
- local maxfails=3
- local title=
- while [ -n "${1:-}" ]; do
- case "${1:-}" in
- "--stdin")
- input="$(bash -c "$2")"
- shift 2
- ;;
-
- "--sleep")
- timing="$2"
- shift 2
- ;;
-
- "--retry-sleep")
- failtiming="$2"
- shift 2
- ;;
-
- "--retry")
- maxfails="$2"
- shift 2
- ;;
-
- "--title")
- title="$2"
- shift 2;
- ;;
-
- *)
- echo "Unknown option: $1"
- exit 1
- ;;
- esac
- done
- printf "run-%s" "${title:-?}" > /proc/$$/comm
- set +e
- while true; do
- bash -c "$cmd" <<<"$input"
- if [ "$?" -eq 0 ]; then
- sleep "$((timing))"
- else
- fails="$((fails + 1))"
- echo "command failed ($fails/$maxfails)"
- if [ "$fails" -ge "$maxfails" ]; then
- echo "giving up"
- exit 1
- fi
- sleep "$failtiming"
- fi
- done
- }
-
- setup
-
- "$@"
|