-
Notifications
You must be signed in to change notification settings - Fork 181
/
install.sh
executable file
·3891 lines (3301 loc) · 135 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
# shellcheck disable=SC2154 # referenced but not assigned
[[ -z ${ALLSKY_HOME} ]] && export ALLSKY_HOME="$( realpath "$( dirname "${BASH_ARGV0}" )" )"
ME="$( basename "${BASH_ARGV0}" )"
#shellcheck source-path=.
source "${ALLSKY_HOME}/variables.sh" || exit "${EXIT_ERROR_STOP}"
#shellcheck source-path=scripts
source "${ALLSKY_SCRIPTS}/functions.sh" || exit "${EXIT_ERROR_STOP}"
#shellcheck source-path=scripts
source "${ALLSKY_SCRIPTS}/installUpgradeFunctions.sh" || exit "${EXIT_ERROR_STOP}"
# Default may be 700 (HOME) or 750 (ALLSKY_HOME) so web server can't read it
chmod 755 "${HOME}" "${ALLSKY_HOME}" || exit "${EXIT_ERROR_STOP}"
cd "${ALLSKY_HOME}" || exit "${EXIT_ERROR_STOP}"
# 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.
SHORT_TITLE="Allsky Installer"
TITLE="${SHORT_TITLE} - ${ALLSKY_VERSION}"
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_DEFAULT_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
COPIED_PRIOR_CONFIG_SH="false" # prior config.sh's settings copied to settings file?
COPIED_PRIOR_FTP_SH="false" # prior ftp-settings.sh's settings copied to settings file?
SUGGESTED_NEW_HOST_NAME="allsky" # Suggested new host name
NEW_HOST_NAME="" # User-specified host name
BRANCH="${GITHUB_MAIN_BRANCH}" # default branch
# shellcheck disable=SC2034
DISPLAY_MSG_LOG="${ALLSKY_LOGS}/install.log" # display_msg() sends log entries to this file.
LONG_BITS=$( getconf LONG_BIT ) # Size of a long, 32 or 64
REBOOT_NEEDED="true" # Is a reboot needed at end of installation?
CONFIGURATION_NEEDED="true" # Does Allsky need to be configured at end of installation?
SPACE=" "
NOT_RESTORED="NO PRIOR VERSION"
TMP_FILE="/tmp/x" # temporary file used by many functions
TAB="$( echo -e '\t' )"
# Overlay variables
SENSOR_WIDTH=""
SENSOR_HEIGHT=""
FULL_OVERLAY_NAME=""
SHORT_OVERLAY_NAME=""
OVERLAY_NAME=""
##### Allsky versions. ${ALLSKY_VERSION} is set in variables.sh
#xxx currently not used: ALLSKY_BASE_VERSION="$( remove_point_release "${ALLSKY_VERSION}" )"
# Base of first version with combined configuration files and all lowercase setting names.
COMBINED_BASE_VERSION="v2024.12.06"
# Base of first version with CAMERA_TYPE instead of CAMERA in config.sh and
# "cameratype" in the settings file.
FIRST_CAMERA_TYPE_BASE_VERSION="v2023.05.01"
# First Allsky version that used the "version" file.
# It's also when ftp-settings.sh moved to ${ALLSKY_CONFIG}
FIRST_VERSION_VERSION="v2022.03.01"
# Versions before ${FIRST_VERSION_VERSION} that didn't have version numbers.
PRE_FIRST_VERSION_VERSION="old"
##### Information on the prior Allsky version, if used
USE_PRIOR_ALLSKY="false"
PRIOR_ALLSKY_STYLE="" # Set to the style if they have a prior version
PRIOR_ALLSKY_VERSION="" # The version number of the prior version, if known
PRIOR_ALLSKY_BASE_VERSION="" # The base version number of the prior version, if known
PRIOR_CAMERA_TYPE=""
PRIOR_CAMERA_MODEL=""
PRIOR_CAMERA_NUMBER=""
# Holds status of installation if we need to exit and get back in.
STATUS_FILE="${ALLSKY_LOGS}/install_status.txt"
STATUS_FILE_TEMP="${ALLSKY_TMP}/temp_status.txt" # holds intermediate status
STATUS_LOCALE_REBOOT="Rebooting to change locale" # status of rebooting due to locale change
STATUS_FINISH_REBOOT="Rebooting to finish installation"
STATUS_NO_FINISH_REBOOT="Did not reboot to finish installation"
STATUS_NO_REBOOT="User elected not to reboot"
STATUS_NO_LOCALE="Desired locale not found" # exiting due to desired locale not installed
STATUS_NO_CAMERA="No camera found" # status of exiting due to no camera found
STATUS_NO_LAT_LONG="Latitude and/or Longitude not entered"
STATUS_OK="OK" # Installation was completed.
STATUS_NOT_CONTINUE="User elected not to continue" # Exiting, but not an error
STATUS_CLEAR="Clear" # Clear the file
STATUS_ERROR="Error encountered"
STATUS_INT="Got interrupt"
STATUS_VARIABLES=() # Holds all the variables and values to save
##### Set in installUpgradeFunctions.sh
# PRIOR_ALLSKY_DIR
# PRIOR_CONFIG_DIR
# PRIOR_REMOTE_WEBSITE_CONFIGURATION_FILE
# PRIOR_CONFIG_FILE, PRIOR_FTP_FILE
# PRIOR_PYTHON_VENV
# WEBSITE_CONFIG_VERSION, WEBSITE_ALLSKY_VERSION
# ALLSKY_DEFINES_INC, REPO_WEBUI_DEFINES_FILE
# REPO_SUDOERS_FILE, REPO_LIGHTTPD_FILE, REPO_AVI_FILE, REPO_OPTIONS_FILE
# LIGHTTPD_LOG_DIR, LIGHTTPD_LOG_FILE
# Plus others I probably forgot about...
############################################## functions
####
#
do_initial_heading()
{
[[ ${SKIP} == "true" ]] && return
if [[ ${UPDATE} == "true" ]]; then
display_header "Updating Allsky"
return
fi
local MSG X H
declare -n v="${FUNCNAME[0]}"
if [[ ${v} == "true" ]]; then
display_header "Welcome back to the ${SHORT_TITLE}!"
else
MSG="Welcome to the ${SHORT_TITLE}!\n"
if [[ ${RESTORE} == "true" ]]; then
H="$( basename "${ALLSKY_HOME}" )"
X="$( basename "${RENAMED_DIR}" )"
MSG+="\nYour current '${H}' directory will be renamed to"
MSG+="\n ${X}"
X="$( basename "${PRIOR_ALLSKY_DIR}" )"
MSG+="\nand the prior Allsky in '${X}' will be"
MSG+=" renamed to back to '${H}'."
MSG+="\n\nFiles that were moved from the old release to the current one"
MSG+=" will be moved back."
MSG+="\nYou will manually need to restart Allsky after checking that"
MSG+=" the settings are correct in the WebUI."
elif [[ ${USE_PRIOR_ALLSKY} == "true" ]]; then
MSG+="\nYou will be asked if you want to use the images and darks"
MSG+=" from your prior version of Allsky."
else
MSG+="\nYou will be prompted for required information such as the type"
MSG+="\nof camera you have and the camera's latitude, logitude, and locale."
fi
if [[ ${RESTORE} != "true" ]]; then
MSG+="\n\nNOTE: your camera must be connected to the Pi before continuing."
fi
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 "${STATUS_CLEAR}" ""
fi
display_header "Welcome to the ${SHORT_TITLE}"
fi
declare -n v="${FUNCNAME[0]}"; [[ ${v} != "true" ]] && STATUS_VARIABLES+=("${FUNCNAME[0]}='true'\n")
}
####
usage_and_exit()
{
local RET C MSG
RET=${1}
if [[ ${RET} -eq 0 ]]; then
C="${YELLOW}"
else
C="${RED}"
fi
MSG="Usage: ${ME} [--help] [--debug [...]] [--fix |--update | --restore | --function function]"
{
echo -e "\n${C}${MSG}${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 "'--fix' should only be used when instructed to by the Allsky Website."
echo
echo "'--update' should only be used when instructed to by the Allsky Website."
echo
echo "'--restore' restores ${PRIOR_ALLSKY_DIR} to ${ALLSKY_HOME}."
echo
echo "'--function' executes the specified function and quits."
echo
} >&2
exit_installation "${RET}"
}
####
# Get the branch of the release we are installing;
get_this_branch()
{
declare -n v="${FUNCNAME[0]}"; [[ ${v} == "true" ]] && return
local B # BRANCH is global
#shellcheck disable=SC2119
if ! B="$( get_branch )" ; then
display_msg --log warning "Unable to determine branch; assuming '${BRANCH}'."
else
BRANCH="${B}"
display_msg --logonly info "Using the '${BRANCH}' branch."
fi
STATUS_VARIABLES+=("BRANCH='${BRANCH}'\n")
STATUS_VARIABLES+=("${FUNCNAME[0]}='true'\n")
}
####
##### 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
if [[ -n ${CAMERA_TYPE} ]]; then
MSG="Unknown CAMERA_TYPE: '${CAMERA_TYPE}'"
else
MSG="'CAMERA_TYPE' not defined."
fi
display_msg --log error "${MSG}"
exit_installation 1 "${STATUS_ERROR}" "${MSG}"
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
if [[ -n ${CAMERA} ]]; then
MSG="Unknown CAMERA: '${CAMERA}'"
else
MSG="'CAMERA' not defined."
fi
display_msg --log error "Unknown CAMERA: '${CAMERA}'"
exit_installation 1 "${STATUS_CLEAR}" "${MSG}"
fi
}
#######
# Set up the file that contains information on all supported RPi cameras.
# Have separate function so it can be called from "--function".
setup_rpi_supported_cameras()
{
local CMD="${1}"
local notCMD
if [[ ! -f ${RPi_SUPPORTED_CAMERAS} ]]; then
local B="$( basename "${RPi_SUPPORTED_CAMERAS}" )"
if [[ -z ${CMD} ]]; then
notCMD="xxxxx" # won't match anything
CMD="all"
elif [[ ${CMD} == "raspistill" ]]; then
notCMD="libcamera"
else
notCMD="raspistill"
fi
local MSG="Creating ${RPi_SUPPORTED_CAMERAS} with '${CMD}' entries."
display_msg --log progress "${MSG}"
# Remove comment and blank lines and lines for the command we are NOT using.
grep -v -E "^\$|^#|^${notCMD}" "${ALLSKY_REPO}/${B}.repo" > "${RPi_SUPPORTED_CAMERAS}"
fi
}
#######
CONNECTED_CAMERA_MODELS=""
NUM_CONNECTED_CAMERAS=0
CT=() # Camera Type array - what to display in whiptail
get_connected_cameras()
{
local CMD CMD_RET CC MSG NUM_RPI=0 NUM_ZWO=0
# true == ignore errors. ${CMD} will be "" if no command found.
CMD="$( determineCommandToUse "false" "" "true" 2> /dev/null )"
CMD_RET=$? # return of 2 means no command was found
setup_rpi_supported_cameras "${CMD}" # Will create full file is CMD == ""
# RPi format: RPi \t camera_number \t camera_sensor [\t optional_other_stuff]
# ZWO format: ZWO \t camera_number \t camera_model
# "true" == ignore errors
get_connected_cameras_info --cmd "${CMD}" "true" > "${CONNECTED_CAMERAS_INFO}" 2>/dev/null
# Get the RPi connected cameras, if any.
CC=""
if [[ -n ${CMD} ]]; then
local RPI_MODELS="$( get_connected_camera_models --full "RPi" )"
# Output from above is:
# RPi \t camera_number \t camera_model \t camera_sensor
if [[ -n ${RPI_MODELS} ]]; then
CC="RPi"
local CT_ CN_ MODEL SENSOR
# shellcheck disable=SC2034
while read -r CT_ CN_ MODEL SENSOR
do
MODEL="${MODEL//++/ }"
SENSOR="${SENSOR//++/ }"
local FULL_NAME="${MODEL} (${SENSOR})"
[[ -z ${FUNCTION} ]] && display_msg --log progress "RPi ${FULL_NAME} camera found."
CT+=("${NUM_RPI};RPi;${MODEL}" "RPi ${FULL_NAME}")
((NUM_RPI++))
done <<<"${RPI_MODELS// /++}" # replace any spaces
fi
fi
# Get the ZWO connected cameras, if any.
local ZWO_MODELS="$( get_connected_camera_models "ZWO" )"
if [[ -n ${ZWO_MODELS} ]]; then
[[ -n ${CC} ]] && CC+=" "
CC+="ZWO"
for X in ${ZWO_MODELS// /++}
do
MODEL="${X//++/ }"
[[ -z ${FUNCTION} ]] && display_msg --log progress "ZWO ${MODEL} camera found."
CT+=( "${NUM_ZWO};ZWO;${MODEL}" "ZWO ${MODEL}" )
((NUM_ZWO++))
done
fi
NUM_CONNECTED_CAMERAS=$(( NUM_RPI + NUM_ZWO ))
if [[ ${NUM_CONNECTED_CAMERAS} -eq 0 ]]; then
MSG="No connected cameras were detected. The installation will exit."
MSG+="\nMake sure a camera is plugged in and working prior to restarting"
MSG+=" the installation."
whiptail --title "${TITLE}" --msgbox "${MSG}" 12 "${WT_WIDTH}" 3>&1 1>&2 2>&3
MSG="No connected cameras were detected."
local MSG2=""
if [[ ${CMD_RET} -eq 2 ]]; then
MSG2="No command to take RPi images was found"
MSG2+=" - make sure 'libcamera-apps' is installed if you have an RPi camera."
fi
display_msg --log error "${MSG}" "${MSG2}"
exit_installation 1 "${STATUS_NO_CAMERA}" ""
fi
declare -n v="${FUNCNAME[0]}";
[[ ${v} != "true" ]] && STATUS_VARIABLES+=("${FUNCNAME[0]}='true'\n")
# CONNECTED_CAMERAS_MODELS was set from a prior installation, if any.
# If it was set, warn the user if the prior models is different than
# the current ones, but it's not an error.
if [[ -n ${CONNECTED_CAMERA_MODELS} ]]; then
if [[ ${CONNECTED_CAMERA_MODELS} != "${CC}" ]]; then
MSG="Connected cameras were '${CONNECTED_CAMERA_MODELS}' during last installation"
MSG+=" but are '${CC}' now."
display_msg --log info "${MSG}"
STATUS_VARIABLES+=("CONNECTED_CAMERA_MODELS='${CC}'\n")
CONNECTED_CAMERA_MODELS="${CC}"
fi
return
fi
CONNECTED_CAMERA_MODELS="${CC}" # Either not set before or is different this time
}
#
# 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()
{
local MSG CAMERA NEW S CAMERA_INFO
# CAMERA_TYPE and NUM_CONNECTED_CAMERAS are global
if [[ ${USE_PRIOR_ALLSKY} == "true" ]]; then
# bash doesn't have ">=" so we have to use "! ... < "
if [[ ! ${PRIOR_ALLSKY_VERSION} < "${FIRST_CAMERA_TYPE_BASE_VERSION}" ]]; then
# New style Allsky using ${CAMERA_TYPE}.
CAMERA_TYPE="${PRIOR_CAMERA_TYPE}"
if [[ -n ${CAMERA_TYPE} ]]; then
MSG="Using Camera Type '${CAMERA_TYPE}' from prior Allsky; not prompting user."
display_msg --logonly info "${MSG}"
STATUS_VARIABLES+=("CAMERA_TYPE='${CAMERA_TYPE}'\n")
if [[ -n ${CAMERA_MODEL} ]]; then
STATUS_VARIABLES+=("CAMERA_MODEL='${CAMERA_MODEL}'\n")
fi
if [[ -n ${CAMERA_NUMBER} ]]; then
STATUS_VARIABLES+=("CAMERA_NUMBER='${CAMERA_NUMBER}'\n")
fi
STATUS_VARIABLES+=("${FUNCNAME[0]}='true'\n")
return
else
MSG="Camera Type not in prior new-style settings file."
display_msg --log error "${MSG}"
exit_installation 2 "${STATUS_NO_CAMERA}" "${MSG}"
fi
else
# Older style using ${CAMERA}
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}."
STATUS_VARIABLES+=("CAMERA_TYPE='${CAMERA_TYPE}'\n")
# Old style doesn't have CAMERA_MODEL or CAMERA_NUMBER.
STATUS_VARIABLES+=("${FUNCNAME[0]}='true'\n")
return
else
MSG="CAMERA not in old-style '${PRIOR_CONFIG_FILE}'.sh."
display_msg --log warning "${MSG}"
fi
fi
fi
S=" is"
[[ ${NUM_CONNECTED_CAMERAS} -gt 1 ]] && S="s are"
MSG="\nThe following camera${S} connected to the Pi.\n"
[[ ${NUM_CONNECTED_CAMERAS} -gt 1 ]] && MSG+="Pick the one you want."
MSG+="\nIf it's not in the list, select <Cancel> and determine why."
if ! CAMERA_INFO=$( whiptail --title "${TITLE}" --notags --menu "${MSG}" 15 "${WT_WIDTH}" \
"${NUM_CONNECTED_CAMERAS}" "${CT[@]}" 3>&1 1>&2 2>&3 ) ; then
MSG="Camera selection required."
MSG+=" Please re-run the installation and select a camera to continue."
display_msg --log warning "${MSG}"
exit_installation 2 "${STATUS_NO_CAMERA}" "User did not select a camera."
fi
# CAMERA_INFO is: number;type;model
CAMERA_NUMBER="${CAMERA_INFO%%;*}" # before first ";"
CAMERA_MODEL="${CAMERA_INFO##*;}" # after last ";"
CAMERA_INFO="${CAMERA_INFO/${CAMERA_NUMBER};/}" # Now: type;model
CAMERA_TYPE="${CAMERA_INFO%;*}" # before ";"
display_msg --log progress "Using user-selected ${CAMERA_TYPE} ${CAMERA_MODEL} camera."
STATUS_VARIABLES+=("CAMERA_TYPE='${CAMERA_TYPE}'\n")
STATUS_VARIABLES+=("CAMERA_MODEL='${CAMERA_MODEL}'\n")
STATUS_VARIABLES+=("CAMERA_NUMBER='${CAMERA_NUMBER}'\n")
STATUS_VARIABLES+=("${FUNCNAME[0]}='true'\n")
}
####
# Wrapper function to call do_save_camera_capabilities and exit on error.
save_camera_capabilities()
{
declare -n v="${FUNCNAME[0]}"; [[ ${v} == "true" ]] && return
do_save_camera_capabilities "${1}"
[[ $? -ne 0 ]] && exit_with_image 1 "${STATUS_ERROR}" "${FUNCNAME[0]} failed."
STATUS_VARIABLES+=("${FUNCNAME[0]}='true'\n")
}
####
# 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.
do_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
local OPTIONSFILEONLY="${1}" # Set to "true" if we should ONLY create the options file.
local FORCE MSG OPTIONSONLY ERR M RET
# CAMERA_MODEL is global
# 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_DEFAULT_SETTINGS_FILE} == "true" ]]; then
FORCE=" --force"
MSG=" and default settings"
else
FORCE=""
MSG=""
fi
if [[ ${OPTIONSFILEONLY} == "true" ]]; then
OPTIONSONLY=" --optionsOnly"
else
OPTIONSONLY=""
MSG="Setting up WebUI options${MSG} for ${CAMERA_TYPE} cameras."
display_msg --log progress "${MSG}"
fi
# Restore the prior settings file or camera-specific settings file(s) if present so
# the appropriate one can be used by makeChanges.sh.
[[ -n ${PRIOR_SETTINGS_FILE} ]] && restore_prior_settings_file
display_msg --log progress "Making new settings file '${SETTINGS_FILE}'."
CMD="makeChanges.sh${FORCE}${OPTIONSONLY}"
CMD+=" --cameraTypeOnly --fromInstall --addNewSettings ${DEBUG_ARG}"
#shellcheck disable=SC2089
CMD+=" cameranumber 'Camera Number' '${PRIOR_CAMERA_NUMBER}' '${CAMERA_NUMBER}'"
#shellcheck disable=SC2089
CMD+=" cameramodel 'Camera Model' '${PRIOR_CAMERA_MODEL}' '${CAMERA_MODEL}'"
# cameratype needs to come last.
#shellcheck disable=SC2089
CMD+=" cameratype 'Camera Type' '${PRIOR_CAMERA_TYPE}' '${CAMERA_TYPE}'"
MSG="Executing ${CMD}"
display_msg "${LOG_TYPE}" info "${MSG}"
local TMP="${ALLSKY_LOGS}/makeChanges.log"
#shellcheck disable=SC2086,SC2090
M="$( eval "${ALLSKY_SCRIPTS}/"${CMD} 2> "${TMP}" )"
RET=$?
if [[ ${RET} -ne 0 ]]; then
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+="After connecting your camera, re-run the installation."
whiptail --title "${TITLE}" --msgbox "${MSG}" 12 "${WT_WIDTH}" 3>&1 1>&2 2>&3
display_msg --log error "No camera detected - installation aborted."
[[ -s ${TMP} ]] && display_msg --log error "$( < "${TMP}" )"
exit_with_image 1 "${STATUS_ERROR}" "No camera detected"
elif [[ ${OPTIONSFILEONLY} == "false" ]]; then
display_msg --log error "Unable to save camera capabilities."
[[ -s ${TMP} ]] && display_msg --log error "$( < "${TMP}" )"
[[ -n ${M} ]] && display_msg --log error "${M}"
fi
return 1
else
[[ -n ${M} ]] && display_msg --logonly info "${M}"
if [[ ! -f ${SETTINGS_FILE} ]]; then
display_msg --log error "Settings file not created; cannot continue."
return 1
fi
fi
#shellcheck disable=SC2012
MSG="$( /bin/ls -l "${ALLSKY_CONFIG}/settings"*.json 2>/dev/null | sed 's/^/ /' )"
display_msg --logonly info "Settings files:\n${MSG}"
CAMERA_MODEL="$( settings ".cameramodel" "${SETTINGS_FILE}" )"
if [[ -z ${CAMERA_MODEL} ]]; then
display_msg --log error "cameramodel not found in settings file."
return 1
fi
return 0
}
####
# If the raspistill command exists on post-Buster releases,
# rename it so it's not used.
check_for_raspistill()
{
declare -n v="${FUNCNAME[0]}"; [[ ${v} == "true" ]] && return
local W
if W="$( which raspistill )" && [[ ${PI_OS} != "buster" ]]; then
display_msg --longonly info "Renaming 'raspistill' on ${PI_OS}."
sudo mv "${W}" "${W}-OLD"
fi
STATUS_VARIABLES+=("${FUNCNAME[0]}='true'\n")
}
####
# Get a count of the number of the specified file in the specified directory.
get_count()
{
local DIR="${1}"
local FILENAME="${2}"
find "${DIR}" -maxdepth 1 -name "${FILENAME}" | wc -l
}
####
# Update various PHP define() variables.
update_php_defines()
{
declare -n v="${FUNCNAME[0]}"; [[ ${v} == "true" ]] && return
[[ ${SKIP} == "true" ]] && return
display_msg --log progress "Modifying variables for WebUI and Website."
local FILE="${ALLSKY_WEBUI}/includes/${ALLSKY_DEFINES_INC}"
sed -e "s;XX_HOME_XX;${HOME};g" \
-e "s;XX_ALLSKY_HOME_XX;${ALLSKY_HOME};g" \
-e "s;XX_ALLSKY_CONFIG_XX;${ALLSKY_CONFIG};g" \
-e "s;XX_ALLSKY_SCRIPTS_XX;${ALLSKY_SCRIPTS};g" \
-e "s;XX_ALLSKY_TMP_XX;${ALLSKY_TMP};g" \
-e "s;XX_ALLSKY_IMAGES_XX;${ALLSKY_IMAGES};g" \
-e "s;XX_ALLSKY_MESSAGES_XX;${ALLSKY_MESSAGES};g" \
-e "s;XX_ALLSKY_CHECK_ALLSKY_LOG_XX;${CHECK_ALLSKY_LOG};g" \
-e "s;XX_ALLSKY_WEBUI_XX;${ALLSKY_WEBUI};g" \
-e "s;XX_ALLSKY_WEBSITE_XX;${ALLSKY_WEBSITE};g" \
-e "s;XX_ALLSKY_WEBSITE_LOCAL_CONFIG_NAME_XX;${ALLSKY_WEBSITE_CONFIGURATION_NAME};g" \
-e "s;XX_ALLSKY_WEBSITE_REMOTE_CONFIG_NAME_XX;${ALLSKY_REMOTE_WEBSITE_CONFIGURATION_NAME};g" \
-e "s;XX_ALLSKY_WEBSITE_LOCAL_CONFIG_XX;${ALLSKY_WEBSITE_CONFIGURATION_FILE};g" \
-e "s;XX_ALLSKY_WEBSITE_REMOTE_CONFIG_XX;${ALLSKY_REMOTE_WEBSITE_CONFIGURATION_FILE};g" \
-e "s;XX_ALLSKY_OVERLAY_XX;${ALLSKY_OVERLAY};g" \
-e "s;XX_ALLSKY_ENV_XX;${ALLSKY_ENV};g" \
-e "s;XX_MY_OVERLAY_TEMPLATES_XX;${MY_OVERLAY_TEMPLATES};g" \
-e "s;XX_ALLSKY_MODULES_XX;${ALLSKY_MODULES};g" \
-e "s;XX_ALLSKY_MODULE_LOCATION_XX;${ALLSKY_MODULE_LOCATION};g" \
-e "s;XX_ALLSKY_OWNER_XX;${ALLSKY_OWNER};g" \
-e "s;XX_ALLSKY_GROUP_XX;${ALLSKY_GROUP};g" \
-e "s;XX_WEBSERVER_OWNER_XX;${WEBSERVER_OWNER};g" \
-e "s;XX_WEBSERVER_GROUP_XX;${WEBSERVER_GROUP};g" \
-e "s;XX_ALLSKY_REPO_XX;${ALLSKY_REPO};g" \
-e "s;XX_ALLSKY_VERSION_XX;${ALLSKY_VERSION};g" \
-e "s;XX_ALLSKY_STATUS_XX;${ALLSKY_STATUS};g" \
-e "s;XX_RASPI_CONFIG_XX;${ALLSKY_CONFIG};g" \
"${REPO_WEBUI_DEFINES_FILE}" > "${FILE}"
chmod 644 "${FILE}"
# Don't save status if we did a fix.
if [[ ${FIX} == "false" ]]; then
STATUS_VARIABLES+=("${FUNCNAME[0]}='true'\n")
fi
}
####
# Recreate the options file.
# This can be used after installation if the options file gets hosed.
recreate_options_file()
{
CAMERA_TYPE="$( settings ".cameratype" )"
save_camera_capabilities "true"
set_permissions
}
####
# Update the sudoers file so the web server can execute certain commands with sudo.
do_sudoers()
{
declare -n v="${FUNCNAME[0]}"; [[ ${v} == "true" ]] && return
[[ ${SKIP} == "true" ]] && return
display_msg --log progress "Creating/updating sudoers file."
sed -e "s;XX_ALLSKY_SCRIPTS_XX;${ALLSKY_SCRIPTS};" "${REPO_SUDOERS_FILE}" > "${TMP_FILE}"
sudo install -m 0644 "${TMP_FILE}" "${FINAL_SUDOERS_FILE}" && rm -f "${TMP_FILE}"
STATUS_VARIABLES+=("${FUNCNAME[0]}='true'\n")
}
####
# Ask the user if they want to reboot.
# Call every time in case they change their mind.
WILL_REBOOT="false"
ask_reboot()
{
local TYPE="${1}"
local MSG AT
if [[ ${TYPE} == "locale" ]]; then
MSG="A reboot is needed for the locale change to take effect."
MSG+="\nYou must reboot before continuing the installation."
MSG+="\n\nReboot now?"
if whiptail --title "${TITLE}" --yesno "${MSG}" 18 "${WT_WIDTH}" 3>&1 1>&2 2>&3; then
MSG="\nAfter the reboot you MUST continue with the installation"
MSG+=" before anything will work."
MSG+="\nTo restart the installation, do the following:\n"
MSG+="\n cd ~/allsky"
MSG+="\n ./install.sh"
MSG+="\n\nThe installation will pick up where it left off."
whiptail --title "${TITLE}" --msgbox "${MSG}" 15 "${WT_WIDTH}" 3>&1 1>&2 2>&3
return 0
else
REBOOT_NEEDED="true"
return 1
fi
fi
AT=" http://${NEW_HOST_NAME}.local\n"
AT+="or\n"
AT+=" http://$( hostname -I | sed -e 's/ .*$//' )"
if [[ ${REBOOT_NEEDED} == "false" ]]; then
MSG="\nAfter installation you can connect to the WebUI at:\n${AT}"
display_msg -log progress "${MSG}"
return 0
fi
MSG="*** Allsky installation is almost done. ***"
MSG+="\n\nWhen done, you must reboot the Raspberry Pi to finish the installation."
MSG+="\n\nAfter reboot you can connect to the WebUI at:\n"
MSG+="${AT}"
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."
MSG="If you have not already rebooted your Pi, please do so now.\n"
MSG+="You can then connect to the WebUI at:\n"
MSG+="${AT}"
"${ALLSKY_SCRIPTS}/addMessage.sh" "info" "${MSG}"
fi
}
do_reboot()
{
exit_installation -1 "${1}" "${2}" # -1 means just log ending statement but don't exit.
sudo reboot now
}
####
# 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
mkdir -p "${TMP_DIR}"
find "${ALLSKY_TMP}" \( -name '*.jpg' -o -name '*.png' \) -exec mv '{}' "${TMP_DIR}" \;
rm -fr "${ALLSKY_TMP:?}"/*
else
mkdir "${ALLSKY_TMP}"
fi
# Now mount and restore any images that were there before
sudo systemctl daemon-reload 2> /dev/null
sudo mount -a || display_msg --log warning "Unable to mount '${ALLSKY_TMP}'."
if [[ -d ${TMP_DIR} ]]; then
mv "${TMP_DIR}"/* "${ALLSKY_TMP}" 2>/dev/null
rmdir "${TMP_DIR}"
fi
}
####
# Run apt-get, first checking if it's locked.
run_aptGet()
{
local NUM_FAILS=0
while sudo fuser --silent "/var/lib/dpkg/lock-frontend" ;
do
(( NUM_FAILS++ ))
if [[ ${NUM_FAILS} -eq 5 ]]; then
echo "apt-get is locked. Tried 5 times." >&2
echo "Wait a while and try the Allsky installation again." >&2
return 1
fi
sleep 3
done
sudo apt-get --assume-yes install "${@}"
}
####
# If the return code -ne 0
check_success()
{
local RET=${1}
local MESSAGE="${2}"
local LOG="${3}"
local D=${4}
local MSG
if [[ ${RET} -ne 0 ]]; then
display_msg --log error "${MESSAGE}"
MSG="The full log file is in ${LOG}\nThe end of the file is:"
display_msg --log info "${MSG}"
indent "$( tail "${LOG}" )"
return 1
fi
[[ ${D} -gt 1 ]] && cat "${LOG}"
return 0
}
####
# Get checksums of local Website before the user changes anything.
# We don't use this but it's used if the user installs a remote Website.
get_checksums()
{
declare -n v="${FUNCNAME[0]}"
[[ -s ${ALLSKY_WEBSITE_CHECKSUM_FILE} ]] && return
get_website_checksums > "${ALLSKY_WEBSITE_CHECKSUM_FILE}"
STATUS_VARIABLES+=("${FUNCNAME[0]}='true'\n")
}
####
# Install the web server.
install_webserver_et_al()
{
declare -n v="${FUNCNAME[0]}"
[[ ${SKIP} == "true" ]] && return
sudo systemctl stop hostapd 2>/dev/null
sudo systemctl stop lighttpd 2>/dev/null
if [[ ${v} == "true" ]]; then
# Already installed it; just configure it.
display_msg --log progress "Preparing the web server."
else
display_msg --log progress "Installing the web server."
TMP="${ALLSKY_LOGS}/lighttpd.install.log"
run_aptGet lighttpd php-cgi php-gd hostapd dnsmasq avahi-daemon hwinfo > "${TMP}" 2>&1
check_success $? "lighttpd installation failed" "${TMP}" "${DEBUG}" ||
exit_with_image 1 "${STATUS_ERROR}" "lighttpd installation failed"
fi
create_lighttpd_config_file
create_lighttpd_log_file
# Ignore output since it may already be enabled.
sudo lighty-enable-mod fastcgi-php > /dev/null 2>&1
TMP="${ALLSKY_LOGS}/lighttpd.start.log"
#shellcheck disable=SC2024
sudo systemctl start lighttpd > "${TMP}" 2>&1
check_success $? "Unable to start lighttpd" "${TMP}" "${DEBUG}"
# Starting it added an entry so truncate the file so it's 0-length
sleep 1; truncate -s 0 "${LIGHTTPD_LOG_FILE}"
STATUS_VARIABLES+=("${FUNCNAME[0]}='true'\n")
}
####
# Prompt for a new hostname if needed,
# and update all the files that contain the hostname.
# The default hostname in Pi OS is "raspberrypi"; if it's still that,
# prompt to update. If it's anything else that means the user
# already changed it to something so don't overwrite their change.
prompt_for_hostname()
{
declare -n v="${FUNCNAME[0]}"; [[ ${v} == "true" ]] && return
local CURRENT_HOSTNAME=$( tr -d " \t\n\r" < /etc/hostname )
if [[ ${CURRENT_HOSTNAME} != "raspberrypi" ]]; then
display_msg --logonly info "Using current hostname of '${CURRENT_HOSTNAME}'."
NEW_HOST_NAME="${CURRENT_HOSTNAME}"
STATUS_VARIABLES+=("${FUNCNAME[0]}='true'\n")
STATUS_VARIABLES+=("NEW_HOST_NAME='${NEW_HOST_NAME}'\n")
return
fi
MSG="Please enter a hostname for your Pi."
MSG+="\n\nIf you have more than one Pi on your network they MUST all have unique names."
MSG+="\n\nThe current hostname is '${CURRENT_HOSTNAME}'; the suggested name is below:\n"
NEW_HOST_NAME=$( whiptail --title "${TITLE}" --inputbox "${MSG}" 15 "${WT_WIDTH}" \
"${SUGGESTED_NEW_HOST_NAME}" 3>&1 1>&2 2>&3 )
if [[ $? -ne 0 ]]; then
MSG="You must specify a host name."
MSG+=" Please re-run the installation and select one."
display_msg --log warning "${MSG}"
exit_installation 2 "No host name selected"
fi
STATUS_VARIABLES+=("NEW_HOST_NAME='${NEW_HOST_NAME}'\n")
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${TAB}${NEW_HOST_NAME}/" /etc/hosts
# else, they didn't change the default name, but that's their problem...
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_FILE}"
sudo install -m 0644 "${TMP_FILE}" "${FINAL_AVI_FILE}" && rm -f "${TMP_FILE}"
fi
STATUS_VARIABLES+=("${FUNCNAME[0]}='true'\n")
}
####
# Set permissions on various web-related items.
# Do every time - doesn't hurt to re-do them.
set_permissions()
{
display_msg --log progress "Setting permissions on various files."
# Make sure the currently running user is in the right groups.
# "sudo" allows them to run sudo on anything.
# "${WEBSERVER_GROUP}" allows the web server to write files to Allsky directories.
# "video" allows the user to access video devices
local G="$( id "${ALLSKY_OWNER}" )"
for g in "sudo" "${WEBSERVER_GROUP}" "video"
do
#shellcheck disable=SC2076
if ! [[ ${G} =~ "(${g})" ]]; then
display_msg --log progress "Adding ${ALLSKY_OWNER} to ${g} group."
sudo adduser --quiet "${ALLSKY_OWNER}" "${g}"
fi
done
# These directories aren't in GitHub so need to be manually created.
mkdir -p \
"${ALLSKY_EXTRA}" \
"${ALLSKY_MYFILES_DIR}"
# 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}"
# Modules and overlays
sudo mkdir -p "${ALLSKY_MODULE_LOCATION}/modules"
sudo chgrp -R "${WEBSERVER_GROUP}" "${ALLSKY_MODULE_LOCATION}"
sudo chmod -R 775 "${ALLSKY_MODULE_LOCATION}"
# 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 '{}' \;
sudo find "${ALLSKY_WEBUI}/" -type d -exec chmod 755 '{}' \;
# Exceptions to files at 644:
chmod 775 "${ALLSKY_TMP}"
sudo chgrp "${WEBSERVER_GROUP}" "${ALLSKY_TMP}"
#### Website files
chmod 664 "${ALLSKY_ENV}"
sudo chgrp "${WEBSERVER_GROUP}" "${ALLSKY_ENV}"
# These directories aren't in GitHub so need to be manually created.
mkdir -p \
"${ALLSKY_WEBSITE_MYFILES_DIR}" \
"${ALLSKY_WEBSITE}/videos/thumbnails" \
"${ALLSKY_WEBSITE}/keograms/thumbnails" \
"${ALLSKY_WEBSITE}/startrails/thumbnails"
# Not everything in the Website needs to be writable by the web server,
# but make them all that way so we don't worry about missing something.
sudo find "${ALLSKY_WEBSITE}" -type d -exec chmod 775 '{}' \;
sudo find "${ALLSKY_WEBSITE}" -type f -exec chmod 664 '{}' \;
sudo chgrp --recursive "${WEBSERVER_GROUP}" "${ALLSKY_WEBSITE}"
# Get the session handler type from th ephp ini file
SESSION_HANDLER="$( get_php_setting "session.save_handler" )"
# We need to make changes if the handler is using the filesystem
if [[ ${SESSION_HANDLER} == "files" ]]; then
# Get the path to the php sessions
SESSION_PATH="$( get_php_setting "session.save_path" )"
# Loop over all files in the session folder and if any are not owned by the
# web server user then changs ALL of the php sessions to be owned by the
# web server user
sudo find "${SESSION_PATH}" -type f -print0 | while read -r -d $'\0' SESSION_FILE
do
OWNER="$( sudo stat -c '%U' "${SESSION_FILE}" )"
if [[ ${OWNER} != "${WEBSERVER_OWNER}" ]]; then
display_msg --log info "Found php sessions with wrong owner - fixing them"