]> rtime.felk.cvut.cz Git - novaboot.git/blob - server/novaboot-shell
Merge branch 'feat/pxe-boot'
[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         sudo novaboot-power "$1"
102     else
103         eval "$cmd"
104     fi
105 }
106
107 run_console() {
108     trap "rm -f $RUN_DIR/ppid" EXIT
109     echo $NOVABOOT_PPID > $RUN_DIR/ppid
110     echo 'novaboot-shell: Connected'
111     # TODO: $reset_begin_cmd
112     [ -n "${on_cmd}" ] && power on
113     eval "$1"
114 }
115
116 # run_subcommand should be called only after permission checks and/or locking
117 run_subcommand() {
118     read_config
119     case "$*" in
120         "default")
121             run_console "${default_cmd:-${console_cmd:?}}";;
122         "console")
123             run_console "${console_cmd:?}";;
124         "reset")
125             eval exec "${reset_cmd:?}";;
126         "rsync --server "*" . .")
127             if ! [ $# -eq 5 -o \( $# -eq 6 -a "$4" = '--log-format=X' \) ]; then
128                 die "Unexpected rsync invocation: $*"
129             fi
130             mkdir -p "$HOME/tftproot"
131             cd "$HOME/tftproot"
132             exec "$@";;
133         "on")
134             power on
135             exit;;
136         "off")
137             power off
138             exit;;
139         *)
140             die "Unknown command: $*";;
141     esac
142 }
143
144 main() {
145     if [ "$1" = "-c" ]; then
146         set -- $2
147     elif [ $# -gt 0 ]; then
148         die "Permission denied"
149     fi
150
151     NB_ADMIN=
152     if [ "$1" = "user" ]; then
153         # Get user name encoded in ~/.ssh/authorized_keys
154         export NB_USER="$2";
155         [ "$3" = "admin" ] && NB_ADMIN=1
156         set -- $SSH_ORIGINAL_COMMAND
157     fi
158
159     IP=${SSH_CONNECTION%% *}
160     if [ "$IP" ]; then
161         HOST=$(getent hosts $IP) || HOST=$IP
162     else
163         HOST=localhost
164     fi
165     REMOTE=${HOST##* }
166     DATE=$(LANG=C date +'%F_%T')
167     export NOVABOOT_ID="${NB_USER:-?} $DATE ${REMOTE}"
168     export NOVABOOT_PPID=$PPID
169
170     mkdir -p "$RUN_DIR"
171
172     case "$1" in
173         # Commands allowed at any time
174         "") locked $0 default;;
175         "console") locked $0 console;;
176         "get-config") read_config && echo -n "${target_config}"; exit;;
177         "add-key") shift; add_key "$@"; exit;;
178         "shell") exec_shell; exit;;
179         "help") print_help;;
180
181         # Commands allowed only when nobody or the same user is connected
182         # to the console. "The same user" means that we were executed by
183         # the same sshd process that has the lock. This is ensured by
184         # using SSH connection sharing on client side.
185         reset | rsync | on | off)
186             ALLOWED_PPID=$(cat $RUN_DIR/ppid 2>/dev/null || :)
187             if [ "$PPID" -eq "${ALLOWED_PPID:-0}" ]; then run=unlocked; else run=locked; fi
188             $run $0 "$@";;
189         *)
190             echo >&2 "novaboot-shell: Command not allowed: $*"
191             logger -p error "novaboot-shell: Command not allowed: $*"
192             exit 1;;
193     esac
194 }
195
196 if [ -d "$HOME" ]; then
197     RUN_DIR="$HOME"
198 else
199     RUN_DIR="/tmp/novaboot-shell@$USER"
200     mkdir -p "$RUN_DIR"
201 fi
202
203 if [ -z "$NOVABOOT_ID" ] && [ "$PPID" -ne 1 ]; then
204     main "$@"
205 else
206     run_subcommand "$@"
207 fi