-
Notifications
You must be signed in to change notification settings - Fork 61
/
toltecctl
1032 lines (908 loc) · 27.1 KB
/
toltecctl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#!/usr/bin/env bash
# Copyright (c) 2021 The Toltec Contributors
# SPDX-License-Identifier: MIT
set -euo pipefail
# Path where Toltec resides (will be mounted to $toltec_dest)
toltec_src=/home/root/.entware
# Path where Toltec is mounted
toltec_dest=/opt
# Path where toltec caches compatibility information
toltec_share=/home/root/.local/share/toltec
# Path to static opkg build
opkg_path=/home/root/.local/bin/opkg
# Path to opkg install status file
opkg_status="/opt/lib/opkg/status"
# Path to Opkg configuration
opkg_conf="$toltec_src"/etc/opkg.conf
opkg_conf_dir="$toltec_src"/etc/opkg.conf.d
# Path to the Xochitl configuration
xochitl_conf=/home/root/.config/remarkable/xochitl.conf
ssh_key_file=/home/root/.ssh/authorized_keys
# Root of the remote Toltec server
toltec_srv_root=https://toltec-dev.org
# Start and end markers of Toltec-managed section in bashrc
bashrc_path=/home/root/.bashrc
bashrc_start_marker="# Added by Toltec bootstrap (do not modify!)"
bashrc_old_start_marker="# Path added by Toltec bootstrap"
bashrc_end_marker="# End of Toltec bootstrap additions"
# Print a log message
#
# Arguments:
#
# [$1] - Log level: INFO, WARN or ERROR (default: INFO)
# $2... - Messages to print, each argument goes to a separate line
log() {
# Output stream where the messages will be sent
local log_type="INFO"
local fd=1
local colored_prefix
if [[ $# -ge 2 ]]; then
case "$1" in
INFO | WARN | ERROR)
log_type="$1"
shift
;;
esac
fi
case "$log_type" in
INFO) colored_prefix='\e[32mINFO:\e[0m ' ;;
WARN)
colored_prefix='\e[33mWARN:\e[0m '
fd=2
;;
ERROR)
colored_prefix='\e[31mERROR:\e[0m '
fd=2
;;
esac
echo -e "${colored_prefix}$1" >&$fd
# Extra lines to print indented
shift
local line
for line in "$@"; do
echo -e " $line" >&$fd
done
}
# Identify which reMarkable model we’re currently running on
#
# Output: One of the following strings
#
# rm1 - for reMarkable 1
# rm2 - for reMarkable 2
# unknown
identify-model() {
local device_id
device_id="$(cat /sys/devices/soc0/machine)"
case $device_id in
"reMarkable 1.0" | "reMarkable Prototype 1")
echo "rm1"
;;
"reMarkable 2.0")
echo "rm2"
;;
*)
echo "unknown"
;;
esac
}
# Find the current OS release version
#
# Output: Current version in the X.Y.Z.P format
get-release-version() {
awk -F= '/RELEASE_VERSION/{print $2}' /usr/share/remarkable/update.conf
}
# Check to see if the version is supported and error if it isn't supported
#
# Arguments:
#
# $1 - Branch for which to check support
#
# Exit code:
#
# 0 - if the current version is supported
# 1 - if unsupported
# 2 - if version compatibility information could not be retrieved
# 3 - unable to install standalone wget
check-version() {
local wget
if [[ "$(install-state)" != "yes" ]] || [[ "$(command -v wget)" != "/opt/bin/wget" ]]; then
if ! install-standalone-wget; then
return 2
fi
wget="${toltec_share}/wget"
else
wget="wget"
fi
local check_branch
local current_model
local current_version
local cwd
check_branch="$1"
current_model="$(identify-model)"
current_version="$(get-release-version)"
cwd="$(pwd)"
mkdir -p "$toltec_share"
cd "$toltec_share"
if ! "$wget" "$toltec_srv_root"/"$check_branch"/Compatibility \
--timestamping \
--no-verbose \
--ignore-length 2> /dev/null; then
log ERROR "Unable to fetch updated version compatibility information, make sure you have a stable Wi-Fi connection"
if ! [ -f Compatibility ]; then
cd "$cwd"
return 2
fi
log WARN "Using outdated cached version compatibility information"
fi
cd "$cwd"
if ! grep -q "${current_model}=${current_version}" "${toltec_share}/Compatibility"; then
log ERROR "You’re running an unsupported OS version: $current_version"
log ERROR "Please monitor Toltec releases for upcoming support"
return 1
fi
return 0
}
# Get the password used to login to the SSH prompt
#
# Output: SSH password
get-ssh-password() {
awk -F= '/DeveloperPassword=/{ printf "%s" $2 }' "$xochitl_conf"
}
# Get the path for a systemd bind mount unit
#
# Arguments:
#
# $1 - Mount point
#
# Output:
#
# Path at which the definition of a bind mount on $1 should reside
get-bind-mount-path() {
echo "/lib/systemd/system/$(systemd-escape --path "$1").mount"
}
# Create or update a bind mount systemd unit and enable it
#
# Arguments:
#
# $1 - Source directory
# $2 - Mount point
add-bind-mount() {
local unit_path
local unit_name
unit_path="$(get-bind-mount-path "$2")"
unit_name="$(basename "$unit_path")"
if [[ -e $unit_path ]]; then
echo "Bind mount configuration for '$2' already exists, updating"
else
echo "Mounting '$1' over '$2'"
fi
cat > "$unit_path" << UNIT
[Unit]
Description=Bind mount $1 over $2
DefaultDependencies=no
Conflicts=umount.target
Before=local-fs.target umount.target
[Mount]
What=$1
Where=$2
Type=none
Options=bind
[Install]
WantedBy=local-fs.target
UNIT
systemctl daemon-reload
systemctl enable "$unit_name"
systemctl start "$unit_name"
}
# Disable and remove a bind mount systemd unit
#
# Arguments:
#
# $1 - Mount point
remove-bind-mount() {
local unit_path
local unit_name
unit_path="$(get-bind-mount-path "$1")"
unit_name="$(basename "$unit_path")"
if [[ ! -e $unit_path ]]; then
echo "No existing bind mount for '$1'"
return
fi
echo "Removing mount over '$1'"
systemctl disable "$unit_name"
systemctl stop "$unit_name"
if mountpoint -q "$1"; then
umount -l "$1"
fi
rm "$unit_path"
systemctl daemon-reload
}
# Escape a string for use in a sed search
#
# Arguments
#
# $1 - String to escape
#
# Output:
#
# Escaped string
escape-for-sed() {
# shellcheck disable=SC1003
sed -e 's/[^^]/[&]/g; s/\^/\\^/g; $!a\'$'\n''\\n' <<< "$1" | tr -d '\n'
}
# Escape a string for use in a sed replacement
#
# Arguments
#
# $1 - String to escape
#
# Output:
#
# Escaped string
escape-for-sed-replace() {
IFS= read -d '' -r < <(sed -e ':a' -e '$!{N;ba' -e '}' -e 's/[&/\]/\\&/g; s/\n/\\&/g' <<< "$1")
printf %s "${REPLY%$'\n'}"
}
# Replace multiline string content in stdin
#
# Arguments:
#
# $1 - String to replace
# $2 - string to replace with
# stdin - Text to perform replacement on
#
# Output:
#
# Contents of stdin with the string replaced
replace-string() {
sed \
-e ':a' \
-e '$!{N;ba' -e '}' \
-e "s/$(escape-for-sed "$1")/$(escape-for-sed-replace "$2")/"
}
# Get the opkg status for a package
#
# Arguments:
#
# $1 - Package name
#
# Output: Opkg status information for the package
#
# Exit Code:
#
# 0 - Status found
# 1 - Status not found
get-package-status() {
local status
status="$(opkg status "$1" | grep -v "has no valid architecture, ignoring" || true)"
if [ -n "$status" ]; then
echo "$status"
return 0
fi
local index
local index2
local count
# Don't use --line-number so this works on the built in grep
index="$(grep -n "Package: ${1}" "$opkg_status" | cut -d':' -f1 || echo "-1")"
if [ "$index" -eq -1 ]; then
return 1
fi
# Don't use --line-number so this works on the built in grep
count="$(tail -n +$((index + 1)) "$opkg_status" | grep -n "Package: " | head -n1 | cut -d':' -f1 || echo "-1")"
if [ "$count" -eq -1 ]; then
return 1
fi
index2=$((index + count - 1))
status="$(sed -n "${index},${index2}p;$((index2 + 1))q" "$opkg_status")"
if [ -z "$status" ]; then
return 1
fi
echo "$status"
return 0
}
# Check if a string appears in a set of strings
#
# Arguments:
#
# $1 - String to look for
# $2... - Elements to compare to $1
#
# Example:
#
# > has-element "needle" "${haystack[@]}"
# Will return 0 if "needle" is found in the "haystack" array.
#
# Exit code: 0 if the element is found, 1 otherwise.
has-element() {
local needle="$1"
shift
for element; do
if [[ $needle == "$element" ]]; then
return 0
fi
done
return 1
}
# Update the opkg status for a package with the correct architecture for the device
#
# Arguments:
#
# $1 - Package name
#
# Exit Codes:
#
# 0 - Package status was updated
# 1 - Package status was not modified
# 2 - Package status was not found
# 3 - Package status has no architecture
update-package-arch() {
if ! grep -q "Package: ${1}" "$opkg_status"; then
return 1
fi
local arch
local model
local new_status
local version
local status
local rc=0
status="$(get-package-status "$1" || true)"
if [ -z "$status" ]; then
log WARN "Unable to get status for package: ${1}"
return 2
fi
arch="$(echo "$status" | grep "Architecture:" | awk '{print $2}')"
if [ -z "$arch" ]; then
return 3
fi
case "$arch" in
rmallos2 | rmallos3)
version="$(identify-support-version)"
new_status="$(echo "$status" | replace-string "Architecture: ${arch}" "Architecture: rmall${version}")"
;;
rm1os2 | rm1os3 | rm2os2 | rm2os3)
model="$(identify-model)"
version="$(identify-support-version)"
new_status="$(echo "$status" | replace-string "Architecture: ${arch}" "Architecture: ${model}${version}")"
;;
*)
new_status="$status"
;;
esac
if [[ "$status" == "$new_status" ]]; then
return 1
fi
echo "Updating package architecture for package: ${1}"
sed -i \
-e ':a' \
-e '$!{N;ba' -e '}' \
-e "s/$(escape-for-sed "$status")/$(escape-for-sed-replace "$new_status")/" \
"$opkg_status"
return 0
}
# Reinstall toltec-base and toltec-deletions
reinstall-base() {
local packages=()
while read -r pkg; do
if update-package-arch "$pkg"; then
packages+=("$pkg")
fi
done < <(opkg list-installed | grep -v "has no valid architecture, ignoring" | cut -d' ' -f1 || true)
while read -r pkg; do
if update-package-arch "$pkg"; then
packages+=("$pkg")
fi
done < <(opkg list-installed | grep "has no valid architecture, ignoring" | cut -d' ' -f2 || true)
if ! has-element "toltec-base" "${packages[@]}"; then
packages+=(toltec-base)
fi
if ! has-element "toltec-deletions" "${packages[@]}"; then
packages+=(toltec-deletions)
fi
opkg install --force-reinstall "${packages[@]}" || true
}
# Reinstall all Toltec packages that had files installed outside of
# $toltec_src, e.g. systemd configuration files
reinstall-root() {
# Get the list of installed packages with files on root
local pkgname
declare -A on_root_packages
while read -r inst_line; do
pkgname="$(echo "$inst_line" | awk '{ print $1 }')"
if opkg files "$pkgname" | grep -v -e "/home/root" -e "$toltec_dest" \
-e "is installed on root" -q; then
on_root_packages[$pkgname]=1
fi
done < <(opkg list-installed)
# Filter the list to keep only packages that can be installed
declare -A reinstall_packages
while read -r pkgname; do
if [[ -v "on_root_packages[$pkgname]" ]]; then
reinstall_packages[$pkgname]=1
fi
done < <(gunzip -c /opt/var/opkg-lists/* | grep "^Package:" | awk '{print $2}')
# Workaround: Checking the size of an empty array when the nounset option
# is active may throw an error on some Bash versions, so we disable it
# temporarily
set +u
if [[ ${#reinstall_packages[@]} -ne 0 ]]; then
opkg install --force-reinstall --force-remove "${!reinstall_packages[@]}" || true
else
echo "No package needs to be reinstalled"
fi
set -u
}
# Remove all PATH definitions for /opt in bashrc
clean-path() {
if [[ -f $bashrc_path ]]; then
sed -i "/^$bashrc_start_marker\$/,/^$bashrc_end_marker\$/d" "$bashrc_path"
sed -i "/^$bashrc_old_start_marker\$/!b;n;d" "$bashrc_path"
sed -i "/^$bashrc_old_start_marker\$/d" "$bashrc_path"
sed -i '/^\(export \)\?PATH="\?\.*\/opt\/bin:\/opt\/sbin.*"\?$/d' "$bashrc_path"
fi
}
# Add/update PATH declarations in bashrc to include Toltec binaries
set-path() {
local old_path
old_path="$(bash -l -c "echo \$PATH")"
clean-path
cat >> "$bashrc_path" << SHELL
$bashrc_start_marker
PATH="/opt/bin:/opt/sbin:/home/root/.local/bin:\$PATH"
$bashrc_end_marker
SHELL
# Warn user if PATH contents have changed
local new_path
new_path="$(bash -l -c "echo \$PATH")"
if [[ $old_path != "$new_path" ]]; then
log WARN "Please restart your SSH session or run 'exec bash --login' to use Toltec"
fi
}
# Print a file with its comments and blank lines removed
#
# Arguments:
#
# $1 - Path to the file
#
# Output: Cleaned file contents
clean-file-comments() {
sed -e '/^#/d' -e '/^$/d' "$1"
}
# Rebuild the Opkg configuration file from split config files
#
# Exit code:
#
# 0 - If the configuration file has changed (in which case
# `opkg update` needs to be run)
# 1 - If there were no changes
generate-opkg-conf() {
local old_opkg_conf
old_opkg_conf="$(mktemp)"
if [[ -f $opkg_conf ]]; then
mv "$opkg_conf" "$old_opkg_conf"
else
touch "$old_opkg_conf"
fi
cat > "$opkg_conf" << CONF
# Opkg configuration
# Generated by toltecctl (do not modify!)
# Define custom configuration in files under the '$opkg_conf_dir' directory
# then run \`toltecctl generate-opkg-conf\` to regenerate this file
dest root /
dest ram /opt/tmp
lists_dir ext /opt/var/opkg-lists
option tmp_dir /opt/tmp
CONF
for conf in "$opkg_conf_dir"/*; do
echo "# $conf"
clean-file-comments "$conf"
echo
done >> "$opkg_conf"
! diff <(clean-file-comments "$old_opkg_conf" | sort) \
<(clean-file-comments "$opkg_conf" | sort) > /dev/null
}
# Create the Opkg configuration file for fetching Entware packages
create-entware-conf() {
mkdir -p "$opkg_conf_dir"
cat > "$opkg_conf_dir"/10-entware.conf << CONF
# Entware repository configuration
# Managed by Toltec (do not modify!)
# Define custom configuration in other files in this directory
# then run \`toltecctl generate-opkg-conf\` to regenerate '$opkg_conf'
arch all 100
arch armv7-3.2 160
src/gz entware https://bin.entware.net/armv7sf-k3.2
CONF
}
# Get the current Toltec branch of this installation
#
# Output: Current branch name
get-branch() {
if ! [ -f "$opkg_conf_dir/15-toltec.conf" ]; then
return
fi
local srv_prefix_len=${#toltec_srv_root}
local srv_prefix="${toltec_srv_root//\//\\/}"
local awk_get_branch="
match(\$0, /$srv_prefix\/(.+)\/rmall/) {
print substr( \
\$0, \
RSTART+$srv_prefix_len+1, \
RLENGTH-$srv_prefix_len-1-6 \
)
}
"
awk "$awk_get_branch" "$opkg_conf_dir/15-toltec.conf" | head -n1 || true
}
# Compare two version numbers
#
# Arguments:
#
# $1 - Version number to compare
# $2 - Version number to compare
#
# Output: True if the first number is larger than the second
compare-versions() {
[ "$(echo -e "${1}\n${2}" | sort | head -n1)" != "$1" ]
}
# Identify which toltec version this should be limited to
#
# Output: One of the following strings
#
# os2 - 2.x OS version
# os3 - 3.x OS version
# unknown
identify-support-version() {
local current_version
current_version="$(get-release-version)"
if compare-versions "2.0" "$current_version"; then
echo "unknown"
elif compare-versions "3.0" "$current_version"; then
echo "os2"
elif compare-versions "4.0" "$current_version"; then
echo "os3"
else
echo "unknown"
fi
}
# Set the Toltec branch for this installation
# (generate-opkg-conf must be run afterwards to rebuild the main config file)
#
# Arguments:
#
# $1 - Name of the branch to use (either stable or testing)
switch-branch() {
local branch="${1:-stable}"
local model
local version
model="$(identify-model)"
version="$(identify-support-version)"
if [[ $model = "unknown" ]]; then
log ERROR "You’re running an unsupported or unrecognised device"
exit 1
fi
if [[ $version = "unknown" ]]; then
log ERROR "You're running an unsupported or unrecognised OS version"
exit 1
fi
mkdir -p "$opkg_conf_dir"
cat >| "$opkg_conf_dir"/15-toltec.conf << CONF
# Toltec repository configuration
# Generated by toltecctl (do not modify!)
# Use \`toltecctl switch-branch [stable|testing]\` to
# switch to a different Toltec branch
arch rmall 200
src/gz toltec-rmall ${toltec_srv_root}/${branch}/rmall
arch ${model} 250
src/gz toltec-${model} ${toltec_srv_root}/${branch}/${model}
arch rmall${version} 260
src/gz toltec-rmall$version ${toltec_srv_root}/${branch}/rmall${version}
arch ${model}os2 270
arch ${model}os3 270
src/gz toltec-${model}${version} ${toltec_srv_root}/${branch}/${model}${version}
CONF
}
# Re-enable Toltec install after system update
reenable() {
log INFO "Mounting /opt"
add-bind-mount "$toltec_src" "$toltec_dest"
switch-branch "$(get-branch)"
log INFO "Generating /opt/etc/opkg.conf"
generate-opkg-conf || true
log INFO "Opkg update"
opkg update
log INFO "Reinsalling base packages"
reinstall-base
log INFO "Reinstalling packages with files on the root partition"
reinstall-root
if [ -d /opt/share/toltec/reenable.d ]; then
find /opt/share/toltec/reenable.d -maxdepth 1 -mindepth 1 -print0 \
| xargs -0rn1 basename \
| while read -r pkg; do
local script
script="/opt/lib/opkg/info/${pkg}.postinst"
if [ -f "$script" ]; then
log INFO "Reconfiguring ${pkg}"
"$script" configure || true
fi
done
fi
log INFO "Configuring bashrc"
set-path
}
# List all installed packages such that any package comes before
# its dependencies. Dependency cycles are broken arbitrarily
list-installed-ordered() {
# shellcheck disable=SC2016
local awk_list_to_graph='
/^.* depends on:$/{
from=$1;
print from " " from;
}
/^\t/{
print from " " $1;
}
'
opkg depends '*' | awk "$awk_list_to_graph" | tsort 2> /dev/null || true
# tsort reports errors if there are dependency cycles, we ignore them
}
# Install standalone opkg binary
install-standalone-opkg() {
local wget
if [[ "$(install-state)" != "yes" ]] || [[ "$(command -v wget)" != "/opt/bin/wget" ]]; then
if ! install-standalone-wget; then
return 2
fi
wget="${toltec_share}/wget"
else
wget="wget"
fi
local opkg_remote=https://bin.entware.net/armv7sf-k3.2/installer/opkg
if ! "$wget" --no-verbose "$opkg_remote" --output-document "$opkg_path"; then
log ERROR "Unable to fetch standalone opkg, make sure you have a stable Wi-Fi connection"
return 1
fi
chmod u+x "$opkg_path"
if [[ $(command -v opkg) != "$opkg_path" ]]; then
export PATH
PATH="$(dirname "$opkg_path"):$PATH)"
fi
}
# Install a local wget binary which supports TLS (the original one
# installed on the reMarkable does not) in the PATH
install-standalone-wget() {
local wget_path="${toltec_share}/wget"
local wget_remote=http://toltec-dev.org/thirdparty/bin/wget-v1.21.1-1
local wget_checksum=c258140f059d16d24503c62c1fdf747ca843fe4ba8fcd464a6e6bda8c3bbb6b5
if [ -f "$wget_path" ] && ! [[ -e $wget_path ]] || ! sha256sum -c <(echo "$wget_checksum $wget_path") > /dev/null 2>&1; then
rm "$wget_path"
fi
if ! [ -f "$wget_path" ]; then
log INFO "Fetching secure wget"
# Download and compare to hash
mkdir -p "$(dirname "$wget_path")"
if ! wget -q "$wget_remote" --output-document "$wget_path"; then
log ERROR "Could not fetch wget, make sure you have a stable Wi-Fi connection"
exit 1
fi
if ! sha256sum -c <(echo "$wget_checksum $wget_path") > /dev/null 2>&1; then
log ERROR "Invalid checksum for the local wget binary"
exit 1
fi
chmod 755 "$wget_path"
fi
}
# Remove Toltec completely
uninstall() {
# Fetch standalone opkg used to uninstall packages
if ! install-standalone-opkg; then
return 1
fi
# Remove installed packages in reverse dependency order
list-installed-ordered | while read -r pkgname; do
"$opkg_path" remove --force-removal-of-essential-packages --force-depends --force-remove "$pkgname"
done
systemctl daemon-reload
rm -f "$opkg_path"
# Remove mount point
remove-bind-mount "$toltec_dest"
rmdir "$toltec_dest"
# Unset PATH
clean-path
# Remove Toltec data
rm -r "$toltec_src"
# Re-enable xochitl if needed
systemctl enable xochitl
}
# The current toltec install state
#
# Output:
#
# yes - opt.mount is enabled and active
# partial - opt.mount is active but not enabled
# no - opt.mount is not enabled, or does not exist
install-state() {
local unit_name
local enabled
unit_name="$(systemd-escape --path "$toltec_dest").mount"
if ! systemctl --quiet is-enabled "$unit_name" 2> /dev/null; then
echo "no"
elif systemctl --quiet is-active "$unit_name"2 > /dev/null; then
echo "partial"
else
echo "yes"
fi
}
# Output toltec installation status
status() {
echo -ne "Enabled: \033[1m"
local enabled=false
case "$(install-state)" in
yes)
enabled=true
echo -ne "\e[32mYes"
;;
no)
echo -ne "\e[31mNo"
;;
partial)
echo -ne "\e[33mPartial"
;;
*)
echo -ne "\e[31mUnknown"
;;
esac
echo -e "\e[0m"
echo -ne "Supported: \033[1m"
local rc
rc=0
check-version "$(get-branch)" &> /dev/null || rc=$?
if [ $rc -eq 0 ]; then
echo -ne "\e[32mYes"
elif [ $rc -eq 2 ] || [ $rc -eq 3 ]; then
echo -ne "\e[33mUnknown"
else
echo -ne "\e[31mNo"
fi
echo -e "\e[0m"
echo -n "Branch: "
if [ -f "$opkg_conf" ]; then
# shellcheck disable=SC2005
echo "$(get-branch)"
else
echo -e "\033[1m\e[33mUnknown\e[0m"
fi
echo -n "Model: "
identify-model
echo -n "OS: "
get-release-version
}
help() {
echo "Usage: $(basename "$0") COMMAND
Manage your Toltec install. Available commands:
help Show this help message.
generate-opkg-conf Rebuild the Opkg configuration file.
switch-branch [BRANCH] Change the current branch to BRANCH.
reenable Re-enable Toltec after a system update.
uninstall Permanently remove Toltec."
}
if [[ $0 = "${BASH_SOURCE[0]}" ]]; then
if [[ $# -eq 0 ]]; then
help
exit 1
fi
action="$1"
shift
case $action in
generate-opkg-conf)
no_update=$({ (($# > 0)) && [[ $1 = "--no-update" || $1 = "-n" ]] && echo 1; } || true)
if generate-opkg-conf && [[ -z $no_update ]]; then
read -p "Your Opkg configuration changed. Reload it? [Y/n] " -n 1 -r
echo
if [[ $REPLY =~ ^[Nn]$ ]]; then
log WARN "Not reloading. Use 'opkg update' to do it manually."
else
opkg update
fi
fi
;;
switch-branch)
target_branch=stable
force=
while (($#)); do
if [[ $1 = "--force" || $1 = "-f" ]]; then
force=1
else
target_branch="$1"
fi
shift
done
if [[ -z $force ]] && ! wget --quiet --spider "$toltec_srv_root/$target_branch"; then
cat << MSG
The '$target_branch' branch does not seem to exist. This may happen if you made
a typo in the branch name or if you have no Internet access.
MSG
read -p "Continue anyway? [y/N] " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
log WARN "Canceled"
exit 1
fi
fi
if ! check-version "$target_branch"; then
if [[ -z $force ]]; then
exit 1
fi
fi
if [[ -z $force ]] && [[ $target_branch = "testing" ]]; then
cat << MSG
Using packages from the testing branch may cause breakage or data loss.
Please make sure you read the following before switching to testing:
<https://github.com/toltec-dev/toltec/blob/stable/docs/branches.md>
MSG
read -p "Continue? [y/N] " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
log WARN "Canceled"
exit 1
fi
fi
switch-branch "$target_branch"
if generate-opkg-conf; then
if ((force == 0)); then
read -p "Your Opkg configuration changed. Reload it? [Y/n] " -n 1 -r
echo
if [[ $REPLY =~ ^[Nn]$ ]]; then
log WARN "Not reloading. Use 'opkg update' to do it manually."
exit
fi
fi
opkg update
fi
;;
reenable)
force=$({ (($# > 0)) && [[ $1 = "--force" || $1 = "-f" ]] && echo 1; } || true)
if ! check-version "$(get-branch)"; then
if [[ -z $force ]]; then
exit 1
fi
fi
reenable
;;
uninstall)
force=$({ (($# > 0)) && [[ $1 = "--force" || $1 = "-f" ]] && echo 1; } || true)
if [[ -z $force ]]; then
read -p "This will wipe out all files in '$toltec_dest'. Continue? [y/N] " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
log WARN "Canceled"
exit 1
fi
fi
uninstall
if [[ -z $force ]]; then