forked from AllskyTeam/allsky
/
install.sh
executable file
·2255 lines (1931 loc) · 77 KB
/
install.sh
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
#!/bin/bash
[[ -z ${ALLSKY_HOME} ]] && export ALLSKY_HOME="$(realpath "$(dirname "${BASH_ARGV0}")")"
ME="$(basename "${BASH_ARGV0}")"
#shellcheck disable=SC2086 source-path=.
source "${ALLSKY_HOME}/variables.sh" || exit ${ALLSKY_ERROR_STOP}
#shellcheck disable=SC2086 source-path=scripts
source "${ALLSKY_SCRIPTS}/functions.sh" || exit ${ALLSKY_ERROR_STOP}
# This file defines functions plus sets many variables.
#shellcheck disable=SC2086 source-path=scripts
source "${ALLSKY_SCRIPTS}/installUpgradeFunctions.sh" || exit ${ALLSKY_ERROR_STOP}
if [[ ${EUID} -eq 0 ]]; then
display_msg error "This script must NOT be run as root, do NOT use 'sudo'."
exit 1
fi
# This script assumes the user already did the "git clone" into ${ALLSKY_HOME}.
#shellcheck disable=SC2086
cd "${ALLSKY_HOME}" || exit ${ALLSKY_ERROR_STOP}
# PRIOR_ALL_DIR is passed to us and is the location of an optional prior copy of Allsky.
PRIOR_CONFIG_DIR="${PRIOR_ALLSKY_DIR}/config"
PRIOR_CONFIG_FILE="${PRIOR_CONFIG_DIR}/config.sh"
PRIOR_FTP_FILE="${PRIOR_CONFIG_DIR}/ftp-settings.sh" # may change depending on old version
TITLE="Allsky Installer"
FINAL_SUDOERS_FILE="/etc/sudoers.d/allsky"
OLD_RASPAP_DIR="/etc/raspap" # used to contain WebUI configuration files
SETTINGS_FILE_NAME="$(basename "${SETTINGS_FILE}")"
FORCE_CREATING_SETTINGS_FILE="false" # should a default settings file be created?
RESTORED_PRIOR_SETTINGS_FILE="false"
PRIOR_SETTINGS_FILE="" # Full pathname to the prior settings file, if it exists
RESTORED_PRIOR_CONFIG_SH="false" # prior config.sh restored?
RESTORED_PRIOR_FTP_SH="false" # prior ftp-settings.sh restored?
ALLSKY_VERSION="$( get_version )" # version we're installing
PRIOR_ALLSKY="" # Set to "new" or "old" if they have a prior version
PRIOR_ALLSKY_VERSION="" # The version number of the prior version, if known
SUGGESTED_NEW_HOST_NAME="allsky" # Suggested new host name
NEW_HOST_NAME="" # User-specified host name
BRANCH="${GITHUB_MAIN_BRANCH}" # default branch
# Repo files
REPO_SUDOERS_FILE="${ALLSKY_REPO}/sudoers.repo"
REPO_WEBUI_DEFINES_FILE="${ALLSKY_REPO}/allskyDefines.inc.repo"
REPO_LIGHTTPD_FILE="${ALLSKY_REPO}/lighttpd.conf.repo"
REPO_AVI_FILE="${ALLSKY_REPO}/avahi-daemon.conf.repo"
# The POST_INSTALLATION_ACTIONS contains information the user needs to act upon after the reboot.
rm -f "${POST_INSTALLATION_ACTIONS}" # Shouldn't be there, but just in case.
rm -f "${ALLSKY_MESSAGES}" # Start out with no messages.
# display_msg() will send "log" entries to this file.
# DISPLAY_MSG_LOG is used in display_msg()
# shellcheck disable=SC2034
DISPLAY_MSG_LOG="${ALLSKY_INSTALLATION_LOGS}/install.sh.log"
# Some versions of Linux default to 750 so web server can't read it
chmod 755 "${ALLSKY_HOME}"
####################### functions
####
#
do_initial_heading()
{
if [[ ${UPDATE} == "true" ]]; then
display_header "Updating Allsky"
return
fi
MSG="Welcome to the ${TITLE}!\n"
if [[ -n ${PRIOR_ALLSKY} ]]; then
MSG="${MSG}\nYou will be asked if you want to use the images and darks (if any) from"
MSG="${MSG}\nyour prior version of Allsky."
if [[ ${PRIOR_ALLSKY} == "new" ]]; then
MSG="${MSG}\nIf so, we will use its settings as well."
else
MSG="${MSG}\nIf so, we will attempt to use its settings as well, but may not be"
MSG="${MSG}\nable to use ALL prior settings depending on how old your prior Allsky is."
MSG="${MSG}\nIn that case, you'll be prompted for required information such as"
MSG="${MSG}\nthe camera's latitude, logitude, and locale."
fi
else
MSG="${MSG}\nYou will be prompted for required information such as the type"
MSG="${MSG}\nof camera you have and the camera's latitude, logitude, and locale."
fi
MSG="${MSG}\n\nNOTE: your camera must be connected to the Pi before continuing."
MSG="${MSG}\n\nContinue?"
if ! whiptail --title "${TITLE}" --yesno "${MSG}" 25 "${WT_WIDTH}" 3>&1 1>&2 2>&3; then
display_msg "${LOG_TYPE}" info "User not ready to continue."
exit_installation 1
fi
display_header "Welcome to the ${TITLE}!"
}
####
usage_and_exit()
{
RET=${1}
if [[ ${RET} -eq 0 ]]; then
C="${YELLOW}"
else
C="${RED}"
fi
# Don't show --testing option since users shouldn't use it.
echo
echo -e "${C}Usage: ${ME} [--help] [--debug [...]] [--update] [--function function]${NC}"
echo
echo "'--help' displays this message and exits."
echo
echo "'--debug' displays debugging information. Can be called multiple times to increase level."
echo
echo "'--update' should only be used when instructed to by the Allsky Website."
echo
echo "'--function' executes the specified function and quits."
echo
#shellcheck disable=SC2086
exit_installation ${RET}
}
####
# Stop Allsky. If it's not running, nothing happens.
stop_allsky()
{
sudo systemctl stop allsky 2> /dev/null
}
####
# Get the branch of the release we are installing;
# if not GITHUB_MAIN_BRANCH, save the name of the branch.
# There is no "branch" file in GitHub so we need to determine the branch
# by looking in the .git/config file.
get_this_branch()
{
local FILE="${ALLSKY_HOME}/.git/config"
if [[ -f ${FILE} ]]; then
local B="$(sed -E --silent -e '/^\[branch "/s/(^\[branch ")(.*)("])/\2/p' "${FILE}")"
if [[ -n ${B} ]]; then
if [[ ${B} == "${GITHUB_MAIN_BRANCH}" ]]; then
display_msg --logonly info "Using the '${B}' branch."
else
BRANCH="${B}"
echo -n "${BRANCH}" > "${ALLSKY_BRANCH_FILE}"
display_msg --log info "Using '${BRANCH}' branch."
fi
else
display_msg --log warning "Unable to determine branch from '${FILE}'; assuming ${BRANCH}."
fi
else
display_msg --log warning "${FILE} not found; assuming ${BRANCH} branch."
fi
}
####
##### Execute any specified function, then exit.
do_function()
{
local FUNCTION="${1}"
shift
if ! type "${FUNCTION}" > /dev/null; then
display_msg error "Unknown function: '${FUNCTION}'."
exit 1
fi
${FUNCTION} "$@"
exit $?
}
####
# Map the new ${CAMERA_TYPE} setting to the old ${CAMERA} setting.
CAMERA_TYPE_to_CAMERA()
{
local CAMERA_TYPE="${1}"
if [[ ${CAMERA_TYPE} == "ZWO" ]]; then
echo "ZWO"
elif [[ ${CAMERA_TYPE} == "RPi" ]]; then
echo "RPiHQ" # RPi cameras used to be called "RPiHQ".
else
display_msg --log error "Unknown CAMERA_TYPE: '${CAMERA_TYPE}'"
exit_installation 1
fi
}
####
# Map the old ${CAMERA} setting to the new ${CAMERA_TYPE} setting.
CAMERA_to_CAMERA_TYPE()
{
local CAMERA="${1}"
if [[ ${CAMERA} == "ZWO" ]]; then
echo "ZWO"
elif [[ ${CAMERA} == "RPiHQ" ]]; then
echo "RPi"
else
display_msg --log error "Unknown CAMERA: '${CAMERA}'"
exit_installation 1
fi
}
####
# Prompt the user to select their camera type, if we can't determine it automatically.
# If they have a prior installation of Allsky that uses either CAMERA or CAMERA_TYPE in config.sh,
# we can use its value and not prompt.
CAMERA_TYPE=""
select_camera_type()
{
if [[ -f ${PRIOR_CONFIG_FILE} ]]; then
case "${PRIOR_ALLSKY_VERSION}" in
# New versions go here...
v2023.05.01*)
# New style Allsky using ${CAMERA_TYPE}.
CAMERA_TYPE="$(get_variable "CAMERA_TYPE" "${PRIOR_CONFIG_FILE}")"
# Don't bother with a message since this is a "similar" release.
if [[ -n ${CAMERA_TYPE} ]]; then
MSG="Using CAMERA_TYPE '${CAMERA_TYPE}' from prior config.sh"
display_msg --logonly info "${MSG}"
return
else
MSG="CAMERA_TYPE not in prior config.sh; possibly corrupted file?"
display_msg --log error "${MSG}"
fi
;;
"v2022.03.01" | "old")
local CAMERA="$(get_variable "CAMERA" "${PRIOR_CONFIG_FILE}")"
if [[ -n ${CAMERA} ]]; then
CAMERA_TYPE="$( CAMERA_to_CAMERA_TYPE "${CAMERA}" )"
if [[ ${CAMERA} != "${CAMERA_TYPE}" ]]; then
NEW=" (now called ${CAMERA_TYPE})"
else
NEW=""
fi
display_msg --log progress "Using prior ${CAMERA} camera${NEW}."
return
else
MSG="CAMERA not in prior old-style config.sh."
display_msg --log warning "${MSG}"
fi
;;
esac
fi
# "2" is the number of menu items.
MSG="\nSelect your camera type:\n"
CAMERA_TYPE=$(whiptail --title "${TITLE}" --menu "${MSG}" 15 "${WT_WIDTH}" 2 \
"ZWO" " ZWO ASI" \
"RPi" " Raspberry Pi HQ, Module 3, and compatible" \
3>&1 1>&2 2>&3)
if [[ $? -ne 0 ]]; then
display_msg --log warning "Camera selection required. Please re-run the installation and select a camera to continue."
exit_installation 2
fi
display_msg --log progress "Using ${CAMERA_TYPE} camera."
}
####
# Create the file that defines the WebUI variables.
create_webui_defines()
{
display_msg --log progress "Modifying locations for WebUI."
FILE="${ALLSKY_WEBUI}/includes/allskyDefines.inc"
sed -e "s;XX_ALLSKY_HOME_XX;${ALLSKY_HOME};" \
-e "s;XX_ALLSKY_CONFIG_XX;${ALLSKY_CONFIG};" \
-e "s;XX_ALLSKY_SCRIPTS_XX;${ALLSKY_SCRIPTS};" \
-e "s;XX_ALLSKY_TMP_XX;${ALLSKY_TMP};" \
-e "s;XX_ALLSKY_IMAGES_XX;${ALLSKY_IMAGES};" \
-e "s;XX_ALLSKY_MESSAGES_XX;${ALLSKY_MESSAGES};" \
-e "s;XX_ALLSKY_WEBUI_XX;${ALLSKY_WEBUI};" \
-e "s;XX_ALLSKY_WEBSITE_XX;${ALLSKY_WEBSITE};" \
-e "s;XX_ALLSKY_WEBSITE_LOCAL_CONFIG_NAME_XX;${ALLSKY_WEBSITE_CONFIGURATION_NAME};" \
-e "s;XX_ALLSKY_WEBSITE_REMOTE_CONFIG_NAME_XX;${ALLSKY_REMOTE_WEBSITE_CONFIGURATION_NAME};" \
-e "s;XX_ALLSKY_WEBSITE_LOCAL_CONFIG_XX;${ALLSKY_WEBSITE_CONFIGURATION_FILE};" \
-e "s;XX_ALLSKY_WEBSITE_REMOTE_CONFIG_XX;${ALLSKY_REMOTE_WEBSITE_CONFIGURATION_FILE};" \
-e "s;XX_ALLSKY_OWNER_XX;${ALLSKY_OWNER};" \
-e "s;XX_ALLSKY_GROUP_XX;${ALLSKY_GROUP};" \
-e "s;XX_WEBSERVER_GROUP_XX;${WEBSERVER_GROUP};" \
-e "s;XX_ALLSKY_REPO_XX;${ALLSKY_REPO};" \
-e "s;XX_ALLSKY_VERSION_XX;${ALLSKY_VERSION};" \
-e "s;XX_RASPI_CONFIG_XX;${ALLSKY_CONFIG};" \
-e "s;XX_ALLSKY_OVERLAY_XX;${ALLSKY_OVERLAY};" \
-e "s;XX_ALLSKY_MODULES_XX;${ALLSKY_MODULES};" \
"${REPO_WEBUI_DEFINES_FILE}" > "${FILE}"
chmod 644 "${FILE}"
}
####
# Recreate the options file.
# This can be used after installation if the options file gets hosed.
recreate_options_file()
{
CAMERA_TYPE="$(get_variable "CAMERA_TYPE" "${ALLSKY_CONFIG}/config.sh")"
save_camera_capabilities "true"
set_permissions
}
####
# Save the camera capabilities and use them to set the WebUI min, max, and defaults.
# This will error out and exit if no camera is installed,
# otherwise it will determine what capabilities the connected camera has,
# then create an "options" file specific to that camera.
# It will also create a default camera-specific "settings" file if one doesn't exist.
save_camera_capabilities()
{
if [[ -z ${CAMERA_TYPE} ]]; then
display_msg --log error "INTERNAL ERROR: CAMERA_TYPE not set in save_camera_capabilities()."
return 1
fi
OPTIONSFILEONLY="${1}" # Set to "true" if we should ONLY create the options file.
# Create the camera type/model-specific options file and optionally a default settings file.
# --cameraTypeOnly tells makeChanges.sh to only change the camera info, then exit.
# It displays any error messages.
if [[ ${FORCE_CREATING_SETTINGS_FILE} == "true" ]]; then
FORCE="--force"
MSG=" and default settings"
else
FORCE=""
MSG=""
fi
if [[ ${OPTIONSFILEONLY} == "true" ]]; then
OPTIONSONLY="--optionsOnly"
else
OPTIONSONLY=""
display_msg --log progress "Setting up WebUI options${MSG} for ${CAMERA_TYPE} cameras."
fi
# Restore the prior settings file so it can be used by makeChanges.sh.
[[ ${PRIOR_ALLSKY} != "" ]] && restore_prior_settings_files
MSG="Executing makeChanges.sh ${FORCE} ${OPTIONSONLY} --cameraTypeOnly"
MSG="${MSG} ${DEBUG_ARG} 'cameraType' 'Camera Type' '${PRIOR_CAMERA_TYPE}' '${CAMERA_TYPE}'"
display_msg "${LOG_TYPE}" info "${MSG}"
#shellcheck disable=SC2086
MSG="$( "${ALLSKY_SCRIPTS}/makeChanges.sh" ${FORCE} ${OPTIONSONLY} --cameraTypeOnly ${DEBUG_ARG} \
"cameraType" "Camera Type" "${PRIOR_CAMERA_TYPE}" "${CAMERA_TYPE}" 2>&1 )"
RET=$?
display_msg "${LOG_TYPE}" info "${MSG}"
if [[ ${RET} -ne 0 ]]; then
#shellcheck disable=SC2086
if [[ ${RET} -eq ${EXIT_NO_CAMERA} ]]; then
MSG="No camera was found; one must be connected and working for the installation to succeed.\n"
MSG="$MSG}After connecting your camera, run '${ME} --update'."
whiptail --title "${TITLE}" --msgbox "${MSG}" 12 "${WT_WIDTH}" 3>&1 1>&2 2>&3
display_msg --log error "No camera detected - installation aborted."
elif [[ ${OPTIONSFILEONLY} == "false" ]]; then
display_msg --log error "Unable to save camera capabilities."
fi
return 1
else
MSG="$( ls -l "${ALLSKY_CONFIG}/settings"*.json 2>/dev/null )"
display_msg "${LOG_TYPE}" info "Settings files:\n${MSG}"
fi
return 0
}
####
# Update the sudoers file so the web server can execute certain commands with sudo.
do_sudoers()
{
display_msg --log progress "Creating/updating sudoers file."
sed -e "s;XX_ALLSKY_SCRIPTS_XX;${ALLSKY_SCRIPTS};" "${REPO_SUDOERS_FILE}" > /tmp/x
sudo install -m 0644 /tmp/x "${FINAL_SUDOERS_FILE}" && rm -f /tmp/x
}
####
# Ask the user if they want to reboot
WILL_REBOOT="false"
ask_reboot()
{
local TYPE="${1}"
if [[ ${TYPE} == "locale" ]]; then
local MSG="A reboot is needed for the locale change to take effect."
MSG="${MSG}\nYou must reboot before continuing the installation."
MSG="${MSG}\n\nReboot now?"
if whiptail --title "${TITLE}" --yesno "${MSG}" 18 "${WT_WIDTH}" 3>&1 1>&2 2>&3; then
return 0
else
return 1
fi
fi
local AT=" http://${NEW_HOST_NAME}.local\n"
AT="${AT}or\n"
AT="${AT} http://$(hostname -I | sed -e 's/ .*$//')"
local MSG="*** Allsky installation is almost done. ***"
MSG="${MSG}\n\nWhen done, you must reboot the Raspberry Pi to finish the installation."
MSG="${MSG}\n\nAfter reboot you can connect to the WebUI at:\n"
MSG="${MSG}${AT}"
MSG="${MSG}\n\nReboot when installation is done?"
if whiptail --title "${TITLE}" --yesno "${MSG}" 18 "${WT_WIDTH}" 3>&1 1>&2 2>&3; then
WILL_REBOOT="true"
display_msg --logonly info "Pi will reboot after installation completes."
else
display_msg --logonly info "User elected not to reboot; warning to them provided."
display_msg notice "You need to reboot the Pi before Allsky will work."
MSG="If you have not already rebooted your Pi, please do so now.\n"
MSG="${MSG}You can then connect to the WebUI at:\n"
MSG="${MSG}${AT}"
echo -e "\n\n==========\n${MSG}" >> "${POST_INSTALLATION_ACTIONS}"
fi
}
do_reboot()
{
exit_installation -1 # -1 means just log ending statement but don't exit.
sudo reboot now
}
####
# Check for size of RAM+swap during installation (Issue # 969).
# recheck_swap is used to check swap after the installation,
# and is referenced in the Allsky Documentation.
recheck_swap()
{
check_swap "prompt"
}
check_swap()
{
if [[ ${1} == "prompt" ]]; then
PROMPT="true"
else
PROMPT="false"
fi
# This can return "total_mem is unknown" if the OS is REALLY old.
RAM_SIZE="$( vcgencmd get_config total_mem )"
if echo "${RAM_SIZE}" | grep --silent "unknown" ; then
# Note: This doesn't produce exact results. On a 4 GB Pi, it returns 3.74805.
RAM_SIZE=$(free --mebi | awk '{if ($1 == "Mem:") {print $2; exit 0} }') # in MB
else
RAM_SIZE="${RAM_SIZE//total_mem=/}"
fi
DESIRED_COMBINATION=$((1024 * 4)) # desired minimum memory + swap
SUGGESTED_SWAP_SIZE=0
for i in 512 1024 2048 # 4096 and above don't need any swap
do
if [[ ${RAM_SIZE} -le ${i} ]]; then
SUGGESTED_SWAP_SIZE=$((DESIRED_COMBINATION - i))
break
fi
done
display_msg "${LOG_TYPE}" debug "RAM_SIZE=${RAM_SIZE}, SUGGESTED_SWAP_SIZE=${SUGGESTED_SWAP_SIZE}."
# Not sure why, but displayed swap is often 1 MB less than what's in /etc/dphys-swapfile
CURRENT_SWAP=$(free --mebi | awk '{if ($1 == "Swap:") {print $2 + 1; exit 0} }') # in MB
CURRENT_SWAP=${CURRENT_SWAP:-0}
if [[ ${CURRENT_SWAP} -lt ${SUGGESTED_SWAP_SIZE} || ${PROMPT} == "true" ]]; then
local SWAP_CONFIG_FILE="/etc/dphys-swapfile"
[[ -z ${FUNCTION} ]] && sleep 2 # give user time to read prior messages
if [[ ${CURRENT_SWAP} -eq 1 ]]; then
CURRENT_SWAP=0
AMT="no"
M="added"
else
AMT="${CURRENT_SWAP} MB of"
M="increased"
fi
MSG="\nYour Pi currently has ${AMT} swap space."
MSG="${MSG}\nBased on your memory size of ${RAM_SIZE} MB,"
if [[ ${CURRENT_SWAP} -ge ${SUGGESTED_SWAP_SIZE} ]]; then
SUGGESTED_SWAP_SIZE=${CURRENT_SWAP}
MSG="${MSG} there is no need to change anything, but you can if you would like."
else
MSG="${MSG} we suggest ${SUGGESTED_SWAP_SIZE} MB of swap"
MSG="${MSG} to decrease the chance of timelapse and other failures."
MSG="${MSG}\n\nDo you want swap space ${M}?"
MSG="${MSG}\n\nYou may change the amount of swap by changing the number below."
fi
SWAP_SIZE=$(whiptail --title "${TITLE}" --inputbox "${MSG}" 18 "${WT_WIDTH}" \
"${SUGGESTED_SWAP_SIZE}" 3>&1 1>&2 2>&3)
# If the suggested swap was 0 and the user added a number but didn't first delete the 0,
# do it now so we don't have numbers like "0256".
[[ ${SWAP_SIZE:0:1} == "0" ]] && SWAP_SIZE="${SWAP_SIZE:1}"
if [[ -z ${SWAP_SIZE} || ${SWAP_SIZE} == "0" ]]; then
if [[ ${CURRENT_SWAP} -eq 0 && ${SUGGESTED_SWAP_SIZE} -gt 0 ]]; then
display_msg --log warning "With no swap space you run the risk of programs failing."
else
display_msg --log info "Swap will remain at ${CURRENT_SWAP}."
fi
else
display_msg --log progress "Setting swap space set to ${SWAP_SIZE} MB."
sudo dphys-swapfile swapoff # Stops the swap file
sudo sed -i "/CONF_SWAPSIZE/ c CONF_SWAPSIZE=${SWAP_SIZE}" "${SWAP_CONFIG_FILE}"
CURRENT_MAX="$(get_variable "CONF_MAXSWAP" "${SWAP_CONFIG_FILE}")"
# TODO: Can we determine the default max rather than hard-code it.
CURRENT_MAX="${CURRENT_MAX:-2048}"
if [[ ${CURRENT_MAX} -lt ${SWAP_SIZE} ]]; then
if [[ ${DEBUG} -gt 0 ]]; then
display_msg --log debug "Increasing max swap size to ${SWAP_SIZE} MB."
fi
sudo sed -i "/CONF_MAXSWAP/ c CONF_MAXSWAP=${SWAP_SIZE}" "${SWAP_CONFIG_FILE}"
fi
sudo dphys-swapfile setup > /dev/null # Sets up new swap file
sudo dphys-swapfile swapon # Turns on new swap file
fi
else
display_msg --log progress "Size of current swap (${CURRENT_SWAP} MB) is sufficient; no change needed."
fi
}
####
# Check if ${ALLSKY_TMP} exists, and if it does,
# save any *.jpg files (which we probably created), then remove everything else,
# then mount it.
check_and_mount_tmp()
{
local TMP_DIR="/tmp/IMAGES"
if [[ -d "${ALLSKY_TMP}" ]]; then
local IMAGES="$(find "${ALLSKY_TMP}" -name '*.jpg')"
display_msg "${LOG_TYPE}" debug "Existing IMAGES=${IMAGES}"
if [[ -n ${IMAGES} ]]; then
mkdir "${TMP_DIR}"
# Need to allow for files with spaces in their names.
# TODO: there has to be a better way.
echo "${IMAGES}" | \
while read -r image
do
mv "${image}" "${TMP_DIR}"
done
fi
rm -f "${ALLSKY_TMP}"/*
else
mkdir "${ALLSKY_TMP}"
fi
# Now mount and restore any images that were there before
sudo mount -a
if [[ -d ${TMP_DIR} ]]; then
mv "${TMP_DIR}"/* "${ALLSKY_TMP}"
rmdir "${TMP_DIR}"
fi
}
####
# Check if prior ${ALLSKY_TMP} was a memory filesystem.
# If not, offer to make it one.
check_tmp()
{
INITIAL_FSTAB_STRING="tmpfs ${ALLSKY_TMP} tmpfs"
# Check if currently a memory filesystem.
if grep --quiet "^${INITIAL_FSTAB_STRING}" /etc/fstab; then
display_msg --log progress "${ALLSKY_TMP} is currently in memory; no change needed."
# If there's a prior Allsky version and it's tmp directory is mounted,
# try to unmount it, but that often gives an error that it's busy,
# which isn't really a problem since it'll be unmounted at the reboot.
# /etc/fstab has ${ALLSKY_TMP} but the mount point is currently in the PRIOR Allsky.
local D="${PRIOR_ALLSKY_DIR}/tmp"
if [[ -d "${D}" ]] && mount | grep --silent "${D}" ; then
# The Samba daemon is one known cause of "target busy".
sudo umount -f "${D}" 2> /dev/null ||
(
sudo systemctl restart smbd 2> /dev/null
sudo umount -f "${D}" 2> /dev/null
)
fi
# If the new Allsky's ${ALLSKY_TMP} is already mounted, don't do anything.
# This would be the case during an upgrade.
if mount | grep --silent "${ALLSKY_TMP}" ; then
display_msg "${LOG_TYPE}" debug "${ALLSKY_TMP} already mounted"
return 0
fi
check_and_mount_tmp # works on new ${ALLSKY_TMP}
return 0
fi
SIZE=75 # MB - should be enough
MSG="Making ${ALLSKY_TMP} reside in memory can drastically decrease the amount of writes to the SD card, increasing its life."
MSG="${MSG}\n\nDo you want to make it reside in memory?"
MSG="${MSG}\n\nNote: anything in it will be deleted whenever the Pi is rebooted, but that's not an issue since the directory only contains temporary files."
if whiptail --title "${TITLE}" --yesno "${MSG}" 15 "${WT_WIDTH}" 3>&1 1>&2 2>&3; then
echo "${INITIAL_FSTAB_STRING} size=${SIZE}M,noatime,lazytime,nodev,nosuid,mode=775,uid=${ALLSKY_OWNER},gid=${WEBSERVER_GROUP}" | sudo tee -a /etc/fstab > /dev/null
check_and_mount_tmp
display_msg --log progress "${ALLSKY_TMP} is now in memory."
else
display_msg --log info "${ALLSKY_TMP} will remain on disk."
mkdir -p "${ALLSKY_TMP}"
fi
}
####
check_success()
{
local RET=${1}
local MESSAGE="${2}"
local LOG="${3}"
local D=${4}
if [[ ${RET} -ne 0 ]]; then
display_msg --log error "${MESSAGE}"
MSG="The full log file is in ${LOG}"
MSG="${MSG}\nThe end of the file is:"
display_msg --log info "${MSG}"
tail -5 "${LOG}"
return 1
fi
[[ ${D} -gt 1 ]] && cat "${LOG}"
return 0
}
####
# Install the web server.
install_webserver()
{
display_msg --log progress "Installing the web server."
sudo systemctl stop hostapd 2> /dev/null
sudo systemctl stop lighttpd 2> /dev/null
TMP="${ALLSKY_INSTALLATION_LOGS}/lighttpd.install.log"
(
sudo apt-get update && \
sudo apt-get --assume-yes install lighttpd php-cgi php-gd hostapd dnsmasq avahi-daemon
) > "${TMP}" 2>&1
check_success $? "lighttpd installation failed" "${TMP}" "${DEBUG}" || exit_with_image 1
FINAL_LIGHTTPD_FILE="/etc/lighttpd/lighttpd.conf"
sed \
-e "s;XX_ALLSKY_WEBUI_XX;${ALLSKY_WEBUI};g" \
-e "s;XX_ALLSKY_HOME_XX;${ALLSKY_HOME};g" \
-e "s;XX_ALLSKY_IMAGES_XX;${ALLSKY_IMAGES};g" \
-e "s;XX_ALLSKY_WEBSITE_XX;${ALLSKY_WEBSITE};g" \
-e "s;XX_ALLSKY_DOCUMENTATION_XX;${ALLSKY_DOCUMENTATION};g" \
-e "s;XX_ALLSKY_CONFIG_XX;${ALLSKY_CONFIG};g" \
-e "s;XX_ALLSKY_OVERLAY_XX;${ALLSKY_OVERLAY};g" \
"${REPO_LIGHTTPD_FILE}" > /tmp/x
sudo install -m 0644 /tmp/x "${FINAL_LIGHTTPD_FILE}" && rm -f /tmp/x
# Ignore output since it may already be enabled.
sudo lighty-enable-mod fastcgi-php > /dev/null 2>&1
# Remove any old log files.
# Start off with a 0-length log file the user can write to.
local D="/var/log/lighttpd"
sudo chmod 755 "${D}"
sudo rm -fr "${D}"/*
local LIGHTTPD_LOG="${D}/error.log"
sudo touch "${LIGHTTPD_LOG}"
sudo chmod 664 "${LIGHTTPD_LOG}"
sudo chown "${WEBSERVER_GROUP}:${ALLSKY_GROUP}" "${LIGHTTPD_LOG}"
sudo systemctl start lighttpd
# Starting it added an entry so truncate the file so it's 0-length
sleep 1; truncate -s 0 "${LIGHTTPD_LOG}"
}
####
# Prompt for a new hostname if needed,
# and update all the files that contain the hostname.
prompt_for_hostname()
{
# If the Pi is already called ${SUGGESTED_NEW_HOST_NAME},
# then the user already updated the name, so don't prompt again.
CURRENT_HOSTNAME=$(tr -d " \t\n\r" < /etc/hostname)
if [[ ${CURRENT_HOSTNAME} == "${SUGGESTED_NEW_HOST_NAME}" ]]; then
NEW_HOST_NAME="${CURRENT_HOSTNAME}"
return
fi
# If we're upgrading, use the current name.
if [[ -n ${PRIOR_ALLSKY} ]]; then
NEW_HOST_NAME="${CURRENT_HOSTNAME}"
display_msg --log progress "Using current hostname of ${CURRENT_HOSTNAME}."
return
fi
MSG="Please enter a hostname for your Pi."
MSG="${MSG}\n\nIf you have more than one Pi on your network they must all have unique names."
NEW_HOST_NAME=$(whiptail --title "${TITLE}" --inputbox "${MSG}" 10 "${WT_WIDTH}" \
"${SUGGESTED_NEW_HOST_NAME}" 3>&1 1>&2 2>&3)
if [[ $? -ne 0 ]]; then
display_msg --log warning "You must specify a host name. Please re-run the installation and select one."
exit_installation 2
fi
if [[ ${CURRENT_HOSTNAME} != "${NEW_HOST_NAME}" ]]; then
echo "${NEW_HOST_NAME}" | sudo tee /etc/hostname > /dev/null
sudo sed -i "s/127.0.1.1.*${CURRENT_HOSTNAME}/127.0.1.1\t${NEW_HOST_NAME}/" /etc/hosts
fi
# Set up the avahi daemon if needed.
FINAL_AVI_FILE="/etc/avahi/avahi-daemon.conf"
[[ -f ${FINAL_AVI_FILE} ]] && grep -i --quiet "host-name=${NEW_HOST_NAME}" "${FINAL_AVI_FILE}"
if [[ $? -ne 0 ]]; then
# New NEW_HOST_NAME is not found in the file, or the file doesn't exist,
# so need to configure it.
display_msg --log progress "Configuring avahi-daemon."
sed "s/XX_HOST_NAME_XX/${NEW_HOST_NAME}/g" "${REPO_AVI_FILE}" > /tmp/x
sudo install -m 0644 /tmp/x "${FINAL_AVI_FILE}" && rm -f /tmp/x
fi
}
####
# Set permissions on various web-related items.
set_permissions()
{
display_msg --log progress "Setting permissions on web-related files."
# Make sure the currently running user has can write to the webserver root
# and can run sudo on anything.
G="$(id "${ALLSKY_OWNER}")"
if ! echo "${G}" | grep --silent "(sudo)"; then
display_msg --log progress "Adding ${ALLSKY_OWNER} to sudo group."
### TODO: Hmmm. We need to run "sudo" to add to the group,
### but we don't have "sudo" permissions yet... so this will likely fail:
sudo addgroup --quiet "${ALLSKY_OWNER}" "sudo"
fi
if ! echo "${G}" | grep --silent "(${WEBSERVER_GROUP})"; then
display_msg --log progress "Adding ${ALLSKY_OWNER} to ${WEBSERVER_GROUP} group."
sudo addgroup --quiet "${ALLSKY_OWNER}" "${WEBSERVER_GROUP}"
# TODO: We had a case where the login shell wasn't in the group after "addgroup"
# until the user logged out and back in.
# And this was AFTER he ran install.sh and rebooted.
# Not sure what to do about this...
fi
# Remove any old entries; we now use /etc/sudoers.d/allsky instead of /etc/sudoers.
# TODO: Can remove this in the next release
sudo sed -i -e "/allsky/d" -e "/${WEBSERVER_GROUP}/d" /etc/sudoers
do_sudoers
# The web server needs to be able to create and update many of the files in ${ALLSKY_CONFIG}.
# Not all, but go ahead and chgrp all of them so we don't miss any new ones.
sudo find "${ALLSKY_CONFIG}/" -type f -exec chmod 664 {} \;
sudo find "${ALLSKY_CONFIG}/" -type d -exec chmod 775 {} \;
sudo chgrp -R "${WEBSERVER_GROUP}" "${ALLSKY_CONFIG}"
# The files should already be the correct permissions/owners, but just in case, set them.
# We don't know what permissions may have been on the old website, so use "sudo".
sudo find "${ALLSKY_WEBUI}/" -type f -exec chmod 644 {} \;
# These are the exceptions
chmod 755 "${ALLSKY_WEBUI}/includes/createAllskyOptions.php"
sudo find "${ALLSKY_WEBUI}/" -type d -exec chmod 755 {} \;
chmod 775 "${ALLSKY_TMP}"
sudo chgrp "${WEBSERVER_GROUP}" "${ALLSKY_TMP}"
# This is actually an Allsky Website file, but in case we restored the old website,
# set its permissions.
chgrp -f "${WEBSERVER_GROUP}" "${ALLSKY_WEBSITE_CONFIGURATION_FILE}"
}
####
# Check if there's a WebUI in the old-style location,
# or if the directory exists but there doesn't appear to be a WebUI in it.
# The installation (sometimes?) creates the directory.
OLD_WEBUI_LOCATION_EXISTS_AT_START="false"
does_old_WebUI_location_exist()
{
[[ -d ${OLD_WEBUI_LOCATION} ]] && OLD_WEBUI_LOCATION_EXISTS_AT_START="true"
}
check_old_WebUI_location()
{
[[ ! -d ${OLD_WEBUI_LOCATION} ]] && return
if [[ ${OLD_WEBUI_LOCATION_EXISTS_AT_START} == "false" ]]; then
# Installation created the directory so get rid of it.
sudo rm -fr "${OLD_WEBUI_LOCATION}"
return
fi
# The installation of the web server often creates a file in
# ${OLD_WEBUI_LOCATION}. It just says "No files yet...", so delete it.
sudo rm -f "${OLD_WEBUI_LOCATION}/index.lighttpd.html"
if [[ ! -d ${OLD_WEBUI_LOCATION}/includes ]]; then
MSG="The old WebUI location '${OLD_WEBUI_LOCATION}' exists"
COUNT=$(find "${OLD_WEBUI_LOCATION}" | wc -l)
if [[ ${COUNT} -eq 1 ]]; then
MSG="${MSG} and is empty."
MSG="${MSG}\nYou can safely delete it after installation: sudo rmdir '${OLD_WEBUI_LOCATION}'"
else
MSG="${MSG} but doesn't contain a valid WebUI."
MSG="${MSG}\nPlease check it out after installation - if there's nothing you"
MSG="${MSG} want in it, remove it: sudo rm -fr '${OLD_WEBUI_LOCATION}'"
fi
whiptail --title "${TITLE}" --msgbox "${MSG}" 15 "${WT_WIDTH}" 3>&1 1>&2 2>&3
display_msg --log notice "${MSG}"
echo -e "\n\n==========\n${MSG}" >> "${POST_INSTALLATION_ACTIONS}"
return
fi
MSG="An old version of the WebUI was found in ${OLD_WEBUI_LOCATION}; it is no longer being used so you may remove it after intallation."
MSG="${MSG}\n\nWARNING: if you have any other web sites in that directory, they will no longer be accessible via the web server."
whiptail --title "${TITLE}" --msgbox "${MSG}" 15 "${WT_WIDTH}" 3>&1 1>&2 2>&3
display_msg --log notice "${MSG}"
echo -e "\n\n==========\n${MSG}" >> "${POST_INSTALLATION_ACTIONS}"
}
####
# If a website exists, see if it's the newest version. If not, let the user know.
# If it's a new-style website, copy to the new Allsky release directory.
handle_prior_website()
{
local PRIOR_SITE=""
local PRIOR_STYLE=""
local OLD_WEBSITE="${OLD_WEBSITE_LOCATION}"
if [[ -d ${OLD_WEBSITE} ]]; then
PRIOR_SITE="${OLD_WEBSITE}" # old-style Website
PRIOR_STYLE="old"
elif [[ -d ${PRIOR_ALLSKY_DIR}/html/allsky ]]; then
PRIOR_SITE="${PRIOR_ALLSKY_DIR}/html/allsky" # new-style Website
PRIOR_STYLE="new"
else
return # no prior Website
fi
# Move any prior ALLSKY_WEBSITE to the new location.
# This HAS to be done since the lighttpd server only looks in the new location.
# Note: This MUST come before the old WebUI check below so we don't remove the prior website
# when we remove the prior WebUI.
if [[ -d ${ALLSKY_WEBSITE} ]]; then
# Hmmm. There's an old webite AND a new one.
# Allsky doesn't ship with the website directory, so not sure how one got there...
# Try to remove the new one - if it's not empty the remove will fail
# so rename it.
if ! rmdir "${ALLSKY_WEBSITE}" ; then
local UNKNOWN_WEBSITE="${ALLSKY_WEBSITE}-UNKNOWN-$$"
MSG="Unknown Website in '${ALLSKY_WEBSITE}' is not empty."
MSG="${MSG}\nRenaming to '${UNKNOWN_WEBSITE}'."
display_msg --log error "${MSG}"
if ! mv "${ALLSKY_WEBSITE}" "${UNKNOWN_WEBSITE}" ; then
display_msg --log error "Unable to move."
fi
fi
fi
# Trailing "/" tells get_version and get_branch to fill in the file
# names given the directory we pass to them.
# If there's no prior website version, then there IS a newer version available.
# Set ${PV} to a string to display in messages, but we'll still use ${PRIOR_VERSION}
# to determine whether or not there's a newer version. PRIOR_VERSION="" means there is.
local PRIOR_VERSION="$( get_version "${PRIOR_SITE}/" )"
local PV=""
if [[ -z ${PRIOR_VERSION} ]]; then
PV="** Unknown, but old **"
else
PV="${PRIOR_VERSION}"
fi
local NEWEST_VERSION="$(get_Git_version "${GITHUB_MAIN_BRANCH}" "${GITHUB_WEBSITE_PACKAGE}")"
if [[ -z ${NEWEST_VERSION} ]]; then
display_msg --log warning "Unable to determine verson of GitHub branch '${GITHUB_MAIN_BRANCH}'."
fi
local B=""
# Check if the prior website is outdated.
# For new-style websites, only check the branch they are currently running.
# If a non-production branch is used the Website installer will check if there's
# a newer production branch.
if [[ ${PRIOR_STYLE} == "new" ]]; then
# If get_branch returns "" the prior branch is ${GITHUB_MAIN_BRANCH}.
local PRIOR_BRANCH="$( get_branch "${PRIOR_SITE}/" )"
display_msg --log progress "Restoring Allsky Website from ${PRIOR_SITE}."
sudo mv "${PRIOR_SITE}" "${ALLSKY_WEBSITE}"
# Update "AllskyVersion" if needed.
V="$( settings .config.AllskyVersion "${ALLSKY_WEBSITE_CONFIGURATION_FILE}" )"
display_msg "${LOG_TYPE}" info "Prior local Website's AllskyVersion=${V}"
if [[ ${V} != "${ALLSKY_VERSION}" ]]; then
MSG="Updating AllskyVersion in local Website from '${V}' to '${ALLSKY_VERSION}'"
display_msg --log progress "${MSG}"
jq ".config.AllskyVersion = \"${ALLSKY_VERSION}\"" \
"${ALLSKY_WEBSITE_CONFIGURATION_FILE}" > /tmp/x \
&& mv /tmp/x "${ALLSKY_WEBSITE_CONFIGURATION_FILE}"
fi
# We can only check versions if we obtained the new version.
[[ -z ${NEWEST_VERSION} ]] && return
# If the old Website was using a non-production branch,
# see if there's a newer version of the Website with that branch OR
# a newer version with the production branch. Use whichever is newer.
if [[ -n ${PRIOR_BRANCH} && ${PRIOR_BRANCH} != "${GITHUB_MAIN_BRANCH}" ]]; then
NEWEST_VERSION="$(get_Git_version "${PRIOR_BRANCH}" "${GITHUB_WEBSITE_PACKAGE}")"
B=" in the '${PRIOR_BRANCH}' branch"
if [[ ${DEBUG} -gt 0 ]]; then
MSG="'${PRIOR_BRANCH}' branch: prior Website version=${PV},"
MSG="${MSG} Git version=${NEWEST_VERSION}."
display_msg --log debug "${MSG}"
fi
fi
elif [[ -z ${NEWEST_VERSION} ]]; then
return
fi
if [[ -n ${NEWEST_VERSION} ]]; then
display_msg "${LOG_TYPE}" debug "Comparing prior Website ${PV} to newest ${NEWEST_VERSION}${B}"
if [[ -z ${PRIOR_VERSION} || ${PRIOR_VERSION} < "${NEWEST_VERSION}" ]]; then
MSG="There is a newer Allsky Website available${B}; please upgrade to it."
MSG="${MSG}\nYour version: ${PV}"
MSG="${MSG}\nCurrent version: ${NEWEST_VERSION}"
MSG="${MSG}\n\nYou can upgrade by executing:"
MSG="${MSG}\n cd ~/allsky; website/install.sh"
MSG="${MSG}\nafter this installation finishes."
display_msg --log notice "${MSG}"
echo -e "\n\n==========\n${MSG}" >> "${POST_INSTALLATION_ACTIONS}"
fi
fi
}
####
# Get the locale, prompting if we can't determine it.
LOCALE=""
CURRENT_LOCALE=""
get_locale()
{
# A lot of people have the incorrect locale so prompt for the correct one.
# List of all installed locales, ignoring any lines with ":" which
# are usually error messages.
local INSTALLED_LOCALES="$(locale -a 2>/dev/null | grep -E -v "^C$|:" | sed 's/utf8/UTF-8/')"
if [[ -z ${INSTALLED_LOCALES} ]]; then
MSG="There are no locales on your system ('locale -a' didn't return valid locales)."
MSG="${MSG}\nYou need to install and set one before Allsky installation can run."
MSG="${MSG}\nTo install locales, run:"
MSG="${MSG}\n\tsudo raspi-config"
MSG="${MSG}\n\t\tPick 'Localisation Options'"
MSG="${MSG}\n\t\tPick 'Locale'"
MSG="${MSG}\n\t\tScroll down to the locale(s) you want to install, then press the SPACE key."
MSG="${MSG}\n\t\tWhen done, press the TAB key to select <Ok>, then press ENTER."
MSG="${MSG}\n\nIt will take a moment for the locale(s) to be installed."
MSG="${MSG}\n\nWhen that is completed, rerun the Allsky installation."
display_msg --log error "${MSG}"
exit_installation 1
fi
[[ ${DEBUG} -gt 1 ]] && display_msg --logonly debug "INSTALLED_LOCALES=${INSTALLED_LOCALES}"
# If the prior version of Allsky had a locale set but it's not
# an installed one, let the user know.
# This can happen if they use the settings file from a different Pi or different OS.
local MSG2=""
if [[ -n ${PRIOR_ALLSKY} && -n ${PRIOR_SETTINGS_FILE} ]]; then
local L="$( settings .locale "${PRIOR_SETTINGS_FILE}" )"
if [[ ${L} != "" && ${L} != "null" ]]; then
local X="$(echo "${INSTALLED_LOCALES}" | grep "${L}")"