frugal-iot-server 2.0.11 → 2.0.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/INSTALLATION.md +128 -31
- package/docs/OPENWRT.md +2 -2
- package/package.json +5 -3
- package/public/service-worker.js +1 -1
- package/scripts/addbridge-prod.zsh +15 -19
- package/scripts/addorganization.zsh +9 -3
- package/scripts/backup.zsh +361 -0
- package/scripts/broker-config.zsh +185 -0
- package/scripts/diagnostic.zsh +122 -83
- package/scripts/init.zsh +161 -84
- package/scripts/not-published.txt +1 -0
- package/scripts/openwrt-preflight.sh +14 -2
- package/scripts/restore.zsh +375 -0
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
#!/usr/bin/env zsh
|
|
2
|
+
#
|
|
3
|
+
# Put back a snapshot taken by frugal-iot-backup. See BACKUP_PLAN.md.
|
|
4
|
+
#
|
|
5
|
+
# Usage, from the server's own directory, on a machine where the package is already installed:
|
|
6
|
+
# npx --no frugal-iot-restore <snapshot|latest> # say what it would do; changes nothing
|
|
7
|
+
# npx --no frugal-iot-restore <snapshot|latest> state # onto a machine laid out differently
|
|
8
|
+
# npx --no frugal-iot-restore <snapshot|latest> all # onto the layout it was taken from
|
|
9
|
+
# Add "force" as the last word to replace an installation that already has logins of its own.
|
|
10
|
+
#
|
|
11
|
+
# "latest" is the newest snapshot in /var/backups/frugal-iot, from whichever machine it was taken on.
|
|
12
|
+
#
|
|
13
|
+
# "state" is for a machine set up by its own installer - a rebuilt Pi, a new server, the restore
|
|
14
|
+
# drill. It brings back what this installation knows: the database, config.d (secrets included),
|
|
15
|
+
# the broker's password file, ACL file and dynamic-security state - each put wherever THIS broker's
|
|
16
|
+
# configuration says it keeps them. It leaves alone what describes the machine: the broker's own
|
|
17
|
+
# configuration, its TLS certificate, the systemd unit, and config.d/mqtt.yaml and server.yaml.
|
|
18
|
+
#
|
|
19
|
+
# "all" is for the same machine, or a copy of it: everything goes back exactly where it came from,
|
|
20
|
+
# and broker configuration files the snapshot does not have are moved aside, because mosquitto
|
|
21
|
+
# would read them too and a listener declared twice stops it starting.
|
|
22
|
+
#
|
|
23
|
+
# Either way, it first takes a snapshot of what is here now into /var/backups/frugal-iot/pre-restore,
|
|
24
|
+
# stops the server and the broker (every node disconnects for a few seconds), puts the files back,
|
|
25
|
+
# runs frugal-iot-init (so an older database gets this release's migrations) and
|
|
26
|
+
# frugal-iot-rebuild-dynsec, starts both again and runs the diagnostic.
|
|
27
|
+
#
|
|
28
|
+
# data/ is not in a snapshot. Copy it back from wherever it is mirrored.
|
|
29
|
+
|
|
30
|
+
set -euo pipefail
|
|
31
|
+
autoload -Uz is-at-least
|
|
32
|
+
|
|
33
|
+
SCRIPT="${0:A}"
|
|
34
|
+
# Where "latest" looks, and where the snapshot of what was here before goes (in pre-restore/)
|
|
35
|
+
BACKUP_DIR=${FRUGAL_IOT_BACKUPS:-/var/backups/frugal-iot}
|
|
36
|
+
|
|
37
|
+
usage() {
|
|
38
|
+
sed -n '5,10p' "$SCRIPT" | sed 's/^# //' >&2
|
|
39
|
+
exit 2
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
(( $# >= 1 )) || usage
|
|
43
|
+
SNAPARG=$1
|
|
44
|
+
MODE=check
|
|
45
|
+
FORCE=0
|
|
46
|
+
for a in "${@[2,-1]}"; do
|
|
47
|
+
case $a in
|
|
48
|
+
check|state|all) MODE=$a ;;
|
|
49
|
+
force) FORCE=1 ;;
|
|
50
|
+
*) echo "Unexpected argument: $a" >&2; usage ;;
|
|
51
|
+
esac
|
|
52
|
+
done
|
|
53
|
+
|
|
54
|
+
have() { command -v "$1" >/dev/null 2>&1; }
|
|
55
|
+
die() { echo "Error: $*" >&2; exit 1; }
|
|
56
|
+
warn() { echo "Warning: $*" >&2; }
|
|
57
|
+
ownmode() { stat -c '%U:%G %a' "$1" 2>/dev/null || stat -f '%Su:%Sg %Lp' "$1" 2>/dev/null || echo "?:? ?"; }
|
|
58
|
+
as_root() {
|
|
59
|
+
have sudo || die "this needs root, and there is no sudo here - run it as root"
|
|
60
|
+
echo "Re-running as root..." >&2
|
|
61
|
+
exec sudo -- zsh "$SCRIPT" "$@"
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
# ---- Which snapshot ----
|
|
65
|
+
if [[ $SNAPARG == latest ]]; then
|
|
66
|
+
# Newest by the time in its name, not by mtime - a snapshot copied here from another machine has
|
|
67
|
+
# the mtime of the copy. The directory is 700, so this may need root just to look.
|
|
68
|
+
if [[ ! -r "$BACKUP_DIR" ]] && (( EUID != 0 )); then as_root "$@"; fi
|
|
69
|
+
SNAPSHOT=""; NEWEST=""
|
|
70
|
+
for f in "$BACKUP_DIR"/frugal-iot-state-*-<->-<->.tar.gz(N); do
|
|
71
|
+
stamp=${${f:t}%.tar.gz}; stamp=${stamp[-15,-1]}
|
|
72
|
+
[[ "$stamp" > "$NEWEST" ]] && { NEWEST=$stamp; SNAPSHOT=$f; }
|
|
73
|
+
done
|
|
74
|
+
[[ -n "$SNAPSHOT" ]] || die "no snapshots in $BACKUP_DIR"
|
|
75
|
+
else
|
|
76
|
+
SNAPSHOT=${SNAPARG:A}
|
|
77
|
+
[[ -e "$SNAPSHOT" ]] || die "$SNAPARG does not exist"
|
|
78
|
+
fi
|
|
79
|
+
|
|
80
|
+
# ---- Which installation ----
|
|
81
|
+
# As frugal-iot-backup: the current directory if it holds a database, else the frugaliot service's.
|
|
82
|
+
# The package has to be installed already - restoring is not installing.
|
|
83
|
+
if [[ -f ./frugal-iot.db ]]; then
|
|
84
|
+
INSTALL_DIR=$PWD
|
|
85
|
+
else
|
|
86
|
+
INSTALL_DIR=""
|
|
87
|
+
have systemctl && { INSTALL_DIR=$(systemctl show -p WorkingDirectory --value frugaliot 2>/dev/null) || true; }
|
|
88
|
+
[[ -n "$INSTALL_DIR" && -f "$INSTALL_DIR/frugal-iot.db" ]] ||
|
|
89
|
+
die "no frugal-iot.db here - install the package and run 'npx --no frugal-iot-init' first, then run this from that directory"
|
|
90
|
+
fi
|
|
91
|
+
cd "$INSTALL_DIR"
|
|
92
|
+
have sqlite3 || die "sqlite3 command not found - install it with: sudo apt install sqlite3"
|
|
93
|
+
|
|
94
|
+
# ---- Root, before anything secret is unpacked ----
|
|
95
|
+
# Re-running under sudo replaces this process, and an EXIT trap does not fire on exec - so deciding
|
|
96
|
+
# after unpacking would leave a copy of every credential behind in /tmp.
|
|
97
|
+
if (( EUID != 0 )); then
|
|
98
|
+
if [[ $MODE != check ]] && have systemctl; then as_root "$@"; fi
|
|
99
|
+
[[ -r "$SNAPSHOT" ]] || as_root "$@"
|
|
100
|
+
fi
|
|
101
|
+
|
|
102
|
+
# Quoted: unquoted, the substitution is split into words and the %% applies to each
|
|
103
|
+
SERVICE_USER=${"$(ownmode frugal-iot.db)"%%:*}
|
|
104
|
+
SERVICE_GROUP=${${"$(ownmode frugal-iot.db)"#*:}%% *}
|
|
105
|
+
|
|
106
|
+
# ---- Unpack and check it ----
|
|
107
|
+
umask 077
|
|
108
|
+
WORK=$(mktemp -d "${TMPDIR:-/tmp}/frugal-iot-restore.XXXXXX")
|
|
109
|
+
trap 'rm -rf "$WORK"' EXIT
|
|
110
|
+
tar -xpzf "$SNAPSHOT" -C "$WORK" 2>/dev/null || die "$SNAPSHOT cannot be read as a snapshot"
|
|
111
|
+
MANIFEST="$WORK/MANIFEST"
|
|
112
|
+
[[ -f "$MANIFEST" ]] || die "$SNAPSHOT has no MANIFEST - it was not made by frugal-iot-backup"
|
|
113
|
+
mf() { awk -F'\t' -v k="$1" '$1 == k {print $2; exit}' "$MANIFEST"; }
|
|
114
|
+
[[ "$(mf format)" == 2 ]] ||
|
|
115
|
+
die "$SNAPSHOT was taken by a test version of frugal-iot-backup that did not record enough to restore it - take a new one"
|
|
116
|
+
|
|
117
|
+
SNAP_HOSTNAME=$(mf host)
|
|
118
|
+
SNAP_INSTALL=$(mf install_dir)
|
|
119
|
+
SNAP_CREATED=$(mf created)
|
|
120
|
+
SNAP_VERSION=$(mf frugal-iot-server)
|
|
121
|
+
HERE_VERSION=$(node -e "console.log(require('${SCRIPT:h:h}/package.json').version)" 2>/dev/null) || true
|
|
122
|
+
|
|
123
|
+
INTEGRITY=$(sqlite3 "$WORK/install/frugal-iot.db" 'PRAGMA integrity_check;' 2>/dev/null) || true
|
|
124
|
+
[[ "$INTEGRITY" == ok ]] || die "the database in the snapshot fails its integrity check: ${INTEGRITY:-unreadable}"
|
|
125
|
+
[[ -f "$WORK/install/config.d/secrets.yaml" ]] ||
|
|
126
|
+
die "the snapshot has no config.d/secrets.yaml - without it no broker password can be derived"
|
|
127
|
+
|
|
128
|
+
# Every file the MANIFEST lists, as parallel arrays
|
|
129
|
+
M_ROLE=(); M_OWNMODE=(); M_FROM=(); M_IN=()
|
|
130
|
+
while IFS=$'\t' read -r kind role om from in; do
|
|
131
|
+
[[ $kind == file ]] || continue
|
|
132
|
+
[[ -e "$WORK/$in" ]] || die "the MANIFEST lists $in but it is not in the snapshot"
|
|
133
|
+
M_ROLE+=("$role"); M_OWNMODE+=("$om"); M_FROM+=("$from"); M_IN+=("$in")
|
|
134
|
+
done < "$MANIFEST"
|
|
135
|
+
|
|
136
|
+
# ---- This machine's broker ----
|
|
137
|
+
source "${SCRIPT:h}/broker-config.zsh"
|
|
138
|
+
broker_conf_read
|
|
139
|
+
(( ${#BROKER_CONF_UNREADABLE} == 0 )) ||
|
|
140
|
+
die "cannot read ${(j:, :)BROKER_CONF_UNREADABLE}, so cannot tell where this broker keeps its files - run with sudo"
|
|
141
|
+
here_setting() { broker_setting "$1"; print -r -- "$BROKER_SETTING"; }
|
|
142
|
+
|
|
143
|
+
# ---- The plan ----
|
|
144
|
+
# P_SRC (in the unpacked snapshot) -> P_DST, owned and moded as P_OWNMODE. SKIPS says why not.
|
|
145
|
+
P_SRC=(); P_DST=(); P_OWNMODE=(); SKIPS=(); ASIDE=()
|
|
146
|
+
plan() { P_SRC+=("$1"); P_DST+=("$2"); P_OWNMODE+=("$3"); }
|
|
147
|
+
# Where a broker file goes keeps the owner and mode the file there has now - this machine's broker
|
|
148
|
+
# already reads it - and only takes the snapshot's when there is nothing there yet.
|
|
149
|
+
broker_ownmode() { if [[ -e "$1" ]]; then ownmode "$1"; else print -r -- "$2"; fi; }
|
|
150
|
+
|
|
151
|
+
for i in {1..${#M_ROLE}}; do
|
|
152
|
+
role=$M_ROLE[$i]; from=$M_FROM[$i]; src="$WORK/$M_IN[$i]"
|
|
153
|
+
case $role in
|
|
154
|
+
install)
|
|
155
|
+
rel=${M_IN[$i]#install/}
|
|
156
|
+
if [[ -d "$src" ]]; then
|
|
157
|
+
for f in "$src"/**/*(.N); do
|
|
158
|
+
frel="$rel/${f#$src/}"
|
|
159
|
+
if [[ $MODE != all && ( $frel == config.d/mqtt.yaml || $frel == config.d/server.yaml ) && -e "$INSTALL_DIR/$frel" ]]; then
|
|
160
|
+
SKIPS+=("$INSTALL_DIR/$frel - describes this machine (its broker address, port, paths); this one's is kept")
|
|
161
|
+
continue
|
|
162
|
+
fi
|
|
163
|
+
plan "$f" "$INSTALL_DIR/$frel" "$SERVICE_USER:$SERVICE_GROUP ${${"$(ownmode "$f")"}##* }"
|
|
164
|
+
done
|
|
165
|
+
else
|
|
166
|
+
plan "$src" "$INSTALL_DIR/$rel" "$SERVICE_USER:$SERVICE_GROUP ${M_OWNMODE[$i]##* }"
|
|
167
|
+
fi
|
|
168
|
+
;;
|
|
169
|
+
service|config|certfile|keyfile|cafile|capath|dhparamfile|crlfile)
|
|
170
|
+
if [[ $MODE == all ]]; then
|
|
171
|
+
plan "$src" "$from" "$M_OWNMODE[$i]"
|
|
172
|
+
else
|
|
173
|
+
case $role in
|
|
174
|
+
service) SKIPS+=("$from - this machine's own systemd unit is kept: it names this machine's paths and account") ;;
|
|
175
|
+
config) SKIPS+=("$from - this machine's own broker configuration is kept") ;;
|
|
176
|
+
*) SKIPS+=("$from - a TLS file for ${SNAP_HOSTNAME}'s name; this machine keeps its own") ;;
|
|
177
|
+
esac
|
|
178
|
+
fi
|
|
179
|
+
;;
|
|
180
|
+
password_file|acl_file|plugin_opt_config_file)
|
|
181
|
+
if [[ $MODE == all ]]; then
|
|
182
|
+
dst=$from
|
|
183
|
+
else
|
|
184
|
+
dst=$(here_setting "$role")
|
|
185
|
+
fi
|
|
186
|
+
if [[ -z "$dst" ]]; then
|
|
187
|
+
SKIPS+=("$from - this machine's broker has no $role setting, so nothing would read it")
|
|
188
|
+
else
|
|
189
|
+
plan "$src" "$dst" "$(broker_ownmode "$dst" "$M_OWNMODE[$i]")"
|
|
190
|
+
fi
|
|
191
|
+
;;
|
|
192
|
+
*)
|
|
193
|
+
SKIPS+=("$from - role '$role' is not one this version knows how to restore")
|
|
194
|
+
;;
|
|
195
|
+
esac
|
|
196
|
+
done
|
|
197
|
+
|
|
198
|
+
# "all": a broker configuration file here that the snapshot does not have would still be read by
|
|
199
|
+
# mosquitto alongside the restored ones - so move it aside, out of the *.conf pattern it reads
|
|
200
|
+
if [[ $MODE == all ]]; then
|
|
201
|
+
for f in ${BROKER_CONF_FILES[2,-1]}; do
|
|
202
|
+
(( ${M_FROM[(Ie)$f]} )) || ASIDE+=("$f")
|
|
203
|
+
done
|
|
204
|
+
fi
|
|
205
|
+
|
|
206
|
+
# An organization configured here that the snapshot does not have is left, and said so: its logger
|
|
207
|
+
# would keep starting, for an organization the restored database knows nothing about
|
|
208
|
+
EXTRA_ORGS=()
|
|
209
|
+
for f in "$INSTALL_DIR"/config.d/organizations/*.yaml(N); do
|
|
210
|
+
[[ -e "$WORK/install/config.d/organizations/${f:t}" ]] || EXTRA_ORGS+=("${f:t:r}")
|
|
211
|
+
done
|
|
212
|
+
|
|
213
|
+
# ---- Refusals ----
|
|
214
|
+
BLOCKERS=()
|
|
215
|
+
LOGINS_HERE=$(sqlite3 frugal-iot.db "SELECT count(*) FROM users WHERE id > 1;" 2>/dev/null) || LOGINS_HERE=0
|
|
216
|
+
if (( LOGINS_HERE > 0 && ! FORCE )); then
|
|
217
|
+
BLOCKERS+=("this installation already has ${LOGINS_HERE} login(s) of its own, which the restore would replace - add \"force\" if that is what you mean (a snapshot of them is taken first)")
|
|
218
|
+
fi
|
|
219
|
+
if [[ $MODE == all && "$SNAP_INSTALL" != "$INSTALL_DIR" ]]; then
|
|
220
|
+
BLOCKERS+=("the snapshot was installed at $SNAP_INSTALL and this one is at $INSTALL_DIR - \"all\" puts everything back exactly where it was, so use \"state\" here")
|
|
221
|
+
fi
|
|
222
|
+
if [[ -n "$HERE_VERSION" && -n "$SNAP_VERSION" && "$SNAP_VERSION" != "not installed here" ]] && ! is-at-least "$SNAP_VERSION" "$HERE_VERSION"; then
|
|
223
|
+
BLOCKERS+=("the snapshot is from frugal-iot-server $SNAP_VERSION and this is $HERE_VERSION - its database may have changes this version does not know. Upgrade here first")
|
|
224
|
+
fi
|
|
225
|
+
|
|
226
|
+
# ---- Say what it would do ----
|
|
227
|
+
echo "Snapshot: $SNAPSHOT"
|
|
228
|
+
echo " taken: $SNAP_CREATED on $SNAP_HOSTNAME, frugal-iot-server $SNAP_VERSION, from $SNAP_INSTALL"
|
|
229
|
+
echo "Restoring to: $INSTALL_DIR on $(hostname 2>/dev/null) (runs as $SERVICE_USER), frugal-iot-server ${HERE_VERSION:-unknown}"
|
|
230
|
+
echo "Mode: $MODE"
|
|
231
|
+
echo
|
|
232
|
+
echo "Would put back:"
|
|
233
|
+
for i in {1..${#P_DST}}; do
|
|
234
|
+
if [[ ! -e "$P_DST[$i]" ]]; then st="new"
|
|
235
|
+
elif cmp -s "$P_SRC[$i]" "$P_DST[$i]" 2>/dev/null; then st="unchanged"
|
|
236
|
+
elif [[ -r "$P_DST[$i]" ]]; then st="replaces what is there"
|
|
237
|
+
# check runs as the service user, which cannot read the broker's files - ask sudo before
|
|
238
|
+
# reporting an identical file as one that will be replaced
|
|
239
|
+
elif (( EUID != 0 )) && have sudo && sudo -n cmp -s "$P_SRC[$i]" "$P_DST[$i]" 2>/dev/null; then st="unchanged"
|
|
240
|
+
elif (( EUID != 0 )) && have sudo && sudo -n test -r "$P_DST[$i]" 2>/dev/null; then st="replaces what is there"
|
|
241
|
+
else st="replaces (needs root to compare)"
|
|
242
|
+
fi
|
|
243
|
+
printf ' %-60s %-32s %s\n' "$P_DST[$i]" "$st" "$P_OWNMODE[$i]"
|
|
244
|
+
done
|
|
245
|
+
if (( ${#ASIDE} )); then
|
|
246
|
+
echo
|
|
247
|
+
echo "Would move aside, as <name>.pre-restore, because the snapshot's broker does not have them:"
|
|
248
|
+
for f in $ASIDE; do echo " $f"; done
|
|
249
|
+
fi
|
|
250
|
+
if (( ${#SKIPS} )); then
|
|
251
|
+
echo
|
|
252
|
+
echo "Would leave alone:"
|
|
253
|
+
for s in $SKIPS; do echo " $s"; done
|
|
254
|
+
fi
|
|
255
|
+
(( ${#EXTRA_ORGS} )) && { echo; echo "Organizations configured here and not in the snapshot, left as they are: ${EXTRA_ORGS}"; }
|
|
256
|
+
echo
|
|
257
|
+
echo "Not in a snapshot: data/ - copy it back from its mirror, e.g. rsync -a <mirror>/data/ ${INSTALL_DIR}/data/"
|
|
258
|
+
|
|
259
|
+
if (( ${#BLOCKERS} )); then
|
|
260
|
+
echo
|
|
261
|
+
for b in $BLOCKERS; do echo "CANNOT: $b"; done
|
|
262
|
+
[[ $MODE == check ]] && exit 1
|
|
263
|
+
exit 1
|
|
264
|
+
fi
|
|
265
|
+
if [[ $MODE == check ]]; then
|
|
266
|
+
echo
|
|
267
|
+
echo "Nothing changed. To do it: npx --no frugal-iot-restore $SNAPARG state (or all - see the top of this script)"
|
|
268
|
+
exit 0
|
|
269
|
+
fi
|
|
270
|
+
|
|
271
|
+
# ---- Do it ----
|
|
272
|
+
echo
|
|
273
|
+
# Run something as the account the server runs as, in its directory - init and rebuild-dynsec write
|
|
274
|
+
# files there, which must not end up owned by root
|
|
275
|
+
run_as() {
|
|
276
|
+
if [[ "$(id -un)" == "$SERVICE_USER" ]]; then
|
|
277
|
+
( cd "$INSTALL_DIR" && "$@" )
|
|
278
|
+
else
|
|
279
|
+
sudo -u "$SERVICE_USER" -H -- zsh -c 'cd "$1" && shift && "$@"' _ "$INSTALL_DIR" "$@"
|
|
280
|
+
fi
|
|
281
|
+
}
|
|
282
|
+
unit_exists() { have systemctl && systemctl cat "$1" >/dev/null 2>&1; }
|
|
283
|
+
|
|
284
|
+
echo "1. A snapshot of what is here now, in case this is the wrong thing to have done"
|
|
285
|
+
PRE_RESTORE="$BACKUP_DIR/pre-restore"
|
|
286
|
+
if zsh "${SCRIPT:h}/backup.zsh" "$PRE_RESTORE"; then
|
|
287
|
+
# The one just written, so the way back can be named exactly rather than as "the newest"
|
|
288
|
+
PRE_RESTORE=( $PRE_RESTORE/frugal-iot-state-*.tar.gz(Nom[1]) )
|
|
289
|
+
else
|
|
290
|
+
(( FORCE )) || die "could not take it - add \"force\" to restore without a copy of what is here"
|
|
291
|
+
warn "carrying on without it, because of \"force\""
|
|
292
|
+
PRE_RESTORE=""
|
|
293
|
+
fi
|
|
294
|
+
|
|
295
|
+
echo "2. Stopping the server and the broker"
|
|
296
|
+
unit_exists frugaliot && systemctl stop frugaliot
|
|
297
|
+
# The plugin keeps its state in memory and rewrites its file on every change, so the broker must be
|
|
298
|
+
# down while that file is replaced - and it only reads it at startup anyway
|
|
299
|
+
unit_exists mosquitto && systemctl stop mosquitto
|
|
300
|
+
|
|
301
|
+
echo "3. Putting the files back"
|
|
302
|
+
for f in $ASIDE; do
|
|
303
|
+
mv -- "$f" "$f.pre-restore"
|
|
304
|
+
echo " moved aside $f"
|
|
305
|
+
done
|
|
306
|
+
for i in {1..${#P_DST}}; do
|
|
307
|
+
dst=$P_DST[$i]; own=${P_OWNMODE[$i]% *}; mode=${P_OWNMODE[$i]##* }
|
|
308
|
+
mkdir -p "${dst:h}"
|
|
309
|
+
if [[ -d "$P_SRC[$i]" ]]; then # capath
|
|
310
|
+
rm -rf -- "$dst"; cp -Rp -- "$P_SRC[$i]" "$dst"
|
|
311
|
+
else
|
|
312
|
+
cp -p -- "$P_SRC[$i]" "$dst.restoring"
|
|
313
|
+
mv -f -- "$dst.restoring" "$dst"
|
|
314
|
+
fi
|
|
315
|
+
chmod "$mode" "$dst" 2>/dev/null || warn "could not set mode $mode on $dst"
|
|
316
|
+
# The snapshot's owner may not exist here (production's "mitra" on a Pi) - then whoever owns the
|
|
317
|
+
# installation, which is the account that has to read it
|
|
318
|
+
if [[ "${"$(ownmode "$dst")"% *}" != "$own" ]]; then
|
|
319
|
+
chown "$own" "$dst" 2>/dev/null || chown "$SERVICE_USER:$SERVICE_GROUP" "$dst" 2>/dev/null ||
|
|
320
|
+
warn "could not set the owner of $dst to $own"
|
|
321
|
+
fi
|
|
322
|
+
echo " $dst"
|
|
323
|
+
done
|
|
324
|
+
[[ $MODE == all ]] && have systemctl && systemctl daemon-reload
|
|
325
|
+
|
|
326
|
+
echo "4. Starting the broker"
|
|
327
|
+
if unit_exists mosquitto; then
|
|
328
|
+
systemctl start mosquitto || true
|
|
329
|
+
for n in {1..10}; do
|
|
330
|
+
[[ "$(systemctl is-active mosquitto 2>/dev/null)" == active ]] && break
|
|
331
|
+
sleep 1
|
|
332
|
+
done
|
|
333
|
+
if [[ "$(systemctl is-active mosquitto 2>/dev/null)" != active ]]; then
|
|
334
|
+
echo "The broker did not start. The server has been left stopped." >&2
|
|
335
|
+
echo "See why: journalctl -u mosquitto -n 30 --no-pager (and its own log, named in its configuration)" >&2
|
|
336
|
+
[[ -n "$PRE_RESTORE" ]] && echo "Put it back: npx --no frugal-iot-restore $PRE_RESTORE all force" >&2
|
|
337
|
+
exit 1
|
|
338
|
+
fi
|
|
339
|
+
fi
|
|
340
|
+
|
|
341
|
+
echo "5. Bringing the database up to this release (frugal-iot-init)"
|
|
342
|
+
run_as zsh "${SCRIPT:h}/init.zsh" >/dev/null || warn "frugal-iot-init reported a problem - run it by hand to see it"
|
|
343
|
+
|
|
344
|
+
broker_conf_read
|
|
345
|
+
broker_setting plugin
|
|
346
|
+
if [[ -n "$BROKER_SETTING" ]]; then
|
|
347
|
+
echo "6. Making the broker's accounts match the database (frugal-iot-rebuild-dynsec)"
|
|
348
|
+
run_as node "${SCRIPT:h}/rebuild-dynsec.js" || warn "frugal-iot-rebuild-dynsec failed - run it by hand to see why"
|
|
349
|
+
else
|
|
350
|
+
echo "6. (no dynamic-security plugin on this broker - accounts come from its password file)"
|
|
351
|
+
fi
|
|
352
|
+
|
|
353
|
+
echo "7. Starting the server"
|
|
354
|
+
unit_exists frugaliot && systemctl start frugaliot
|
|
355
|
+
|
|
356
|
+
echo "8. Checking"
|
|
357
|
+
if [[ -n "$BROKER_SETTING" ]]; then
|
|
358
|
+
run_as node "${SCRIPT:h}/rebuild-dynsec.js" check || warn "the broker's accounts do not match the database - see above"
|
|
359
|
+
fi
|
|
360
|
+
if run_as zsh "${SCRIPT:h}/diagnostic.zsh" quiet; then
|
|
361
|
+
echo " the diagnostic finds nothing wrong"
|
|
362
|
+
else
|
|
363
|
+
warn "the diagnostic found something - run 'npx --no frugal-iot-diagnostic' for the whole report"
|
|
364
|
+
fi
|
|
365
|
+
|
|
366
|
+
echo
|
|
367
|
+
echo "Restored $SNAPSHOT."
|
|
368
|
+
if [[ -n "$PRE_RESTORE" ]]; then
|
|
369
|
+
echo "What was here before is in $PRE_RESTORE - to go back: npx --no frugal-iot-restore $PRE_RESTORE all force"
|
|
370
|
+
else
|
|
371
|
+
echo "Nothing was kept of what was here before."
|
|
372
|
+
fi
|
|
373
|
+
echo "data/ is not in a snapshot - copy it back from its mirror if this machine lost it."
|
|
374
|
+
(( ${#EXTRA_ORGS} )) && echo "Still configured here and not in the snapshot: ${EXTRA_ORGS} - remove config.d/organizations/<org>.yaml if it should not be."
|
|
375
|
+
exit 0
|