frugal-iot-server 2.0.0 → 2.0.2
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/INSTALLATION.md +149 -128
- package/README.md +3 -0
- package/frugal-iot-createdb.sql +34 -1
- package/frugal-iot-server.js +56 -4
- package/lib/dynsec-plan.js +24 -0
- package/lib/dynsec-server.js +34 -1
- package/lib/enrol.js +132 -29
- package/package.json +7 -4
- package/public/service-worker.js +1 -1
- package/scripts/addbridge-prod.zsh +48 -19
- package/scripts/addorganization.zsh +25 -5
- package/scripts/check-schema.js +24 -4
- package/scripts/copy-schema-to-examples.zsh +18 -3
- package/scripts/diagnostic.zsh +149 -27
- package/scripts/init.zsh +106 -6
- package/scripts/install-pi.sh +269 -44
- package/scripts/prerelease.zsh +84 -33
package/scripts/diagnostic.zsh
CHANGED
|
@@ -10,10 +10,40 @@
|
|
|
10
10
|
#
|
|
11
11
|
# Paste the whole output into a bug report. Passwords are deliberately not printed: the password
|
|
12
12
|
# file is reported by account name only, and organization configs by name only.
|
|
13
|
-
|
|
13
|
+
#
|
|
14
|
+
# QUIET MODE says nothing at all unless something is wrong, which is what makes it usable as a
|
|
15
|
+
# periodic check rather than only as something to read when you already have a problem:
|
|
16
|
+
# npx --no frugal-iot-diagnostic quiet # positional, see below
|
|
17
|
+
# zsh diagnostic.zsh -q
|
|
18
|
+
#
|
|
19
|
+
# The word "quiet" with no dash is not a typo. npm parses the whole command line itself and passes
|
|
20
|
+
# only the values through, so a flag never reaches a script run by npx - and "-q" is worse than
|
|
21
|
+
# most, because it is npm's OWN abbreviation for --quiet, so npm consumes it and goes quiet itself.
|
|
22
|
+
# "npx --no frugal-iot-diagnostic -- --quiet" also works, if you would rather remember that.
|
|
23
|
+
#
|
|
24
|
+
# Exit status, either mode:
|
|
25
|
+
# 0 nothing found
|
|
26
|
+
# 1 problems found - they are listed, and are the only thing a quiet run prints
|
|
27
|
+
#
|
|
14
28
|
# No "set -e": a failing check should report and carry on, not stop the script.
|
|
15
29
|
setopt no_unset 2>/dev/null
|
|
16
30
|
|
|
31
|
+
QUIET=0
|
|
32
|
+
for arg in "$@"; do
|
|
33
|
+
case "$arg" in
|
|
34
|
+
-q|--quiet|quiet) QUIET=1 ;;
|
|
35
|
+
-h|--help|help) sed -n '3,28p' "$0" | sed -e 's/^#$//' -e 's/^# //'; exit 0 ;;
|
|
36
|
+
*) print -u2 "Unknown argument: $arg (try \"quiet\", or \"help\")"; exit 2 ;;
|
|
37
|
+
esac
|
|
38
|
+
done
|
|
39
|
+
|
|
40
|
+
# Keep the real stdout on fd 3 for the summary, which prints in both modes. Under -q everything
|
|
41
|
+
# else - including whatever the commands being run write - goes nowhere, so a quiet run that
|
|
42
|
+
# prints anything is a quiet run that found something. stderr goes too: the noise it carries is
|
|
43
|
+
# things like sudo declining, which the checks below already turn into findings of their own.
|
|
44
|
+
exec 3>&1
|
|
45
|
+
(( QUIET )) && exec 1>/dev/null 2>/dev/null
|
|
46
|
+
|
|
17
47
|
section() { print -r -- ""; print -r -- "===== $* ====="; }
|
|
18
48
|
item() { print -r -- " $*"; }
|
|
19
49
|
have() { command -v "$1" >/dev/null 2>&1; }
|
|
@@ -115,9 +145,33 @@ for f in /etc/mosquitto/conf.d/*(N); do
|
|
|
115
145
|
done
|
|
116
146
|
[[ -z "$(print -r -- /etc/mosquitto/conf.d/*(N))" ]] && problem "Nothing in /etc/mosquitto/conf.d - the Frugal IoT config was never copied there (step 5)"
|
|
117
147
|
|
|
148
|
+
# The files the broker actually reads. mosquitto's include_dir only loads names ending in .conf,
|
|
149
|
+
# so look at exactly those - and the last setting wins, hence "tail -1" everywhere below.
|
|
150
|
+
BROKER_CONF_FILES=(/etc/mosquitto/mosquitto.conf(N) /etc/mosquitto/conf.d/*.conf(N))
|
|
151
|
+
|
|
152
|
+
# Everything the broker reads, as one stream - with sudo if reading it plainly gives nothing. A
|
|
153
|
+
# file under /etc can be readable only by root, and an ordinary user then gets silence rather than
|
|
154
|
+
# an error, which is indistinguishable from "that setting is not configured". Left unguarded, the
|
|
155
|
+
# comparison below reported a broker as missing EVERY setting this release ships, which as a
|
|
156
|
+
# periodic check is worse than no check at all.
|
|
157
|
+
broker_conf_text() {
|
|
158
|
+
local t
|
|
159
|
+
(( ${#BROKER_CONF_FILES} )) || return 0
|
|
160
|
+
t=$(cat "${BROKER_CONF_FILES[@]}" 2>/dev/null) || true
|
|
161
|
+
if [[ -z "$t" ]] && have sudo; then
|
|
162
|
+
t=$(sudo -n cat "${BROKER_CONF_FILES[@]}" 2>/dev/null) || true
|
|
163
|
+
fi
|
|
164
|
+
print -r -- "$t"
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
# Where the packaged copy of a shipped file is. What is in ./extras is NOT it: frugal-iot-init
|
|
168
|
+
# copies extras/ in and then never overwrites it, so once a release changes one of these files the
|
|
169
|
+
# local copy is a fossil. Checks below read the package.
|
|
170
|
+
PKG_EXTRAS="node_modules/frugal-iot-server/extras"
|
|
171
|
+
[[ -d "$PKG_EXTRAS" ]] || PKG_EXTRAS="${0:A:h:h}/extras"
|
|
172
|
+
|
|
118
173
|
# Which password file does the running configuration actually name?
|
|
119
|
-
|
|
120
|
-
CONFIGURED_PWFILE=$(grep -hE '^[[:space:]]*password_file[[:space:]]' /etc/mosquitto/mosquitto.conf /etc/mosquitto/conf.d/*.conf(N) 2>/dev/null | tail -1 | awk '{print $2}')
|
|
174
|
+
CONFIGURED_PWFILE=$(broker_conf_text | grep -hE '^[[:space:]]*password_file[[:space:]]' | tail -1 | awk '{print $2}')
|
|
121
175
|
section "Mosquitto password file"
|
|
122
176
|
if [[ -n "$CONFIGURED_PWFILE" ]]; then
|
|
123
177
|
item "configuration names: $CONFIGURED_PWFILE"
|
|
@@ -133,12 +187,35 @@ else
|
|
|
133
187
|
item "no password_file line found in any mosquitto configuration"
|
|
134
188
|
problem "No password_file configured - the broker would allow anonymous access or reject everything"
|
|
135
189
|
fi
|
|
136
|
-
#
|
|
137
|
-
#
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
190
|
+
# What this release ships, against what the broker is actually running.
|
|
191
|
+
#
|
|
192
|
+
# This used to be "diff ./extras/mosquitto.conf /etc/mosquitto/conf.d/frugal-iot.conf", which
|
|
193
|
+
# reported a problem on every machine there has ever been, for three separate reasons: ./extras is
|
|
194
|
+
# a fossil frugal-iot-init will not overwrite, so it differs from the release for ever; the
|
|
195
|
+
# installed copy has the dynamic-security plugin's path substituted, so it differs from the shipped
|
|
196
|
+
# one by design; and "diff -q" against a frugal-iot.conf that does not exist fails too, which is
|
|
197
|
+
# the normal state of a broker whose configuration was assembled by hand as several small files in
|
|
198
|
+
# conf.d. A check that is always wrong is one you stop reading, so this asks the question that
|
|
199
|
+
# actually matters instead: is the broker missing a setting this release ships?
|
|
200
|
+
if [[ -f "${PKG_EXTRAS}/mosquitto.conf" ]]; then
|
|
201
|
+
item "this release's mosquitto.conf names: $(grep -hE '^[[:space:]]*password_file' "${PKG_EXTRAS}/mosquitto.conf" 2>/dev/null | awk '{print $2}')"
|
|
202
|
+
if (( ${#BROKER_CONF_FILES} )); then
|
|
203
|
+
# The plugin line is normalised: the installer substitutes a path that varies by distribution.
|
|
204
|
+
# Comparing settings rather than files means this works whether the configuration arrived as one
|
|
205
|
+
# frugal-iot.conf or as several hand-written pieces.
|
|
206
|
+
MISSING_CONF=$(comm -23 \
|
|
207
|
+
<(sed -E 's#^plugin .*#plugin -#' "${PKG_EXTRAS}/mosquitto.conf" | grep -vE '^[[:space:]]*(#|$)' | sort -u) \
|
|
208
|
+
<(broker_conf_text | sed -E 's#^plugin .*#plugin -#' | grep -vE '^[[:space:]]*(#|$)' | sort -u) 2>/dev/null)
|
|
209
|
+
# password_file and the listeners are allowed to be sited differently on an older installation,
|
|
210
|
+
# so a difference in those is reported without being called a fault.
|
|
211
|
+
MISSING_REAL=$(print -r -- "$MISSING_CONF" | grep -vE '^[[:space:]]*(password_file|listener|protocol)[[:space:]]' || true)
|
|
212
|
+
if [[ -n "$MISSING_CONF" ]]; then
|
|
213
|
+
item "settings this release ships that the broker does not have:"
|
|
214
|
+
print -r -- "$MISSING_CONF" | while IFS= read -r l; do [[ -n "$l" ]] && item " $l"; done
|
|
215
|
+
else
|
|
216
|
+
item "the broker has every setting this release ships"
|
|
217
|
+
fi
|
|
218
|
+
[[ -n "$MISSING_REAL" ]] && problem "The broker is missing $(print -r -- "$MISSING_REAL" | grep -c .) setting(s) this release ships - see \"Mosquitto password file\" above. Re-run install-pi.sh, or add them by hand if this broker's config.d was written by hand."
|
|
142
219
|
fi
|
|
143
220
|
fi
|
|
144
221
|
item "candidate locations, whether or not configured:"
|
|
@@ -270,15 +347,10 @@ section "Broker access control"
|
|
|
270
347
|
# An acl_file confines each account to its own organization's topics; without one, every account
|
|
271
348
|
# that can log in reaches every topic on the broker. Reported here because a broker installed before
|
|
272
349
|
# the ACL shipped will not have one, and nothing else says so.
|
|
273
|
-
BROKER_CONF_FILES=(/etc/mosquitto/mosquitto.conf /etc/mosquitto/conf.d/*.conf(N))
|
|
274
350
|
BROKER_SETTING=""
|
|
275
351
|
broker_setting() { # name -> value from the broker's own configuration, sudo only if needed
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
if [[ -z "$v" ]] && have sudo; then
|
|
279
|
-
v=$(sudo -n grep -hE "^[[:space:]]*$1[[:space:]]" "${BROKER_CONF_FILES[@]}" 2>/dev/null | tail -1 | awk '{print $2}') || true
|
|
280
|
-
fi
|
|
281
|
-
BROKER_SETTING="$v"
|
|
352
|
+
# broker_conf_text carries the sudo fallback this used to spell out for itself
|
|
353
|
+
BROKER_SETTING=$(broker_conf_text | grep -hE "^[[:space:]]*$1[[:space:]]" | tail -1 | awk '{print $2}') || true
|
|
282
354
|
}
|
|
283
355
|
broker_setting acl_file; ACLFILE="$BROKER_SETTING"
|
|
284
356
|
if [[ -z "$ACLFILE" ]]; then
|
|
@@ -317,11 +389,45 @@ else
|
|
|
317
389
|
fi
|
|
318
390
|
fi
|
|
319
391
|
# Can the server actually drive it? That needs the admin credential frugal-iot-init recorded.
|
|
320
|
-
|
|
321
|
-
|
|
392
|
+
#
|
|
393
|
+
# Read with a sudo fallback, and report the file's ownership, because the two ways this goes wrong
|
|
394
|
+
# look identical from here: the setting really is absent, or the file belongs to somebody else and
|
|
395
|
+
# an ordinary reader gets silence. The second is the one that matters, since the SERVER is an
|
|
396
|
+
# ordinary reader too - it runs as the account in the unit file, and a secrets.yaml it cannot read
|
|
397
|
+
# leaves it with no credential to hand a browser, whatever is written inside.
|
|
398
|
+
item " secrets file: config.d/secrets.yaml $(fileinfo config.d/secrets.yaml)"
|
|
399
|
+
SERVICE_USER=$(sed -n 's/^User=//p' /etc/systemd/system/frugaliot.service 2>/dev/null | head -1)
|
|
400
|
+
[[ -z "$SERVICE_USER" ]] && SERVICE_USER=$(sudo -n sed -n 's/^User=//p' /etc/systemd/system/frugaliot.service 2>/dev/null | head -1)
|
|
401
|
+
secret_value() { # name -> its value, with sudo if reading it plainly gives nothing
|
|
402
|
+
local v
|
|
403
|
+
v=$(sed -n "s/^$1:[[:space:]]*//p" config.d/secrets.yaml 2>/dev/null | tr -d "\"'" | head -1)
|
|
404
|
+
if [[ -z "$v" ]] && have sudo; then
|
|
405
|
+
v=$(sudo -n sed -n "s/^$1:[[:space:]]*//p" config.d/secrets.yaml 2>/dev/null | tr -d "\"'" | head -1)
|
|
406
|
+
fi
|
|
407
|
+
print -r -- "$v"
|
|
408
|
+
}
|
|
409
|
+
if [[ -f config.d/secrets.yaml && -n "$SERVICE_USER" ]] && have stat; then
|
|
410
|
+
SECRETS_OWNER=$(stat -c '%U' config.d/secrets.yaml 2>/dev/null || true)
|
|
411
|
+
if [[ -n "$SECRETS_OWNER" && "$SECRETS_OWNER" != "$SERVICE_USER" && "$SECRETS_OWNER" != root ]]; then
|
|
412
|
+
problem "config.d/secrets.yaml belongs to ${SECRETS_OWNER} but the server runs as ${SERVICE_USER}, so the server cannot read it. Fix with: sudo chown ${SERVICE_USER} config.d/secrets.yaml"
|
|
413
|
+
elif [[ "$SECRETS_OWNER" == root && "$SERVICE_USER" != root ]]; then
|
|
414
|
+
problem "config.d/secrets.yaml belongs to root but the server runs as ${SERVICE_USER}, so the server reads nothing from it - every login is told live data is unavailable. Fix with: sudo chown ${SERVICE_USER} config.d/secrets.yaml && sudo systemctl restart frugaliot"
|
|
415
|
+
fi
|
|
416
|
+
fi
|
|
417
|
+
# Without this a login gets no broker credential at all, whatever else is right - which shows up
|
|
418
|
+
# in the dashboard as "No broker credential for this login".
|
|
419
|
+
USERSECRET=$(secret_value user_secret)
|
|
420
|
+
if [[ -z "$USERSECRET" ]]; then
|
|
421
|
+
item " user_secret: MISSING - no login can be given a broker credential"
|
|
422
|
+
problem "config.d/secrets.yaml has no user_secret, so every login is told \"No broker credential for this login\". The server writes one at startup; if it cannot, it says so in its log."
|
|
423
|
+
else
|
|
424
|
+
item " user_secret: present (each login's broker password is derived from it)"
|
|
425
|
+
fi
|
|
426
|
+
DYNUSER=$(secret_value dynsec_admin_user)
|
|
427
|
+
DYNPW=$(secret_value dynsec_admin_password)
|
|
322
428
|
if [[ -z "$DYNUSER" || -z "$DYNPW" ]]; then
|
|
323
|
-
item " admin credential: not in config.d/secrets.yaml - the server cannot create accounts"
|
|
324
|
-
problem "dynamic security is enabled but config.d/secrets.yaml has no dynsec_admin_user/password"
|
|
429
|
+
item " admin credential: not readable in config.d/secrets.yaml - the server cannot create accounts"
|
|
430
|
+
problem "dynamic security is enabled but config.d/secrets.yaml has no readable dynsec_admin_user/password, so no login gets a broker credential"
|
|
325
431
|
elif have mosquitto_ctrl; then
|
|
326
432
|
if DYNOUT=$(mosquitto_ctrl -h localhost -u "$DYNUSER" -P "$DYNPW" dynsec listClients 2>&1 | grep -viE 'without encryption|visible on the network'); then
|
|
327
433
|
item " admin credential works ($(print -r -- $DYNOUT | tr '\n' ' ' | cut -c1-60))"
|
|
@@ -515,8 +621,11 @@ if [[ -f config.d/server.yaml ]]; then
|
|
|
515
621
|
MORGANSET=$(sed -n 's/^morgan:[[:space:]]*//p' config.d/server.yaml 2>/dev/null | head -1)
|
|
516
622
|
item "server morgan: ${MORGANSET:-not set, so on - a line logged per HTTP request}"
|
|
517
623
|
fi
|
|
518
|
-
|
|
519
|
-
|
|
624
|
+
# Across every file the broker reads, not just frugal-iot.conf: a configuration assembled by hand
|
|
625
|
+
# puts this wherever it likes, and looking in one file meant a broker that logs every connect and
|
|
626
|
+
# disconnect was never reported as doing so.
|
|
627
|
+
if (( ${#BROKER_CONF_FILES} )); then
|
|
628
|
+
if broker_conf_text | grep -qhE '^[[:space:]]*connection_messages[[:space:]]+false'; then
|
|
520
629
|
item "mosquitto connections: not logged"
|
|
521
630
|
else
|
|
522
631
|
item "mosquitto connections: logged - every connect and disconnect is a write"
|
|
@@ -539,10 +648,23 @@ if [[ -d data ]]; then
|
|
|
539
648
|
[[ -n "$OLDEST" ]] && item " oldest day: ${OLDEST}"
|
|
540
649
|
fi
|
|
541
650
|
|
|
542
|
-
|
|
651
|
+
# The summary prints to fd 3, which is the real stdout whether or not the rest was silenced. A
|
|
652
|
+
# quiet run with nothing to report prints nothing at all, which is the point of it.
|
|
543
653
|
if (( ${#PROBLEMS} == 0 )); then
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
654
|
+
if (( ! QUIET )); then
|
|
655
|
+
print -r -- "" >&3
|
|
656
|
+
print -r -- "===== Summary =====" >&3
|
|
657
|
+
print -r -- " No problems detected by these checks." >&3
|
|
658
|
+
print -r -- "" >&3
|
|
659
|
+
fi
|
|
660
|
+
exit 0
|
|
661
|
+
fi
|
|
662
|
+
print -r -- "" >&3
|
|
663
|
+
print -r -- "===== Summary: ${#PROBLEMS} problem(s) =====" >&3
|
|
664
|
+
for p in $PROBLEMS; do print -r -- " PROBLEM: $p" >&3; done
|
|
665
|
+
if (( QUIET )); then
|
|
666
|
+
print -r -- "" >&3
|
|
667
|
+
print -r -- " Run it again without \"quiet\" for the whole report." >&3
|
|
547
668
|
fi
|
|
548
|
-
print -r -- ""
|
|
669
|
+
print -r -- "" >&3
|
|
670
|
+
exit 1
|
package/scripts/init.zsh
CHANGED
|
@@ -63,10 +63,16 @@ done
|
|
|
63
63
|
for f in "${PKG}"/config.d/schema/*.yaml(N); do
|
|
64
64
|
copy_if_missing "$f" "./config.d/schema/${f:t}" compare
|
|
65
65
|
done
|
|
66
|
-
# Copied so that mosquitto.conf and frugaliot.service can be
|
|
67
|
-
#
|
|
66
|
+
# Copied so that mosquitto.conf and frugaliot.service can be there to read and install from.
|
|
67
|
+
#
|
|
68
|
+
# NOT compared against the packaged copy. This copy is never overwritten - that is the whole point
|
|
69
|
+
# of copy_if_missing - so once a release changes one of these it differs for ever, and says so at
|
|
70
|
+
# every upgrade whether or not anything is actually out of date. A warning that is always wrong is
|
|
71
|
+
# one you learn to skip past, and then miss on the day it is right. What matters is not whether
|
|
72
|
+
# this copy is current but whether the file actually in use is, so that is what is checked, below,
|
|
73
|
+
# where it really lives.
|
|
68
74
|
for f in "${PKG}"/extras/*(N); do
|
|
69
|
-
copy_if_missing "$f" "./extras/${f:t}"
|
|
75
|
+
copy_if_missing "$f" "./extras/${f:t}"
|
|
70
76
|
done
|
|
71
77
|
|
|
72
78
|
# ---- 1b. Secrets this server generates for itself ----
|
|
@@ -182,6 +188,74 @@ else
|
|
|
182
188
|
echo " kept ${DB} permissions.project (already present)"
|
|
183
189
|
fi
|
|
184
190
|
|
|
191
|
+
# ---- 4. Is what was installed FROM extras/ still current? ----
|
|
192
|
+
# The files in extras/ are templates for files that live somewhere else - the broker's
|
|
193
|
+
# configuration, its ACL, the systemd unit. Whether this directory's copy matches the release says
|
|
194
|
+
# nothing about any of them, so the question is asked where the file actually is.
|
|
195
|
+
#
|
|
196
|
+
# Each of these is checked in the way that file is allowed to differ, because two of them are
|
|
197
|
+
# *supposed* to differ and a plain cmp would cry wolf at every upgrade:
|
|
198
|
+
# - mosquitto.conf carries a placeholder for the dynamic security plugin, which the installer
|
|
199
|
+
# replaces with a path that varies by distribution.
|
|
200
|
+
# - aclfile gets a stanza appended per bridge by addbridge-prod, so the installed file is
|
|
201
|
+
# expected to be a superset - only a line this release ships and it lacks is a finding.
|
|
202
|
+
STALE=() # installed, and older than this release
|
|
203
|
+
UNINSTALLED=() # nothing at the usual place, so there was nothing to compare
|
|
204
|
+
UNREADABLE=() # there, but not readable without sudo
|
|
205
|
+
|
|
206
|
+
# check_installed <packaged file> <installed path> <exact|plugin|subset>
|
|
207
|
+
check_installed() {
|
|
208
|
+
local from=$1 to=$2 how=$3 a b missing
|
|
209
|
+
[[ -e "$from" ]] || return 0
|
|
210
|
+
if [[ ! -e "$to" ]]; then
|
|
211
|
+
UNINSTALLED+=("${to}|${from}")
|
|
212
|
+
return 0
|
|
213
|
+
fi
|
|
214
|
+
if [[ ! -r "$to" ]]; then
|
|
215
|
+
UNREADABLE+=("$to")
|
|
216
|
+
return 0
|
|
217
|
+
fi
|
|
218
|
+
# Both sides through the same filter, so only a real difference survives. Note the "#"
|
|
219
|
+
# delimiter: an alternation inside an "s|...|" would end the expression at its first branch, and
|
|
220
|
+
# the "|| true" below would then hide the error and report the two files as identical.
|
|
221
|
+
local norm
|
|
222
|
+
case "$how" in
|
|
223
|
+
plugin) norm='s#^plugin .*#plugin -#' ;;
|
|
224
|
+
# The installer rewrites User, WorkingDirectory and ExecStart for whoever runs it and wherever
|
|
225
|
+
# the server went, so those three are normalised - comparing them exactly would report every
|
|
226
|
+
# installation that is not user "pi" in /home/pi/frugal-iot as out of date at every upgrade.
|
|
227
|
+
service) norm='s#^(User|WorkingDirectory|ExecStart)=.*#\1=-#' ;;
|
|
228
|
+
*) norm='' ;;
|
|
229
|
+
esac
|
|
230
|
+
|
|
231
|
+
case "$how" in
|
|
232
|
+
plugin|service)
|
|
233
|
+
a=$(sed -E "$norm" "$from" | grep -vE '^[[:space:]]*(#|$)' | sort || true)
|
|
234
|
+
b=$(sed -E "$norm" "$to" | grep -vE '^[[:space:]]*(#|$)' | sort || true)
|
|
235
|
+
# Neither of these files is ever empty once the comments are gone. Both blank means the
|
|
236
|
+
# filter failed rather than that they agree, and reporting "current" on that would be the
|
|
237
|
+
# worst answer of the three.
|
|
238
|
+
if [[ -z "$a" || -z "$b" ]]; then
|
|
239
|
+
UNREADABLE+=("$to")
|
|
240
|
+
elif [[ "$a" != "$b" ]]; then
|
|
241
|
+
STALE+=("${to}|${from}")
|
|
242
|
+
fi
|
|
243
|
+
;;
|
|
244
|
+
subset)
|
|
245
|
+
missing=$(comm -23 <(grep -vE '^[[:space:]]*(#|$)' "$from" | sort -u || true) \
|
|
246
|
+
<(grep -vE '^[[:space:]]*(#|$)' "$to" | sort -u || true) || true)
|
|
247
|
+
[[ -z "$missing" ]] || STALE+=("${to}|${from}")
|
|
248
|
+
;;
|
|
249
|
+
*)
|
|
250
|
+
cmp -s "$from" "$to" || STALE+=("${to}|${from}")
|
|
251
|
+
;;
|
|
252
|
+
esac
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
check_installed "${PKG}/extras/mosquitto.conf" /etc/mosquitto/conf.d/frugal-iot.conf plugin
|
|
256
|
+
check_installed "${PKG}/extras/aclfile" /etc/mosquitto/aclfile subset
|
|
257
|
+
check_installed "${PKG}/extras/frugaliot.service" /etc/systemd/system/frugaliot.service service
|
|
258
|
+
|
|
185
259
|
if (( ${#DIFFER_TO} )); then
|
|
186
260
|
echo
|
|
187
261
|
echo "These files were left as you have them, but this release ships a different version:"
|
|
@@ -189,9 +263,35 @@ if (( ${#DIFFER_TO} )); then
|
|
|
189
263
|
echo " ${DIFFER_TO[$i]}"
|
|
190
264
|
echo " compare with: diff ${DIFFER_TO[$i]} ${DIFFER_FROM[$i]}"
|
|
191
265
|
done
|
|
192
|
-
echo "
|
|
193
|
-
echo "
|
|
194
|
-
echo "
|
|
266
|
+
echo "The schema describes the sensor types the software understands. If you added one of your"
|
|
267
|
+
echo "own that is exactly as it should be; if you did not, this release changed it and the"
|
|
268
|
+
echo "difference is worth reading."
|
|
269
|
+
fi
|
|
270
|
+
|
|
271
|
+
if (( ${#STALE} )); then
|
|
272
|
+
echo
|
|
273
|
+
echo "Installed elsewhere from extras/, and older than this release:"
|
|
274
|
+
for e in $STALE; do
|
|
275
|
+
echo " ${e%%|*}"
|
|
276
|
+
echo " compare with: sudo diff ${e%%|*} ${e#*|}"
|
|
277
|
+
done
|
|
278
|
+
echo "Re-running the installer brings these up to date and is the safe way to do it - it"
|
|
279
|
+
echo "substitutes the plugin path and will not discard ACL rules added per bridge:"
|
|
280
|
+
echo " bash node_modules/frugal-iot-server/scripts/install-pi.sh"
|
|
281
|
+
fi
|
|
282
|
+
|
|
283
|
+
if (( ${#UNINSTALLED} )); then
|
|
284
|
+
echo
|
|
285
|
+
echo "Nothing at the usual place, so these were not compared:"
|
|
286
|
+
for e in $UNINSTALLED; do echo " ${e%%|*} (would come from extras/${${e#*|}:t})"; done
|
|
287
|
+
echo "Either install-pi.sh has not been run here, or this machine's broker configuration was put"
|
|
288
|
+
echo "together by hand under another name - several small files in /etc/mosquitto/conf.d is a"
|
|
289
|
+
echo "normal way to have done it. Nothing is out of date; there is simply nothing to compare."
|
|
290
|
+
fi
|
|
291
|
+
|
|
292
|
+
if (( ${#UNREADABLE} )); then
|
|
293
|
+
echo
|
|
294
|
+
echo "There, but not readable as $(id -un), so not compared: ${(j:, :)UNREADABLE}"
|
|
195
295
|
fi
|
|
196
296
|
|
|
197
297
|
echo
|