forked from kwindrem/SetupHelper
-
Notifications
You must be signed in to change notification settings - Fork 0
/
CommonResources
executable file
·622 lines (551 loc) · 20.7 KB
/
CommonResources
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
# CommonResources for SetupHelper
# contains a functions and variables necessary for a setup script to interface with reinstallMods
#
# Refer to the SetupHelper ReadMe file for details on how to use these resources.
######## skip to bottom of file for code executed when script is called
setupHelperDir="/data/SetupHelper"
source "$setupHelperDir/EssentialResources"
source "$setupHelperDir/LogHandler"
source "$setupHelperDir/ServiceResources"
source "$setupHelperDir/UpdateResources"
source "$setupHelperDir/DbusSettingsResources"
# what action the script should take:
# NONE - do noting - signals script to prompt for user input on how to proceed
# INSTALL - install package components
# (decommissioned) PROMPT - prompt user for additional installation options
# UNINSTALL - remove package components
# (decommissioned) EXIT - exit script without taking any action (now exits in-line)
# CommonResources may set the the action if initial checks
# indicate a clear direction
# otherwise, the action will be set based on user input (in the script)
# if failures occur during installation,
# scriptAction should be changed to UNINSTALL so the installation can be cleaned up
# and the setup script should test for UNINSTALL after it attempts installation
# A file set error indicates the file set for the current verion is not usable
# and installation should not occur
# checkFileSets EXITS locally
scriptAction='NONE'
# flags to control setup script exit behavior
rebootNeeded=false
runAgain=false
filesUpdated=false
restartGui=false
# yesNoPrompt provides user prompting requesting a yes/no response
#
# $1 is the prompt displayed when pausing for user input
#
# $yesResponse is set to true if the response was yes
yesNoPrompt ()
{
response=''
while true; do
/bin/echo -n "$*"
read response
case $response in
[yY]*)
yesResponse=true
break
;;
[nN]*)
yesResponse=false
break
;;
*)
esac
done
}
# standardActionPrompt provides the standard set of options for selecting script's action
# scriptAction is set by install/uninstall
# other actions are handled locally, including quitting from the script
#
# if nonstandard prompts are necessary, duplicate this code in the setup script
# and add the additional options and do not call standardActionPrompt
#
# the reinstall option is permitted only if setup options were previously set
# if the the reinstall action is choosen, the script action is set to INSTALL
# the setup script can then test this to skip further prompts
#
# $1 indicates if there are additional prompts needed during installaiton
# if this parameter is 'MORE_PROMPTS', installaiton does NOT change scriptAction
# if this parameter does not exist, installation WILL change scriptAction to install
# this provides backaward compatibility with scripts written prior to the reinstall logic
#
standardActionPrompt ()
{
if [ -f "$setupOptionsDir/optionsSet" ]; then
enableReinstall=true
else
enableReinstall=false
fi
if [ $# -gt 0 ] && [ $1 == 'MORE_PROMPTS' ]; then
updateScriptAction=false
else
updateScriptAction=true
fi
echo
echo "Available actions:"
echo " Install and activate (i)"
if $enableReinstall ; then
echo " Reinstall (r) based on options provided at last install"
fi
echo " Uninstall (u) and restores all files to stock"
echo " Quit (q) without further action"
echo " Display setup log (s) outputs the last 100 lines of the log"
if [ ! -z $packageLogFile ]; then
echo " Display Log (l) outputs the last 100 lines of the log"
fi
echo
response=''
while true; do
/bin/echo -n "Choose an action from the list above: "
read response
case $response in
[iI]*)
if $updateScriptAction ; then
scriptAction='INSTALL'
fi
break
;;
[rR]*)
if $enableReinstall ; then
scriptAction='INSTALL'
break
fi
;;
[uU]*)
scriptAction='UNINSTALL'
break
;;
[qQ]*)
exit
;;
[lL]*)
displayLog $packageLogFile
;;
[sS]*)
displayLog $setupLogFile
;;
*)
esac
done
}
# backupActiveFile makes a copy of the active file in file.orig
# if the original file does not exist, no backup is made
# BUT sets a flag file that will cause restoreFile to delete the active copy
#
# $1 is the full path/file name to be backed up
backupActiveFile ()
{
backupExisted=false
baseName=$(basename $1)
if [ -e "$1.orig" ] || [ -e "$1.NO_ORIG" ]; then
mayHaveBeenUpdated=true
elif [ ! -f "$1" ]; then
touch "$1.NO_ORIG"
else
cp "$1" "$1.orig"
rm -f "$1.NO_ORIG"
fi
}
# updateActiveFile first backs up the active file
# then copies the replacement (aka source) to the active file location (aka destination)
#
# two variations:
#
# updateActiveFile activeFile
# an attempt is made to locate the source (replacement)
# in the version directory or FileSets
#
# updateActiveFile sourceFile activeFile
# a separate source (replacement) file is specified
# either as a full path to the actual file
# or as the basename of a file to be found in the version directory or FileSets
#
# if the update fails, scriptAction is changed to UNINSTALL
updateActiveFile ()
{
sourceFound=false
thisFileUpdated=false
sourceFile="$1"
# separate replacement file specified
if [ $# == 2 ]; then
if [ -f "$sourceFile" ]; then
sourceFound=true
fi
destinationFile="$2"
# use active file for both source and destination
else
destinationFile="$1"
fi
# look for source in FileSets
if ! $sourceFound ; then
sourceFile="$(basename "$sourceFile")"
# found in version directory
if [ -f "$fileSet/$sourceFile" ]; then
sourceFile="$fileSet/$sourceFile"
sourceFound=true
elif [ -f "$pkgFileSets/$sourceFile" ]; then
sourceFile="$pkgFileSets/$sourceFile"
sourceFound=true
fi
fi
if ! $sourceFound; then
# replacement files are not be needed for some versions
# if so marked, leave original untouched
if [ -f "$fileSet/$(basename $sourceFile).USE_ORIGINAL" ]; then
return
# if not flagged, this is a fatal error
else
logMessage "ERROR: no replacement file for $sourceFile"
thisFileUpdated=false
scriptAction='UNINSTALL'
fi
return
fi
mayHaveBeenUpdated=false
backupActiveFile "$destinationFile"
# package may already have been installed - check to see
needToUpdate=true
if $mayHaveBeenUpdated ; then
cmp -s "$sourceFile" "$destinationFile" > /dev/null
# already updated - no change to active file
if [ $? == 0 ]; then
needToUpdate=false
fi
fi
if $needToUpdate ; then
cp "$sourceFile" "$destinationFile"
filesUpdated=true
thisFileUpdated=true
fi
}
# restoreActiveFile moves the backup copy to the active location
# if the backup copy doesn't exist BUT the NO_ORIG flag is set
# the active copy is deleted to restore the system to stock
# $1 is the active name, the one to be backed up
restoreActiveFile ()
{
thisFileUpdated=false
baseName="$(basename $1)"
if [ -e "$1.orig" ]; then
mv "$1.orig" "$1"
rm -f "$1.NO_ORIG"
filesUpdated=true
thisFileUpdated=true
elif [ -f "$1.NO_ORIG" ]; then
rm -f "$1"
rm -f "$1.NO_ORIG"
filesUpdated=true
thisFileUpdated=true
fi
}
# checkFileSets validates the file sets used install package modifications
#
# It attempts to create a file set for a new Venus version
# If the new active files for the new version all match another version
# the new file set is populated automatically and may be used with no further action
# If not, new version is marked for manual editing (NO_REPLACEMENT)
# and scriptAction is set to EXIT so incompatible files are not installed
#
# Replacement files that have no original specify an "alternate original" that is used
# for version comparisons that locate an appropriate replacement
# dummy routine for backward compatibility
# the actual work is now done in-line when CommonResources is sourced
checkFileSets ()
{
return
}
_checkFileSets ()
{
# skip checks if uninstalling
if [ $scriptAction == 'UNINSTALL' ];then
return
fi
# nothing to do if there is no fileList (version-dependent files)
if [ ! -f "$pkgFileSets/fileList" ]; then
return
fi
# attempt to create file set if it doesn't exist
if [ ! -d "$fileSet" ]; then
logMessage "creating file set for $venusVersion"
mkdir "$fileSet"
fi
local fileList=$(cat "$pkgFileSets/fileList")
local versionList=($(ls -d "$pkgFileSets"/v*))
local activeFile=""
local baseName=""
local file=""
rm -f "$pkgFileSets/INCOMPLETE"
for file in $fileList ; do
baseName=$(basename "$file")
activeFile=$file
if [ -f "$pkgFileSets/$baseName.ALT_ORIG" ]; then
activeFile=$(cat "$pkgFileSets/$baseName.ALT_ORIG")
fi
# package already installed, use .orig file for comparisons
if [ -f "$activeFile.orig" ]; then
activeFile="$activeFile.orig"
fi
# can't process if no Venus file
if [ ! -f "$activeFile" ]; then
logMessage "WARNING $venusVersion $baseName no active file"
touch "$fileSet/$baseName.NO_ACTIVE_FILE"
continue
fi
# skip checks if replacement file already exists in file set
# or if there is no replacement file for this version
if [ -f "$fileSet/$baseName" ] || [ -f "$fileSet/$baseName.USE_ORIGINAL" ]; then
rm -f "$fileSet/$baseName.NO_REPLACEMENT"
continue
fi
# if an active file exists
# find the LATEST match in PREVOUS file sets
if [ ! -z "$activeFile" ]; then
# look for a match in another version
matchFound=false
# search the version list backwards so the first match is the latest one
local i=${#versionList[@]}
while (( i > 0 )); do
(( i-- ))
otherVersion=$(basename ${versionList[i]})
versionStringToNumber $otherVersion
otherVersionDir="$pkgFileSets/$otherVersion"
otherFile="$pkgFileSets/$otherVersion/$baseName"
# skip future versions
if (( $versionNumber > $venusVersionNumber )); then
continue
fi
# skip symbolic links and nonexistent originals
if [ ! -f "$otherFile.orig" ] || [ -L "$otherFile.orig" ] ; then
continue
fi
cmp -s "$activeFile" "$otherFile.orig" > /dev/null
# files match
if [ $? -eq 0 ]; then
matchFound=true
break
fi
done
if $matchFound ;then
ln -s "../$otherVersion/$baseName.orig" "$fileSet/$baseName.orig"
rm -f "$fileSet/$baseName.NO_ORIG"
# if other file set contains a replacement file, link to it
if [ -f "$otherFile" ]; then
ln -s "../$otherVersion/$baseName" "$fileSet/$baseName"
rm -f "$fileSet/$baseName.NO_REPLACEMENT"
rm -f "$fileSet/$baseName.USE_ORIGINAL"
# if other file set does not contain a replacement, this one will not either
# this IS permitted and handled in the updateActiveFile and restoreActiveFile functions
elif [ -f "$otherFile.USE_ORIGINAL" ]; then
touch "$fileSet/$baseName.USE_ORIGINAL"
rm -f "$fileSet/$baseName.NO_REPLACEMENT"
fi
# no match to a previous verison - can't create file set automatically
# but copy original file to aid manual editing
else
logMessage "ERROR $venusVersion $baseName no replacement file"
cp "$activeFile" "$fileSet/$baseName.orig"
touch "$fileSet/$baseName.NO_REPLACEMENT"
touch "$fileSet/INCOMPLETE"
fi
fi
done
if [ -f "$fileSet/INCOMPLETE" ]; then
logMessage "ERROR: incomplete file set for $venusVersion - can't continue"
exit
fi
}
# determine how startup script should exit based on $scriptAction and other flags
# may EXIT or REBOOT within the function - DOES NOT RETURN TO CALLER
endScript ()
{
if $restartGui $$ !rebootNeeded ; then
logMessage "restarting GUI"
svc -t /service/gui
fi
if [ $scriptAction == 'INSTALL' ] ; then
# indicate installation was successful, so reinstalls can use the existing options in the future
touch "$setupOptionsDir/optionsSet"
# set up reinstallMods to run this script again after a VenusOS update
if [ ! -f "$reinstallScriptsList" ] || [ $(grep -c "$fullScriptName" "$reinstallScriptsList") == 0 ]; then
logMessage "adding $shortScriptName" to $(basename "$reinstallScriptsList")
echo "$fullScriptName" "$reinstallParam" >> "$reinstallScriptsList"
fi
if [ ! -f "$rcLocal" ]; then
logMessage "creating $rcLocal"
cat "$setupHelperDir/rcS.local" > "$rcLocal"
chmod +x "$rcLocal"
elif [ $(grep -c "SetupHelper" "$rcLocal") == 0 ]; then
logMessage "adding SetupHelper reinstall script to $rcLocal"
sed -e '1d' "$setupHelperDir/rcS.local" >> $rcLocal
# update reinstall call to nohup background
elif [ $(grep -c "nohup /data/SetupHelper" "$rcLocal") == 0 ]; then
logMessage "updating SetupHelper reinstall script in $rcLocal"
sed -i -e 's?/data/SetupHelper?nohup /data/SetupHelper?' -e 's?reinstallMods?reinstallMods > /dev/null \&?' "$rcLocal"
fi
# installed flag is removed if script needs to run again
if $runAgain ; then
logMessage "script will run again at startup"
rm -f "$installedFlag"
# otherwise installed flag is set so script won't be run again at boot
else
cp "$scriptDir/version" "$installedFlag"
fi
# add package to packageList if not already there and the GitHub paths have been specified
if [ ! -z $packageGitHubUser ] && [ ! -z $packageGitHubBranch ]; then
# move from previous locaiton
oldPackageListFile="$setupOptionsRoot/SetupHelper/packageList"
if [ -f "$oldPackageListFile" ]; then
mv "$oldPackageListFile" "$packageListFile"
fi
if [ ! -f "$packageListFile" ] || [ $(grep -c "^$packageName\s" "$packageListFile") == 0 ]; then
logMessage "adding $packageName to SetupHelper package list"
echo "$packageName $packageGitHubUser $packageGitHubBranch" >> "$packageListFile"
fi
fi
# update package version for the gui - takes time so do in background
nohup "/data/SetupHelper/updatePackageVersions" > /dev/null &
elif [ $scriptAction == 'UNINSTALL' ] ; then
# remove this script from reinstallScriptsList to prevent further calls during boot
if [ -f "$reinstallScriptsList" ] && [ ! $(grep -c "$fullScriptName" "$reinstallScriptsList") == 0 ]; then
logMessage removing "$shortScriptName" from $(basename "$reinstallScriptsList")
sed "\:$fullScriptName:d" "$reinstallScriptsList" > "$scriptDir/tmp"
mv "$scriptDir/tmp" "$reinstallScriptsList"
fi
# clean up only - flag not used since package is being removed
rm -f "$installedFlag"
# update package version for the gui - takes time so do in background
nohup "/data/SetupHelper/updatePackageVersions" > /dev/null &
fi
# this script was called from reinstallMods
# set exit code based on actual code
if $runningAtBoot ; then
if $rebootNeeded ; then
logMessage "reboot pending"
exit $exitReboot
else
logMessage "completed"
exit $exitSuccess
fi
# this script was run manually
else
# if reboot needed ask user if it should be done now
if $rebootNeeded ; then
if $deferReboot ; then
exit $exitReboot
else
yesNoPrompt "Reboot system now (y) or issue a reboot manually later (n): "
if $yesResponse ; then
echo "rebooting ..."
reboot
else
echo "system must be rebooted to finish installation and activate components"
fi
fi
elif $runAgain ; then
echo "$shortScriptName NOT completed"
echo " run it again manually to finish"
echo " or reboot the system to finish automatically"
exit $exitSuccess
else
logMessage "completed"
exit $exitSuccess
fi
fi
}
######## this code is executed in-line when CommonResources is sourced
# check for reinstall parameter
# set $scriptAction to control work following the source command
# if "force" is also provided on the command line, then the installedFlag is not checked
# installedFlag contains the installed version (if any)
# it is compared to the version file in the package directory
# if installedFlag is missing or contents are different, the installation will proceed
# if the two versions matched, there is no need to reinstall the package
# we assume a reinstall is always run without benefit of a console (runningAtBoot will be true)
# so there will be no prompts and all actions will be automatic
#
# "deferReboot" signals that endScript should not prompt for a user reboot, but return exitReboot
# This is used by the manual package install called from SetupHelper setup script
#
# "uninstall" causes the package to be uninstalled silently
#
# command line parameters may appear in any order
# make sure rootfs is mounted R/W
if [ -f /opt/victronenergy/swupdate-scripts/remount-rw.sh ]; then
/opt/victronenergy/swupdate-scripts/remount-rw.sh
fi
reinstall=false
force=false
deferReboot=false
while [ $# -gt 0 ]; do
case $1 in
$reinstallParam)
reinstall=true
;;
"force")
force=true
;;
"deferReboot")
deferReboot=true
;;
"uninstall")
scriptAction='UNINSTALL'
logToConsole=false
;;
*)
esac
shift
done
if $reinstall ; then
if $force ; then
scriptAction='INSTALL'
elif [ ! -f "$installedFlag" ]; then
scriptAction='INSTALL'
else
# compare installed version with the one in the package directory
# nothing to do
# if they are not the same, continue with install
iVer=$(cat "$installedFlag")
pVer=$(cat "$scriptDir/version")
if [ "$iVer" = "$pVer" ]; then
exit
else
scriptAction='INSTALL'
fi
fi
runningAtBoot=true
logToConsole=false
# not a reinstall, continue with MANUAL installation
else
runningAtBoot=false
fi
# initialze integer version number for venus version
# used below and in _checkFileSets
versionStringToNumber $venusVersion
venusVersionNumber=$versionNumber
# check to see if package is compatible with this Venus version
notCompatible=false
if [ -f "$scriptDir/obsoleteVersion" ]; then
versionStringToNumber $(cat "$scriptDir/obsoleteVersion")
obsoleteVersion=$versionNumber
if (( $venusVersionNumber >= $obsoleteVersion )); then
notCompatible=true
fi
fi
if $notCompatible ; then
logMessage "$packageName not compatible with Venus $venusVersion"
scriptAction='UNINSTALL'
else
if [ ! -d "$setupOptionsRoot" ]; then
logMessage "creating root setup options directory $setupOptionsRoot"
mkdir $setupOptionsRoot
fi
if [ ! -d "$setupOptionsDir" ]; then
logMessage "creating package options directory to $setupOptionsDir"
mkdir $setupOptionsDir
fi
fi
# if forcing an uninstall, skip file set checks
if [ $scriptAction != 'UNINSTALL' ]; then
_checkFileSets
fi