#!/bin/sh set -e die() { echo >&2 "novaboot-shell: $*" exit 1 } print_help() { cat < $tmp mv $tmp ~/.ssh/authorized_keys } exec_shell() { [ "$NB_ADMIN" ] || die "Permission denied" if ! tty > /dev/null; then echo "novaboot-shell: Consider starting the shell with 'ssh -t'" fi exec /usr/bin/env bash || exec /bin/sh } lock_queue() { lslocks | awk '{ if ($9 == "'"$RUN_DIR"'") { print $2 } }' } print_queue() { local queue queue=$( for pid in $(lock_queue); do echo $pid $(sed --null-data -ne '/^NOVABOOT_ID=/ s///p' /proc/$pid/environ) done | sort) if [ "$queue" ]; then echo "Target is occupied by:" ( echo "PID USER LOGIN_TIME FROM"; echo "$queue" ) | column -t fi } locked() { print_queue exec flock --no-fork "$RUN_DIR" "$@" } unlocked() { exec "$@" } read_config() { . "${NOVABOOT_SHELL_CONFIG:-$HOME/.novaboot-shell}" } power() { local cmd case "$1" in "on") cmd="${on_cmd:?}";; "off") cmd="${off_cmd:?}";; *) die "Unexpected power parameter";; esac if [ "$PPID" -ne 1 ] && systemctl --user is-enabled --quiet novaboot-delayed-power-off.service; then case "$1" in "on") systemctl --user start novaboot-delayed-power-off.service;; "off") sudo novaboot-power off;; esac else eval "$cmd" fi } run_console() { trap "rm -f $RUN_DIR/ppid" EXIT echo $NOVABOOT_PPID > $RUN_DIR/ppid echo 'novaboot-shell: Connected' # TODO: $reset_begin_cmd [ -n "${on_cmd}" ] && power on eval "$1" } # Run novaboot with the same configuration as specified in # ~/.novaboot-shell, but allow the caller to extend of override them # via parameters of this function. run_novaboot() { nbscript=$1 shift OLD_IFS=$IFS # Split $target_config below by newlines, not by words IFS=" " novaboot "$nbscript" $target_config --server="$HOME/tftproot" --reset-cmd="${reset_cmd:?}" --remote-cmd="${console_cmd:?}" "$@" IFS=$OLD_IFS } # run_subcommand should be called only after permission checks and/or locking run_subcommand() { read_config case "$*" in "default") run_console "${default_cmd:-${console_cmd:?}}";; "console") run_console "${console_cmd:?}";; "reset") eval "${reset_cmd:?}";; "rsync --server "*" . .") if ! [ $# -eq 5 -o \( $# -eq 6 -a "$4" = '--log-format=X' \) ]; then die "Unexpected rsync invocation: $*" fi mkdir -p "$HOME/tftproot" cd "$HOME/tftproot" exec "$@";; "on") power on exit;; "off") power off exit;; *) die "Unknown command: $*";; esac } main() { if [ "$1" = "-c" ]; then set -- $2 elif [ $# -gt 0 ]; then die "Permission denied" fi NB_ADMIN= if [ "$1" = "user" ]; then # Get user name encoded in ~/.ssh/authorized_keys export NB_USER="$2"; [ "$3" = "admin" ] && NB_ADMIN=1 set -- $SSH_ORIGINAL_COMMAND fi IP=${SSH_CONNECTION%% *} if [ "$IP" ]; then HOST=$(getent hosts $IP) || HOST=$IP else HOST=localhost fi REMOTE=${HOST##* } DATE=$(LANG=C date +'%F_%T') export NOVABOOT_ID="${NB_USER:-?} $DATE ${REMOTE}" export NOVABOOT_PPID=$PPID mkdir -p "$RUN_DIR" case "$1" in # Commands allowed at any time "") locked $0 default;; "console") locked $0 console;; "get-config") read_config && echo -n "${target_config}"; exit;; "add-key") shift; add_key "$@"; exit;; "shell") exec_shell; exit;; "help") print_help;; # Commands allowed only when nobody or the same user is connected # to the console. "The same user" means that we were executed by # the same sshd process that has the lock. This is ensured by # using SSH connection sharing on client side. reset | rsync | on | off) ALLOWED_PPID=$(cat $RUN_DIR/ppid 2>/dev/null || :) if [ "$PPID" -eq "${ALLOWED_PPID:-0}" ]; then run=unlocked; else run=locked; fi $run $0 "$@";; *) echo >&2 "novaboot-shell: Command not allowed: $*" logger -p error "novaboot-shell: Command not allowed: $*" exit 1;; esac } if [ -d "$HOME" ]; then RUN_DIR="$HOME" else RUN_DIR="/tmp/novaboot-shell@$USER" mkdir -p "$RUN_DIR" fi if [ -z "$NOVABOOT_ID" ] && [ "$PPID" -ne 1 ]; then main "$@" else run_subcommand "$@" fi