#!/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 /bin/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 tmp=$(mktemp) no_fork= flock -h 2>&1 | grep -q -e "--no-fork" && no_fork=--no-fork rm -f "$tmp" exec flock $no_fork "$RUN_DIR" "$@" } unlocked() { exec "$@" } read_config() { . "${NOVABOOT_SHELL_CONFIG:-$HOME/.novaboot-shell}" } # run_subcommand should be called only after permission checks and/or locking run_subcommand() { read_config case "$*" in "console") trap "rm -f $RUN_DIR/ppid" EXIT echo $NOVABOOT_PPID > $RUN_DIR/ppid echo 'novaboot-shell: Connected' # TODO: $reset_begin_cmd eval exec "${console_cmd:?}";; "reset") eval exec "${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") eval exec "${on_cmd:?}";; "off") eval exec "${off_cmd:?}";; 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 NB_USER="$2"; [ "$3" = "admin" ] && NB_ADMIN=1 set -- $SSH_ORIGINAL_COMMAND fi if [ $# -eq 0 ]; then print_help; fi IP=${SSH_CONNECTION%% *} HOST=$(getent hosts $IP) || HOST=$IP 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 "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 cline 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 } RUN_DIR="$HOME" if [ -z "$NOVABOOT_ID" ]; then main "$@" else run_subcommand "$@" fi exit : < -c "command [arguments...]" B command [arguments...] B command [arguments...] =head1 DESCRIPTION B provides L with unified SSH-based interface for controlling target hardware. This simplifies client-side configuration, because typically only I<-ssh=...> option is needed on the client side. B is typically configured as a login shell of special user accounts associated with target hardware. It ensures that users are able to perform only a limited set of action (see L below) with the target and have no shell access on the server. =head1 COMMANDS =over 8 =item console Connect to target console (usually serial line). When somebody is connected to the console, other users are blocked from controlling the target. Blocked users see a message indicating who blocks them. The user connected to the console is able to invoke other commands such as L, but only when the command is invoked via the same SSH connection. This can be accomplished by using SSH connection sharing, which is what L uses (see I<-M> and I<-S> in L). =item reset Reset the target hardware. =item on Power on the target hardware. =item off Power off the target hardware. =item rsync [...] This command is not meant to be invoked directly by the user. It allows using L to copy files to the target, perhaps for TFTP server. The rsync command must be invoked as: C, i.e. without specifying destination path. The files will be stored into I<$HOME/tftproot>. =item user [admin] User command is meant to be used with C option in SSH's L file. It allows the shell to display human-readable names when printing information about who blocks the target. Then, the real command is taken from SSH_ORIGINAL_COMMAND environment variable. When "admin" is specified after the user name, this user is considered an administrator and is allowed to run L and L commands. =back =head2 Administration commands Only administrators (see L) are allowed to execute these commands. =over 8 =item add-key Reads the SSH public key from standard input and adds it into in F<~/.ssh/authorized_keys>. Example: C =item shell Runs shell on the server. Useful for editing configuration file. It is better used with allocated pseudo-terminal. Example: C =back =head1 CONFIGURATION FILE B reads configuration file from F<$HOME/.novaboot-shell>. It should define values for the following variables in the SH syntax. =over 8 =item console_cmd Command to C that connects to target's console. =item reset_cmd Command to C that resets the Target. =item on_cmd Command to C that powers the target on. =item off_cmd Command to C that powers the target off. =item target_config Novaboot command line options that specify which boot loader is used by the target (L rejects other, possibly dangerous, options). Each option is on its own line and no quoting, escaping or stripping is performed on the values. Example: target_config="\ --uboot=(uboot) --uboot-init=setenv serverip 192.168.1.1; setenv ipaddr 192.168.1.10 --uboot-addr=kernel=0x8100000 --uboot-addr=fdt=0x83000000 --uboot-addr=ramdisk=0x83100000 " =back =head1 AUTHORS Michal Sojka Latest version can be found at L. =cut EOF