]> rtime.felk.cvut.cz Git - novaboot.git/blob - server/novaboot-shell
Fix u-boot prompt handling
[novaboot.git] / server / novaboot-shell
1 #!/bin/sh
2
3 set -e
4
5 die() {
6     echo >&2 "novaboot-shell: $*"
7     exit 1
8 }
9
10 print_help() {
11     cat <<EOF
12 Target commands:
13 - console (default command)
14 - reset
15 - on
16 - off
17 - rsync ...
18 - get-config
19
20 Management commands:
21 - help
22 EOF
23
24     if [ "$NB_ADMIN" ]; then
25         cat <<EOF
26 - add-key
27 - shell (use with ssh -t)
28 EOF
29     fi
30     exit 0
31 }
32
33 add_key() {
34     local user
35     [ "$NB_ADMIN" ] || return 1
36
37     case $# in
38         0) die "Usage: ssh ... add-key USERNAME < id_rsa.pub";;
39         1) break;;
40         *) die "User name must not contain spaces: $*";;
41     esac
42     user="$1"
43     key=$(cat)
44
45     tmp=$(mktemp ~/.ssh/authorized_keys.XXXXXXXX)
46     {
47         cat ~/.ssh/authorized_keys
48         echo "command=\"user $user\" $key"
49     } | sort -u > $tmp
50
51     mv $tmp ~/.ssh/authorized_keys
52 }
53
54 exec_shell() {
55     [ "$NB_ADMIN" ] || die "Permission denied"
56     if ! tty > /dev/null; then
57         echo "novaboot-shell: Consider starting the shell with 'ssh -t'"
58     fi
59     exec /usr/bin/env bash || exec /bin/sh
60 }
61
62 lock_queue() {
63     lslocks | awk '{ if ($9 == "'"$RUN_DIR"'") { print $2 } }'
64 }
65
66 print_queue() {
67     local queue
68
69     queue=$(
70         for pid in $(lock_queue); do
71             echo $pid $(sed --null-data -ne '/^NOVABOOT_ID=/ s///p' /proc/$pid/environ)
72         done | sort)
73     if [ "$queue" ]; then
74         echo "Target is occupied by:"
75         ( echo "PID USER LOGIN_TIME FROM"; echo "$queue" ) | column -t
76     fi
77 }
78
79 locked() {
80     print_queue
81     exec flock --no-fork "$RUN_DIR" "$@"
82 }
83
84 unlocked() {
85     exec "$@"
86 }
87
88 read_config() {
89     . "${NOVABOOT_SHELL_CONFIG:-$HOME/.novaboot-shell}"
90 }
91
92 power() {
93     local cmd
94     case "$1" in
95         "on")  cmd="${on_cmd:?}";;
96         "off") cmd="${off_cmd:?}";;
97         *) die "Unexpected power parameter";;
98     esac
99
100     if [ "$PPID" -ne 1 ] && systemctl --user is-enabled --quiet novaboot-delayed-power-off.service; then
101         case "$1" in
102             "on") systemctl --user start novaboot-delayed-power-off.service;;
103             "off") sudo novaboot-power off;;
104         esac
105     else
106         eval "$cmd"
107     fi
108 }
109
110 run_console() {
111     trap "rm -f $RUN_DIR/ppid" EXIT
112     echo $NOVABOOT_PPID > $RUN_DIR/ppid
113     echo 'novaboot-shell: Connected'
114     # TODO: $reset_begin_cmd
115     [ -n "${on_cmd}" ] && power on
116     eval "$1"
117 }
118
119 # Run novaboot with the same configuration as specified in
120 # ~/.novaboot-shell, but allow the caller to extend of override them
121 # via parameters of this function.
122 run_novaboot() {
123     nbscript=$1
124     shift
125     OLD_IFS=$IFS
126     # Split $target_config below by newlines, not by words
127     IFS="
128 "
129     novaboot "$nbscript" $target_config --server="$HOME/tftproot" --reset-cmd="${reset_cmd:?}" --remote-cmd="${console_cmd:?}" "$@"
130     IFS=$OLD_IFS
131 }
132
133 # run_subcommand should be called only after permission checks and/or locking
134 run_subcommand() {
135     read_config
136     case "$*" in
137         "default")
138             run_console "${default_cmd:-${console_cmd:?}}";;
139         "console")
140             run_console "${console_cmd:?}";;
141         "reset")
142             eval "${reset_cmd:?}";;
143         "rsync --server "*" . .")
144             if ! [ $# -eq 5 -o \( $# -eq 6 -a "$4" = '--log-format=X' \) ]; then
145                 die "Unexpected rsync invocation: $*"
146             fi
147             mkdir -p "$HOME/tftproot"
148             cd "$HOME/tftproot"
149             exec "$@";;
150         "on")
151             power on
152             exit;;
153         "off")
154             power off
155             exit;;
156         *)
157             die "Unknown command: $*";;
158     esac
159 }
160
161 main() {
162     if [ "$1" = "-c" ]; then
163         set -- $2
164     elif [ $# -gt 0 ]; then
165         die "Permission denied"
166     fi
167
168     NB_ADMIN=
169     if [ "$1" = "user" ]; then
170         # Get user name encoded in ~/.ssh/authorized_keys
171         export NB_USER="$2";
172         [ "$3" = "admin" ] && NB_ADMIN=1
173         set -- $SSH_ORIGINAL_COMMAND
174     fi
175
176     IP=${SSH_CONNECTION%% *}
177     if [ "$IP" ]; then
178         HOST=$(getent hosts $IP) || HOST=$IP
179     else
180         HOST=localhost
181     fi
182     REMOTE=${HOST##* }
183     DATE=$(LANG=C date +'%F_%T')
184     export NOVABOOT_ID="${NB_USER:-?} $DATE ${REMOTE}"
185     export NOVABOOT_PPID=$PPID
186
187     mkdir -p "$RUN_DIR"
188
189     case "$1" in
190         # Commands allowed at any time
191         "") locked $0 default;;
192         "console") locked $0 console;;
193         "get-config") read_config && echo -n "${target_config}"; exit;;
194         "add-key") shift; add_key "$@"; exit;;
195         "shell") exec_shell; exit;;
196         "help") print_help;;
197
198         # Commands allowed only when nobody or the same user is connected
199         # to the console. "The same user" means that we were executed by
200         # the same sshd process that has the lock. This is ensured by
201         # using SSH connection sharing on client side.
202         reset | rsync | on | off)
203             ALLOWED_PPID=$(cat $RUN_DIR/ppid 2>/dev/null || :)
204             if [ "$PPID" -eq "${ALLOWED_PPID:-0}" ]; then run=unlocked; else run=locked; fi
205             $run $0 "$@";;
206         *)
207             echo >&2 "novaboot-shell: Command not allowed: $*"
208             logger -p error "novaboot-shell: Command not allowed: $*"
209             exit 1;;
210     esac
211 }
212
213 if [ -d "$HOME" ]; then
214     RUN_DIR="$HOME"
215 else
216     RUN_DIR="/tmp/novaboot-shell@$USER"
217     mkdir -p "$RUN_DIR"
218 fi
219
220 if [ -z "$NOVABOOT_ID" ] && [ "$PPID" -ne 1 ]; then
221     main "$@"
222 else
223     run_subcommand "$@"
224 fi