3 # Desc: Records gps data until midnight
4 # Author: Steven Baltakatei Sandoval; License: GPLv3+
5 # Usage: bkgpslog -o [output dir]
7 #==BEGIN Define script parameters==
8 ## Logging Behavior parameters
9 BUFFER_TTL
="300"; # time between file writes
10 SCRIPT_TTL
="day"; # (day|hour)
11 #### TZ="UTC"; export TZ; # Default time zone; overridden by '--time-zone=[str]' option
12 DIR_TMP_DEFAULT
="/dev/shm"; # Default parent of working directory
14 SCRIPT_TIME_START
=$
(date +%Y
%m
%dT
%H
%M
%S.
%N
);
15 PATH
="$HOME/.local/bin:$PATH"; # Add "$(systemd-path user-binaries)" path in case apps saved there
16 SCRIPT_HOSTNAME
=$
(hostname
); # Save hostname of system running this script.
17 SCRIPT_VERSION
="0.3.9"; # Define version of script.
18 SCRIPT_NAME
="bkgpslog"; # Define basename of script file.
19 SCRIPT_URL
="https://gitlab.com/baltakatei/ninfacyzga-01"; # Define wesite hosting this script.
20 AGE_VERSION
="1.0.0-beta2"; # Define version of age (encryption program)
21 AGE_URL
="https://github.com/FiloSottile/age/releases/tag/v1.0.0-beta2"; # Define website hosting age.
23 declare -Ag appRollCall
# Associative array for storing app status
24 declare -Ag fileRollCall
# Associative array for storing file status
25 declare -Ag dirRollCall
# Associative array for storing dir status
26 declare -a argRecPubKeys
# for processArguments function
28 ## Initialize variables
29 OPTION_VERBOSE
=""; OPTION_ENCRYPT
=""; OPTION_COMPRESS
=""; OPTION_TMPDIR
="";
31 #===BEGIN Declare local script functions===
33 # Desc: If arg is a command, save result in assoc array 'appRollCall'
34 # Usage: checkapp arg1 arg2 arg3 ...
35 # Input: global assoc. array 'appRollCall'
36 # Output: adds/updates key(value) to global assoc array 'appRollCall'
38 #echo "DEBUG:$(date +%S.%N)..Starting checkapp function."
39 #echo "DEBUG:args: $@"
40 #echo "DEBUG:returnState:$returnState"
44 #echo "DEBUG:processing arg:$arg"
45 if command -v "$arg" 1>/dev
/null
2>&1; then # Check if arg is a valid command
46 appRollCall
[$arg]="true";
47 #echo "DEBUG:appRollCall[$arg]:"${appRollCall[$arg]}
48 if ! [ "$returnState" = "false" ]; then returnState
="true"; fi
50 appRollCall
[$arg]="false"; returnState
="false";
54 #for key in "${!appRollCall[@]}"; do echo "DEBUG:$key => ${appRollCall[$key]}"; done
55 #echo "DEBUG:evaluating returnstate. returnState:"$returnState
57 #===Determine function return code===
58 if [ "$returnState" = "true" ]; then
59 #echo "DEBUG:checkapp returns true for $arg";
62 #echo "DEBUG:checkapp returns false for $arg";
65 } # Check that app exists
67 # Desc: If arg is a file path, save result in assoc array 'fileRollCall'
68 # Usage: checkfile arg1 arg2 arg3 ...
69 # Input: global assoc. array 'fileRollCall'
70 # Output: adds/updates key(value) to global assoc array 'fileRollCall';
71 # Output: returns 0 if app found, 1 otherwise
76 #echo "DEBUG:processing arg:$arg"
77 if [ -f "$arg" ]; then
78 fileRollCall
["$arg"]="true";
79 #echo "DEBUG:fileRollCall[\"$arg\"]:"${fileRollCall["$arg"]}
80 if ! [ "$returnState" = "false" ]; then returnState
="true"; fi
82 fileRollCall
["$arg"]="false"; returnState
="false";
86 #for key in "${!fileRollCall[@]}"; do echo "DEBUG:fileRollCall key [$key] is:${fileRollCall[$key]}"; done
87 #echo "DEBUG:evaluating returnstate. returnState:"$returnState
89 #===Determine function return code===
90 if [ "$returnState" = "true" ]; then
91 #echo "DEBUG:checkapp returns true for $arg";
94 #echo "DEBUG:checkapp returns false for $arg";
97 } # Check that file exists
99 # Desc: If arg is a dir path, save result in assoc array 'dirRollCall'
100 # Usage: checkdir arg1 arg2 arg3 ...
101 # Input: global assoc. array 'dirRollCall'
102 # Output: adds/updates key(value) to global assoc array 'dirRollCall';
103 # Output: returns 0 if app found, 1 otherwise
108 #echo "DEBUG:processing arg:$arg"
109 if [ -d "$arg" ]; then
110 dirRollCall
["$arg"]="true";
111 #echo "DEBUG:dirRollCall[\"$arg\"]:"${dirRollCall["$arg"]}
112 if ! [ "$returnState" = "false" ]; then returnState
="true"; fi
113 elif [ "$arg" = "" ]; then
114 dirRollCall
["$arg"]="false"; returnState
="false";
120 #for key in "${!dirRollCall[@]}"; do echo "DEBUG:dirRollCall key [$key] is:${dirRollCall[$key]}"; done
121 #echo "DEBUG:evaluating returnstate. returnState:"$returnState
123 #===Determine function return code===
124 if [ "$returnState" = "true" ]; then
125 #echo "DEBUG:checkapp returns true for $arg";
128 #echo "DEBUG:checkapp returns false for $arg";
131 } # Check that dir exists
133 # Yell, Die, Try Three-Fingered Claw technique
134 # Ref/Attrib: https://stackoverflow.com/a/25515370
135 yell
() { echo "$0: $*" >&2; }
136 die
() { yell
"$*"; exit 111; }
137 try
() { "$@" || die
"cannot $*"; }
140 echo "$@" 1>&2; # Define stderr echo function.
141 } # Define stderr message function.
144 echoerr
" bkgpslog [ options ]"
147 echoerr
" -h, --help"
148 echoerr
" Display help information."
150 echoerr
" Display script version."
151 echoerr
" -v, --verbose"
152 echoerr
" Display debugging info."
153 echoerr
" -e, --encrypt"
154 echoerr
" Encrypt output."
155 echoerr
" -r, --recipient [ pubkey string ]"
156 echoerr
" Specify recipient. May be age or ssh pubkey."
157 echoerr
" See https://github.com/FiloSottile/age"
158 echoerr
" -o, --output [ directory ]"
159 echoerr
" Specify output directory to save logs."
160 echoerr
" -c, --compress"
161 echoerr
" Compress output with gzip (before encryption if enabled)."
162 echoerr
" -z, --time-zone"
163 echoerr
" Specify time zone. (ex: \"America/New_York\")"
164 echoerr
" -t, --temp-dir"
165 echoerr
" Specify parent directory for temporary working directory."
166 echoerr
" Default: \"/dev/shm\""
167 echoerr
" -R, --recipient-dir"
168 echoerr
" Specify directory containing files whose first lines are"
169 echoerr
" to be interpreted as pubkey strings (see \'-r\' option)."
171 echoerr
"EXAMPLE: (bash script lines)"
172 echoerr
"/bin/bash bkgpslog -v -e -c \\"
173 echoerr
"-z \"UTC\" -t \"/dev/shm\" \\"
174 echoerr
"-r age1mrmfnwhtlprn4jquex0ukmwcm7y2nxlphuzgsgv8ew2k9mewy3rs8u7su5 \\"
175 echoerr
"-r age1ala848kqrvxc88rzaauc6vc5v0fqrvef9dxyk79m0vjea3hagclswu0lgq \\"
176 echoerr
"-o ~/Sync/Location"
177 } # Display information on how to use this script.
179 echoerr
"$SCRIPT_VERSION"
180 } # Display script version.
182 # Usage: vbm "DEBUG:verbose message here"
183 # Description: Prints verbose message ("vbm") to stderr if OPTION_VERBOSE is set to "true".
185 # - OPTION_VERBOSE variable set by processArguments function. (ex: "true", "false")
186 # - "$@" positional arguments fed to this function.
188 # Script function dependencies: echoerr
189 # External function dependencies: echo
190 # Last modified: 2020-04-11T23:57Z
191 # Last modified by: Steven Baltakatei Sandoval
195 if [ "$OPTION_VERBOSE" = "true" ]; then
196 FUNCTION_TIME
=$
(date --iso-8601=ns
); # Save current time in nano seconds.
197 echoerr
"[$FUNCTION_TIME] ""$*"; # Display argument text.
201 return 0; # Function finished.
202 } # Verbose message display function.
204 while [ ! $# -eq 0 ]; do # While number of arguments ($#) is not (!) equal to (-eq) zero (0).
205 #echoerr "DEBUG:Starting processArguments while loop."
206 #echoerr "DEBUG:Provided arguments are:""$*"
208 -h |
--help) showUsage
; exit 1;; # Display usage.
209 --version) showVersion
; exit 1;; # Show version
210 -v |
--verbose) OPTION_VERBOSE
="true"; vbm
"DEBUG:Verbose mode enabled.";; # Enable verbose mode.
211 -o |
--output) if [ -d "$2" ]; then DIR_OUT
="$2"; vbm
"DEBUG:DIR_OUT:$DIR_OUT"; shift; fi ;; # Define output directory.
212 -e |
--encrypt) OPTION_ENCRYPT
="true"; vbm
"DEBUG:Encrypted output mode enabled.";;
213 -r |
--recipient) # Add 'age' recipient via public key string
214 argRecPubKeys
+=("$2"); vbm
"STATUS:pubkey added:""$2"; shift;;
215 -c |
--compress) OPTION_COMPRESS
="true"; vbm
"DEBUG:Compressed output mode enabled.";;
216 -z |
--time-zone) try setTimeZoneEV
"$2"; shift;;
217 -t |
--temp-dir) OPTION_TMPDIR
="true" && argTmpDirPriority
="$2"; shift;;
218 -R |
--recipient-dir) OPTION_RECDIR
="true" && argRecDir
="$2"; shift;;
219 *) echoerr
"ERROR: Unrecognized argument: $1"; echoerr
"STATUS:All arguments:$*"; exit 1;; # Handle unrecognized options.
223 } # Argument Processing
225 # Desc: Set time zone environment variable TZ
226 # Usage: setTimeZoneEV arg1
227 # Input: arg1: 'date'-compatible timezone string (ex: "America/New_York")
228 # TZDIR env var (optional; default: "/usr/share/zoneinfo")
230 # exit code 0 on success
231 # exit code 1 on incorrect number of arguments
232 # exit code 2 if unable to validate arg1
233 # Depends: yell, printenv, bash 5
234 # Tested on: Debian 10
236 local tzDir returnState
237 if ! [[ $# -eq 1 ]]; then
238 yell
"ERROR:Invalid argument count.";
242 # Read TZDIR env var if available
243 if printenv TZDIR
1>/dev
/null
2>&1; then
244 tzDir
="$(printenv TZDIR)";
246 tzDir
="/usr/share/zoneinfo";
250 if ! [[ -f "$tzDir"/"$ARG1" ]]; then
251 yell
"ERROR:Invalid time zone argument.";
254 # Export ARG1 as TZ environment variable
255 TZ
="$ARG1" && export TZ
&& returnState
="true";
258 # Determine function return code
259 if [ "$returnState" = "true" ]; then
262 } # Exports TZ environment variable
264 # Desc: Report seconds until next day.
266 # Output: stdout: integer seconds until next day
267 # Output: exit code 0 if stdout > 0; 1 if stdout = 0; 2 if stdout < 0
268 # Usage: timeUntilNextDay
269 # Usage: if ! myTTL="$(timeUntilNextDay)"; then yell "ERROR in if statement"; exit 1; fi
270 # Depends: date 8, echo 8, yell, try
272 local returnState TIME_CURRENT TIME_NEXT_DAY SECONDS_UNTIL_NEXT_DAY
274 TIME_CURRENT
="$(date --iso-8601=seconds)" ; # Produce `date`-parsable current timestamp with resolution of 1 second.
275 TIME_NEXT_DAY
="$(date -d "$TIME_CURRENT next day
" --iso-8601=date)"; # Produce timestamp of beginning of tomorrow with resolution of 1 second.
276 SECONDS_UNTIL_NEXT_DAY
="$(( $(date +%s -d "$TIME_NEXT_DAY") - $(date +%s -d "$TIME_CURRENT") ))" ; # Calculate seconds until closest future midnight (res. 1 second).
277 if [[ "$SECONDS_UNTIL_NEXT_DAY" -gt 0 ]]; then
279 elif [[ "$SECONDS_UNTIL_NEXT_DAY" -eq 0 ]]; then
280 returnState
="warning_zero";
281 yell
"WARNING:Reported time until next day exactly zero.";
282 elif [[ "$SECONDS_UNTIL_NEXT_DAY" -lt 0 ]]; then
283 returnState
="warning_negative";
284 yell
"WARNING:Reported time until next day is negative.";
287 try
echo "$SECONDS_UNTIL_NEXT_DAY"; # Report
289 # Determine function return code
290 if [[ "$returnState" = "true" ]]; then
292 elif [[ "$returnState" = "warning_zero" ]]; then
294 elif [[ "$returnState" = "warning_negative" ]]; then
297 } # Report seconds until next day
299 # Desc: Report seconds until next hour
301 # Output: stdout: integer seconds until next hour
302 # Output: exit code 0 if stdout > 0; 1 if stdout = 0; 2 if stdout < 0
303 # Usage: timeUntilNextHour
304 # Usage: if ! myTTL="$(timeUntilNextHour)"; then yell "ERROR in if statement"; exit 1; fi
306 local returnState TIME_CURRENT TIME_NEXT_HOUR SECONDS_UNTIL_NEXT_HOUR
307 TIME_CURRENT
="$(date --iso-8601=seconds)"; # Produce `date`-parsable current timestamp with resolution of 1 second.
308 TIME_NEXT_HOUR
="$(date -d "$TIME_CURRENT next hour
" --iso-8601=hours)"; # Produce `date`-parsable current time stamp with resolution of 1 second.
309 SECONDS_UNTIL_NEXT_HOUR
="$(( $(date +%s -d "$TIME_NEXT_HOUR") - $(date +%s -d "$TIME_CURRENT") ))"; # Calculate seconds until next hour (res. 1 second).
310 if [[ "$SECONDS_UNTIL_NEXT_HOUR" -gt 0 ]]; then
312 elif [[ "$SECONDS_UNTIL_NEXT_HOUR" -eq 0 ]]; then
313 returnState
="warning_zero";
314 yell
"WARNING:Reported time until next hour exactly zero.";
315 elif [[ "$SECONDS_UNTIL_NEXT_HOUR" -lt 0 ]]; then
316 returnState
="warning_negative";
317 yell
"WARNING:Reported time until next hour is negative.";
320 try
echo "$SECONDS_UNTIL_NEXT_HOUR"; # Report
322 # Determine function return code
323 if [[ "$returnState" = "true" ]]; then
325 elif [[ "$returnState" = "warning_zero" ]]; then
327 elif [[ "$returnState" = "warning_negative" ]]; then
330 } # Report seconds until next hour
332 # Desc: Timestamp without separators (YYYYmmddTHHMMSS+zzzz)
333 # Usage: dateTimeShort ([str date])
335 # Input: arg1: 'date'-parsable timestamp string (optional)
336 # Output: stdout: timestamp (ISO-8601, no separators)
338 local TIME_CURRENT TIME_CURRENT_SHORT
342 TIME_CURRENT
="$(date --iso-8601=seconds)" ; # Produce `date`-parsable current timestamp with resolution of 1 second.
343 # Decide to parse current or supplied date
344 ## Check if time argument empty
345 if [[ -z "$argTime" ]]; then
346 ## T: Time argument empty, use current time
347 TIME_INPUT
="$TIME_CURRENT";
349 ## F: Time argument exists, validate time
350 if date --date="$argTime" 1>/dev
/null
2>&1; then
351 ### T: Time argument is valid; use it
352 TIME_INPUT
="$argTime";
354 ### F: Time argument not valid; exit
355 yell
"ERROR:Invalid time argument supplied. Exiting."; exit 1;
358 # Construct and deliver separator-les date string
359 TIME_CURRENT_SHORT
="$(date -d "$TIME_INPUT" +%Y%m%dT%H%M%S%z)";
360 echo "$TIME_CURRENT_SHORT";
361 } # Get YYYYmmddTHHMMSS±zzzz
363 # Desc: Date without separators (YYYYmmdd)
364 # Usage: dateShort ([str date])
366 # Input: arg1: 'date'-parsable timestamp string (optional)
367 # Output: stdout: date (ISO-8601, no separators)
369 local TIME_CURRENT DATE_CURRENT_SHORT
373 TIME_CURRENT
="$(date --iso-8601=seconds)" ; # Produce `date`-parsable current timestamp with resolution of 1 second.
374 # Decide to parse current or supplied date
375 ## Check if time argument empty
376 if [[ -z "$argTime" ]]; then
377 ## T: Time argument empty, use current time
378 TIME_INPUT
="$TIME_CURRENT";
380 ## F: Time argument exists, validate time
381 if date --date="$argTime" 1>/dev
/null
2>&1; then
382 ### T: Time argument is valid; use it
383 TIME_INPUT
="$argTime";
385 ### F: Time argument not valid; exit
386 yell
"ERROR:Invalid time argument supplied. Exiting."; exit 1;
389 # Construct and deliver separator-les date string
390 DATE_CURRENT_SHORT
="$(date -d "$TIME_INPUT" +%Y%m%d)"; # Produce separator-less current date with resolution 1 day.
391 echo "$DATE_CURRENT_SHORT";
394 # Desc: Given seconds, output ISO-8601 duration string
395 # Ref/Attrib: ISO-8601:2004(E), §4.4.4.2 Representations of time intervals by duration and context information
396 # Note: "1 month" ("P1M") is assumed to be "30 days" (see ISO-8601:2004(E), §2.2.1.2)
397 # Usage: timeDuration [1:seconds] ([2:precision])
399 # Input: arg1: seconds as base 10 integer >= 0 (ex: 3601)
400 # arg2: precision level (optional; default=2)
401 # Output: stdout: ISO-8601 duration string (ex: "P1H1S", "P2Y10M15DT10H30M20S")
402 # exit code 0: success
403 # exit code 1: error_input
404 # exit code 2: error_unknown
405 # Example: 'timeDuration 111111 3' yields 'P1DT6H51M'
406 # Depends: date 8 (gnucoreutils), yell,
407 local returnState argSeconds argPrecision remainder precision witherPrecision
408 local fullYears fullMonths fullDays fullHours fullMinutes fullSeconds
409 local displayYears displayMonths displayDays displayHours displayMinutes displaySeconds
410 local hasYears hasMonths hasDays hasHours hasMinutes hasSeconds
412 argSeconds
="$1"; # read arg1 (seconds)
413 argPrecision
="$2"; # read arg2 (precision)
414 precision
=2; # set default precision
416 # Check that between one and two arguments is supplied
417 if ! { [[ $# -ge 1 ]] && [[ $# -le 2 ]]; }; then
418 yell
"ERROR:Invalid number of arguments:$# . Exiting.";
419 returnState
="error_input"; fi
421 # Check that argSeconds provided
422 if [[ $# -ge 1 ]]; then
423 ## Check that argSeconds is a positive integer
424 if [[ "$argSeconds" =~ ^
[[:digit
:]]+$
]]; then
427 yell
"ERROR:argSeconds not a digit.";
428 returnState
="error_input";
431 yell
"ERROR:No argument provided. Exiting.";
435 # Consider whether argPrecision was provided
436 if [[ $# -eq 2 ]]; then
437 # Check that argPrecision is a positive integer
438 if [[ "$argPrecision" =~ ^
[[:digit
:]]+$
]] && [[ "$argPrecision" -gt 0 ]]; then
439 precision
="$argPrecision";
441 yell
"ERROR:argPrecision not a positive integer. (is $argPrecision ). Leaving early.";
442 returnState
="error_input";
448 remainder
="$argSeconds" ; # seconds
449 ## Calculate full years Y, update remainder
450 fullYears
=$
(( remainder
/ (365*24*60*60) ));
451 remainder
=$
(( remainder
- (fullYears
*365*24*60*60) ));
452 ## Calculate full months M, update remainder
453 fullMonths
=$
(( remainder
/ (30*24*60*60) ));
454 remainder
=$
(( remainder
- (fullMonths
*30*24*60*60) ));
455 ## Calculate full days D, update remainder
456 fullDays
=$
(( remainder
/ (24*60*60) ));
457 remainder
=$
(( remainder
- (fullDays
*24*60*60) ));
458 ## Calculate full hours H, update remainder
459 fullHours
=$
(( remainder
/ (60*60) ));
460 remainder
=$
(( remainder
- (fullHours
*60*60) ));
461 ## Calculate full minutes M, update remainder
462 fullMinutes
=$
(( remainder
/ (60) ));
463 remainder
=$
(( remainder
- (fullMinutes
*60) ));
464 ## Calculate full seconds S, update remainder
465 fullSeconds
=$
(( remainder
/ (1) ));
466 remainder
=$
(( remainder
- (remainder
*1) ));
467 ## Check which fields filled
468 if [[ $fullYears -gt 0 ]]; then hasYears
="true"; else hasYears
="false"; fi
469 if [[ $fullMonths -gt 0 ]]; then hasMonths
="true"; else hasMonths
="false"; fi
470 if [[ $fullDays -gt 0 ]]; then hasDays
="true"; else hasDays
="false"; fi
471 if [[ $fullHours -gt 0 ]]; then hasHours
="true"; else hasHours
="false"; fi
472 if [[ $fullMinutes -gt 0 ]]; then hasMinutes
="true"; else hasMinutes
="false"; fi
473 if [[ $fullSeconds -gt 0 ]]; then hasSeconds
="true"; else hasSeconds
="false"; fi
475 ## Determine which fields to display (see ISO-8601:2004 §4.4.3.2)
476 witherPrecision
="false"
479 if $hasYears && [[ $precision -gt 0 ]]; then
481 witherPrecision
="true";
483 displayYears
="false";
485 if $witherPrecision; then ((precision--
)); fi;
488 if $hasMonths && [[ $precision -gt 0 ]]; then
489 displayMonths
="true";
490 witherPrecision
="true";
492 displayMonths
="false";
494 if $witherPrecision && [[ $precision -gt 0 ]]; then
495 displayMonths
="true";
497 if $witherPrecision; then ((precision--
)); fi;
500 if $hasDays && [[ $precision -gt 0 ]]; then
502 witherPrecision
="true";
506 if $witherPrecision && [[ $precision -gt 0 ]]; then
509 if $witherPrecision; then ((precision--
)); fi;
512 if $hasHours && [[ $precision -gt 0 ]]; then
514 witherPrecision
="true";
516 displayHours
="false";
518 if $witherPrecision && [[ $precision -gt 0 ]]; then
521 if $witherPrecision; then ((precision--
)); fi;
524 if $hasMinutes && [[ $precision -gt 0 ]]; then
525 displayMinutes
="true";
526 witherPrecision
="true";
528 displayMinutes
="false";
530 if $witherPrecision && [[ $precision -gt 0 ]]; then
531 displayMinutes
="true";
533 if $witherPrecision; then ((precision--
)); fi;
537 if $hasSeconds && [[ $precision -gt 0 ]]; then
538 displaySeconds
="true";
539 witherPrecision
="true";
541 displaySeconds
="false";
543 if $witherPrecision && [[ $precision -gt 0 ]]; then
544 displaySeconds
="true";
546 if $witherPrecision; then ((precision--
)); fi;
548 ## Determine whether or not the "T" separator is needed to separate date and time elements
549 if ( $displayHours ||
$displayMinutes ||
$displaySeconds); then
550 displayDateTime
="true"; else displayDateTime
="false"; fi
552 ## Construct duration output string
554 if $displayYears; then
555 OUTPUT
=$OUTPUT$fullYears"Y"; fi
556 if $displayMonths; then
557 OUTPUT
=$OUTPUT$fullMonths"M"; fi
558 if $displayDays; then
559 OUTPUT
=$OUTPUT$fullDays"D"; fi
560 if $displayDateTime; then
561 OUTPUT
=$OUTPUT"T"; fi
562 if $displayHours; then
563 OUTPUT
=$OUTPUT$fullHours"H"; fi
564 if $displayMinutes; then
565 OUTPUT
=$OUTPUT$fullMinutes"M"; fi
566 if $displaySeconds; then
567 OUTPUT
=$OUTPUT$fullSeconds"S"; fi
569 ## Output duration string to stdout
570 echo "$OUTPUT" && returnState
="true";
572 #===Determine function return code===
573 if [ "$returnState" = "true" ]; then
575 elif [ "$returnState" = "error_input" ]; then
579 yell
"ERROR:Unknown";
583 } # Get duration (ex: PT10M4S )
585 # Desc: Displays missing apps, files, and dirs
586 # Usage: displayMissing
587 # Input: associative arrays: appRollCall, fileRollCall, dirRollCall
588 # Output: stderr messages
589 #==BEGIN Display errors==
590 #===BEGIN Display Missing Apps===
591 missingApps
="Missing apps :"
592 #for key in "${!appRollCall[@]}"; do echo "DEBUG:$key => ${appRollCall[$key]}"; done
593 for key
in "${!appRollCall[@]}"; do
594 value
="${appRollCall[$key]}"
595 if [ "$value" = "false" ]; then
596 #echo "DEBUG:Missing apps: $key => $value";
597 missingApps
="$missingApps""$key "
601 if [ "$appMissing" = "true" ]; then # Only indicate if an app is missing.
602 echo "$missingApps" 1>&2;
604 #===END Display Missing Apps===
606 #===BEGIN Display Missing Files===
607 missingFiles
="Missing files:"
608 #for key in "${!fileRollCall[@]}"; do echo "DEBUG:$key => ${fileRollCall[$key]}"; done
609 for key
in "${!fileRollCall[@]}"; do
610 value
="${fileRollCall[$key]}"
611 if [ "$value" = "false" ]; then
612 #echo "DEBUG:Missing files: $key => $value";
613 missingFiles
="$missingFiles""$key "
617 if [ "$fileMissing" = "true" ]; then # Only indicate if an app is missing.
618 echo "$missingFiles" 1>&2;
620 #===END Display Missing Files===
622 #===BEGIN Display Missing Directories===
623 missingDirs
="Missing dirs:"
624 #for key in "${!dirRollCall[@]}"; do echo "DEBUG:$key => ${dirRollCall[$key]}"; done
625 for key
in "${!dirRollCall[@]}"; do
626 value
="${dirRollCall[$key]}"
627 if [ "$value" = "false" ]; then
628 #echo "DEBUG:Missing dirs: $key => $value";
629 missingDirs
="$missingDirs""$key "
633 if [ "$dirMissing" = "true" ]; then # Only indicate if an dir is missing.
634 echo "$missingDirs" 1>&2;
636 #===END Display Missing Directories===
638 #==END Display errors==
639 } # Display missing apps, files, dirs
641 #Desc: Sets script TTL
642 #Usage: setScriptTTL arg1
643 #Input: arg1: "day" or "hour"
645 #Depends: timeUntilNextHour or timeUntilNextDay
648 if [[ "$ARG1" = "day" ]]; then
649 # Set script lifespan to end at start of next day
650 if ! scriptTTL
="$(timeUntilNextDay)"; then
651 if [[ "$scriptTTL" -eq 0 ]]; then
652 ((scriptTTL
++)); # Add 1 because 0 would cause 'timeout' to never timeout.
654 yell
"ERROR: timeUntilNextDay exit code $?"; exit 1;
657 elif [[ "$ARG1" = "hour" ]]; then
658 # Set script lifespan to end at start of next hour
659 if ! scriptTTL
="$(timeUntilNextHour)"; then
660 if [[ "$scriptTTL" -eq 0 ]]; then
661 ((scriptTTL
++)); # Add 1 because 0 would cause 'timeout' to never timeout.
663 yell
"ERROR: timeUntilNextHour exit code $?"; exit 1;
667 yell
"ERROR:Invalid argument for setScriptTTL function."; exit 1;
669 } # Seconds until next (day|hour).
671 # Desc: Checks that a valid tar archive exists, creates one otherwise
672 # Usage: checkMakeTar [ path ]
674 # Input: arg1: path of tar archive
675 # Output: exit code 0 : tar readable
676 # exit code 1 : tar missing; created
677 # exit code 2 : tar not readable; moved; replaced
678 # Depends: try, tar, date
679 local PATH_TAR returnFlag0 returnFlag1 returnFlag2
682 # Check if file is a valid tar archive
683 if tar --list --file="$PATH_TAR" 1>/dev
/null
2>&1; then
684 ## T1: return success
685 returnFlag0
="tar valid";
687 ## F1: Check if file exists
688 if [[ -f "$PATH_TAR" ]]; then
690 try
mv "$PATH_TAR" "$PATH_TAR""--broken--""$(date +%Y%m%dT%H%M%S)" && \
691 returnFlag1
="tar moved";
696 ## F2: Create tar archive, return 0
697 try
tar --create --file="$PATH_TAR" --files-from=/dev
/null
&& \
698 returnFlag2
="tar created";
701 # Determine function return code
702 if [[ "$returnFlag0" = "tar valid" ]]; then
704 elif [[ "$returnFlag2" = "tar created" ]] && ! [[ "$returnFlag1" = "tar moved" ]]; then
705 return 1; # tar missing so created
706 elif [[ "$returnFlag2" = "tar created" ]] && [[ "$returnFlag1" = "tar moved" ]]; then
707 return 2; # tar not readable so moved; replaced
709 } # checks if arg1 is tar; creates one otherwise
711 # Desc: Writes first argument to temporary file with arguments as options, then appends file to tar
712 # Usage: appendArgTar "$(echo "Data to be written.")" [name of file to be inserted] [tar path] [temp dir] ([cmd1] [cmd2] [cmd3] [cmd4]...)
714 # Input: arg1: data to be written
715 # arg2: file name of file to be inserted into tar
716 # arg3: tar archive path (must exist first)
717 # arg4: temporary working dir
718 # arg5+: command strings (ex: "gpsbabel -i nmea -f - -o kml -F - ")
719 # Output: file written to disk
720 # Example: decrypt multiple large files in parallel
721 # appendArgTar "$(cat /tmp/largefile1.gpg)" "largefile1" $HOME/archive.tar /tmp "gpg --decrypt" &
722 # appendArgTar "$(cat /tmp/largefile2.gpg)" "largefile2" $HOME/archive.tar /tmp "gpg --decrypt" &
723 # appendArgTar "$(cat /tmp/largefile3.gpg)" "largefile3" $HOME/archive.tar /tmp "gpg --decrypt" &
725 # Ref/Attrib: Using 'eval' to construct command strings https://askubuntu.com/a/476533
728 local FN
="${FUNCNAME[0]}";
729 #yell "DEBUG:STATUS:$FN:Finished appendArgTar()."
732 if ! [ -z "$2" ]; then FILENAME
="$2"; else yell
"ERROR:$FN:Not enough arguments."; exit 1; fi
734 # Check tar path is a file
735 if [ -f "$3" ]; then TAR_PATH
="$3"; else yell
"ERROR:$FN:Tar archive arg not a file."; exit 1; fi
738 if ! [ -z "$4" ]; then TMP_DIR
="$4"; else yell
"ERROR:$FN:No temporary working dir set."; exit 1; fi
740 # Set command strings
741 if ! [ -z "$5" ]; then CMD1
="$5"; else CMD1
="tee /dev/null "; fi # command string 1
742 if ! [ -z "$6" ]; then CMD2
="$6"; else CMD2
="tee /dev/null "; fi # command string 2
743 if ! [ -z "$7" ]; then CMD3
="$7"; else CMD3
="tee /dev/null "; fi # command string 3
744 if ! [ -z "$8" ]; then CMD4
="$8"; else CMD4
="tee /dev/null "; fi # command string 4
750 # yell "DEBUG:STATUS:$FN:CMD0:$CMD0"
751 # yell "DEBUG:STATUS:$FN:CMD1:$CMD1"
752 # yell "DEBUG:STATUS:$FN:CMD2:$CMD2"
753 # yell "DEBUG:STATUS:$FN:CMD3:$CMD3"
754 # yell "DEBUG:STATUS:$FN:CMD4:$CMD4"
755 # yell "DEBUG:STATUS:$FN:FILENAME:$FILENAME"
756 # yell "DEBUG:STATUS:$FN:TAR_PATH:$TAR_PATH"
757 # yell "DEBUG:STATUS:$FN:TMP_DIR:$TMP_DIR"
759 # Write to temporary working dir
760 eval "$CMD0"" | ""$CMD1"" | ""$CMD2"" | ""$CMD3"" | ""$CMD4" > "$TMP_DIR"/"$FILENAME";
763 try
tar --append --directory="$TMP_DIR" --file="$TAR_PATH" "$FILENAME";
764 #yell "DEBUG:STATUS:$FN:Finished appendArgTar()."
765 } # Append Bash var to file appended to Tar archive
767 # Desc: Processes first file and then appends to tar
768 # Usage: appendFileTar [file path] [name of file to be inserted] [tar path] [temp dir] ([cmd1] [cmd2] [cmd3] [cmd4]...)
770 # Input: arg1: path of file to be (processed and) written
771 # arg2: name to use for file inserted into tar
772 # arg3: tar archive path (must exist first)
773 # arg4: temporary working dir
774 # arg5+: command strings (ex: "gpsbabel -i nmea -f - -o kml -F - ")
775 # Output: file written to disk
776 # Example: decrypt multiple large files in parallel
777 # appendFileTar /tmp/largefile1.gpg "largefile1" $HOME/archive.tar /tmp "gpg --decrypt" &
778 # appendFileTar /tmp/largefile2.gpg "largefile2" $HOME/archive.tar /tmp "gpg --decrypt" &
779 # appendFileTar /tmp/largefile3.gpg "largefile3" $HOME/archive.tar /tmp "gpg --decrypt" &
783 local FN
="${FUNCNAME[0]}";
784 #yell "DEBUG:STATUS:$FN:Finished appendFileTar()."
787 if ! [ -z "$2" ]; then FILENAME
="$2"; else yell
"ERROR:$FN:Not enough arguments."; exit 1; fi
788 # Check tar path is a file
789 if [ -f "$3" ]; then TAR_PATH
="$3"; else yell
"ERROR:$FN:Tar archive arg not a file."; exit 1; fi
791 if ! [ -z "$4" ]; then TMP_DIR
="$4"; else yell
"ERROR:$FN:No temporary working dir set."; exit 1; fi
792 # Set command strings
793 if ! [ -z "$5" ]; then CMD1
="$5"; else CMD1
="tee /dev/null "; fi # command string 1
794 if ! [ -z "$6" ]; then CMD2
="$6"; else CMD2
="tee /dev/null "; fi # command string 2
795 if ! [ -z "$7" ]; then CMD3
="$7"; else CMD3
="tee /dev/null "; fi # command string 3
796 if ! [ -z "$8" ]; then CMD4
="$8"; else CMD4
="tee /dev/null "; fi # command string 4
798 # Input command string
802 # yell "DEBUG:STATUS:$FN:CMD0:$CMD0"
803 # yell "DEBUG:STATUS:$FN:CMD1:$CMD1"
804 # yell "DEBUG:STATUS:$FN:CMD2:$CMD2"
805 # yell "DEBUG:STATUS:$FN:CMD3:$CMD3"
806 # yell "DEBUG:STATUS:$FN:CMD4:$CMD4"
807 # yell "DEBUG:STATUS:$FN:FILENAME:$FILENAME"
808 # yell "DEBUG:STATUS:$FN:TAR_PATH:$TAR_PATH"
809 # yell "DEBUG:STATUS:$FN:TMP_DIR:$TMP_DIR"
811 # Write to temporary working dir
812 eval "$CMD0 | $CMD1 | $CMD2 | $CMD3 | $CMD4" > "$TMP_DIR"/"$FILENAME";
815 try
tar --append --directory="$TMP_DIR" --file="$TAR_PATH" "$FILENAME";
816 #yell "DEBUG:STATUS:$FN:Finished appendFileTar()."
817 } # Append file to Tar archive
819 # Desc: Checks if string is an age-compatible pubkey
820 # Usage: checkAgePubkey [str pubkey]
822 # Input: arg1: string
823 # Output: return code 0: string is age-compatible pubkey
824 # return code 1: string is NOT an age-compatible pubkey
825 # age stderr (ex: there is stderr if invalid string provided)
826 # Depends: age (v0.1.0-beta2; https://github.com/FiloSottile/age/releases/tag/v1.0.0-beta2 )
830 if echo "test" | age
-a -r "$argPubkey" 1>/dev
/null
; then
837 # Desc: Validates Input
838 # Usage: validateInput [str input] [str input type]
840 # Input: arg1: string to validate
841 # arg2: string specifying input type (ex:"ssh_pubkey")
842 # Output: return code 0: if input string matched specified string type
843 # Depends: bash 5, yell
846 local FN
="${FUNCNAME[0]}";
851 if [[ $# -gt 2 ]]; then yell
"ERROR:$0:$FN:Too many arguments."; exit 1; fi;
854 if [[ -z "$argInput" ]]; then return 1; fi
858 ### Check for alnum/dash base64 (ex: "ssh-rsa AAAAB3NzaC1yc2EAAA")
859 if [[ "$argType" = "ssh_pubkey" ]]; then
860 if [[ "$argInput" =~ ^
[[:alnum
:]-]*[\
]*[[:alnum
:]+/=]*$
]]; then
864 ### Check for age1[:bech32:]
865 if [[ "$argType" = "age_pubkey" ]]; then
866 if [[ "$argInput" =~ ^age1
[qpzry9x8gf2tvdw0s3jn54khce6mua7l
]*$
]]; then
869 # Return error if no condition matched.
871 } # Validates strings
872 magicWriteVersion
() {
873 # Desc: Appends time-stamped VERSION to PATHOUT_TAR
874 # Usage: magicWriteVersion
876 # Input: CONTENT_VERSION, FILEOUT_VERSION, PATHOUT_TAR, DIR_TMP
877 # Input: SCRIPT_VERSION, SCRIPT_URL, AGE_VERSION, AGE_URL, SCRIPT_HOSTNAME
878 # Output: appends tar PATHOUT_TAR
879 # Depends: dateTimeShort, appendArgTar
880 local CONTENT_VERSION pubKeyIndex
882 # Set VERSION file name
883 FILEOUT_VERSION
="$(dateTimeShort)..VERSION";
885 # Gather VERSION data in CONTENT_VERSION
886 CONTENT_VERSION
="SCRIPT_VERSION=$SCRIPT_VERSION";
887 #CONTENT_VERSION="$CONTENT_VERSION""\\n";
888 CONTENT_VERSION
="$CONTENT_VERSION""\\n""SCRIPT_NAME=$SCRIPT_NAME";
889 CONTENT_VERSION
="$CONTENT_VERSION""\\n""SCRIPT_URL=$SCRIPT_URL";
890 CONTENT_VERSION
="$CONTENT_VERSION""\\n""AGE_VERSION=$AGE_VERSION";
891 CONTENT_VERSION
="$CONTENT_VERSION""\\n""AGE_URL=$AGE_URL";
892 CONTENT_VERSION
="$CONTENT_VERSION""\\n""DATE=$(date --iso-8601=seconds)";
893 CONTENT_VERSION
="$CONTENT_VERSION""\\n""HOSTNAME=$SCRIPT_HOSTNAME";
894 ## Add list of recipient pubkeys
895 for pubkey
in "${recPubKeysValid[@]}"; do
897 CONTENT_VERSION
="$CONTENT_VERSION""\\n""PUBKEY_$pubKeyIndex=$pubkey";
899 ## Process newline escapes
900 CONTENT_VERSION
="$(echo -e "$CONTENT_VERSION")"
902 # Write CONTENT_VERSION as file FILEOUT_VERSION and write-append to PATHOUT_TAR
903 appendArgTar
"$CONTENT_VERSION" "$FILEOUT_VERSION" "$PATHOUT_TAR" "$DIR_TMP";
905 } # bkgpslog: write version data to PATHOUT_TAR via appendArgTar()
906 magicGatherWriteBuffer
() {
907 # Desc: bkgpslog-specific meta function for writing data to DIR_TMP then appending each file to PATHOUT_TAR
908 # Inputs: PATHOUT_TAR FILEOUT_{NMEA,GPX,KML} CMD_CONV_{NMEA,GPX,KML} CMD_{COMPRESS,ENCRYPT} DIR_TMP,
909 # Inputs: BUFFER_TTL bufferTTL_STR SCRIPT_HOSTNAME CMD_COMPRESS_SUFFIX CMD_ENCRYPT_SUFFIX
910 # Depends: yell, try, vbm, appendArgTar, tar
911 local FN
="${FUNCNAME[0]}";
912 wait; # Wait to avoid collision with older magicWriteBuffer() instances (see https://www.tldp.org/LDP/abs/html/x9644.html )
913 # Create buffer file with unique name
914 PATHOUT_BUFFER
="$DIR_TMP/buffer$SECONDS";
916 timeout
"$BUFFER_TTL"s gpspipe
-r -o "$PATHOUT_BUFFER" ;
917 timeBufferStart
="$(dateTimeShort "$
(date --date="$BUFFER_TTL seconds ago")")"; # Note start time
918 vbm
"DEBUG:STATUS:$FN:Started magicWriteBuffer().";
919 # Determine file paths (time is start of buffer period)
920 FILEOUT_BASENAME
="$timeBufferStart""--""$bufferTTL_STR""..""$SCRIPT_HOSTNAME""_location" && vbm
"STATUS:Set FILEOUT_BASENAME to:$FILEOUT_BASENAME";
921 ## Files saved to DIR_TMP
922 FILEOUT_NMEA
="$FILEOUT_BASENAME".nmea
"$CMD_COMPRESS_SUFFIX""$CMD_ENCRYPT_SUFFIX" && vbm
"STATUS:Set FILEOUT_NMEA to:$FILEOUT_NMEA";
923 FILEOUT_GPX
="$FILEOUT_BASENAME".gpx
"$CMD_COMPRESS_SUFFIX""$CMD_ENCRYPT_SUFFIX" && vbm
"STATUS:Set FILEOUT_GPX to:$FILEOUT_GPX";
924 FILEOUT_KML
="$FILEOUT_BASENAME".kml
"$CMD_COMPRESS_SUFFIX""$CMD_ENCRYPT_SUFFIX" && vbm
"STATUS:Set FILEOUT_KML to:$FILEOUT_KML";
925 PATHOUT_NMEA
="$DIR_TMP"/"$FILEOUT_NMEA" && vbm
"STATUS:Set PATHOUT_NMEA to:$PATHOUT_NMEA";
926 PATHOUT_GPX
="$DIR_TMP"/"$FILEOUT_GPX" && vbm
"STATUS:Set PATHOUT_GPX to:$PATHOUT_GPX";
927 PATHOUT_KML
="$DIR_TMP"/"$FILEOUT_KML" && vbm
"STATUS:Set PATHOUT_KML to:$PATHOUT_KML";
928 ## Files saved to disk (DIR_OUT)
929 ### one file per day (Ex: "20200731..hostname_location.[.gpx.gz].tar")
930 PATHOUT_TAR
="$DIR_OUT"/"$(dateShort "$
(date --date="$BUFFER_TTL seconds ago")")"..
"$SCRIPT_HOSTNAME""_location""$CMD_COMPRESS_SUFFIX""$CMD_ENCRYPT_SUFFIX".
tar && \
931 vbm
"STATUS:Set PATHOUT_TAR to:$PATHOUT_TAR";
933 vbm
"STATUS:DIR_TMP :$DIR_TMP";
934 vbm
"STATUS:PATHOUT_TAR :$PATHOUT_TAR";
935 vbm
"STATUS:PATHOUT_NMEA:$PATHOUT_NMEA";
936 vbm
"STATUS:PATHOUT_GPX:$PATHOUT_GPX";
937 vbm
"STATUS:PATHOUT_KML:$PATHOUT_KML";
940 # Validate PATHOUT_TAR as tar.
941 checkMakeTar
"$PATHOUT_TAR";
942 ## Add VERSION file if checkMakeTar had to create a tar (exited 1) or replace one (exited 2)
943 if [[ $?
-eq 1 ]] ||
[[ $?
-eq 2 ]]; then magicWriteVersion
; fi
945 # Write bufferBash to PATHOUT_TAR
946 appendFileTar
"$PATHOUT_BUFFER" "$FILEOUT_NMEA" "$PATHOUT_TAR" "$DIR_TMP" "$CMD_CONV_NMEA" "$CMD_COMPRESS" "$CMD_ENCRYPT"; # Write NMEA data
947 appendFileTar
"$PATHOUT_BUFFER" "$FILEOUT_GPX" "$PATHOUT_TAR" "$DIR_TMP" "$CMD_CONV_GPX" "$CMD_COMPRESS" "$CMD_ENCRYPT"; # Write GPX file
948 appendFileTar
"$PATHOUT_BUFFER" "$FILEOUT_KML" "$PATHOUT_TAR" "$DIR_TMP" "$CMD_CONV_KML" "$CMD_COMPRESS" "$CMD_ENCRYPT"; # Write KML file
950 # Remove secured chunks from DIR_TMP
951 rm "$PATHOUT_BUFFER" "$PATHOUT_NMEA" "$PATHOUT_GPX" "$PATHOUT_KML";
952 vbm
"DEBUG:STATUS:$FN:Finished magicWriteBuffer().";
953 } # write buffer to disk
954 magicParseRecipientDir
() {
955 # Desc: Updates recPubKeysValid with pubkeys in dir specified by '-R' option ("recipient directory")
956 # Inputs: vars: OPTION_RECDIR, argRecDir,
957 # arry: recPubKeysValid
958 # Outputs: arry: recPubKeysValid (modified with pubkeys in argRecDir if pubkeys valid)
959 # Depends: processArguments,
960 local recFileLine updateRecipients recipientDir
961 declare -a candRecPubKeysValid
963 if [[ "$OPTION_RECDIR" = "true" ]]; then
964 ### Check that argRecDir is a directory.
965 if [[ -d "$argRecDir" ]]; then
966 recipientDir
="$argRecDir";
967 #### Initialize variable indicating outcome of pubkey review
968 unset updateRecipients
969 #### Add existing recipients
970 candRecPubKeysValid
=(${recPubKeysValid[@]});
971 #### Parse files in recipientDir
972 for file in "$recipientDir"/*; do
973 ##### Read first line of each file
974 recFileLine
="$(cat "$file" | head -n1)";
975 ##### check if first line is a valid pubkey
976 if checkAgePubkey
"$recFileLine" && \
977 ( validateInput
"$recFileLine" "ssh_pubkey" || validateInput
"$recFileLine" "age_pubkey"); then
978 ###### T: add candidate pubkey to candRecPubKeysValid
979 candRecPubKeysValid
+=("$recFileLine");
981 ###### F: throw warning;
982 yell
"ERROR:Invalid recipient file detected. Not modifying recipient list."
983 updateRecipients
="false";
986 #### Write updated recPubKeysValid array to recPubKeysValid if no failure detected
987 if ! updateRecipients
="false"; then
988 recPubKeysValid
=(${candRecPubKeysValid[@]});
991 yell
"ERROR:$0:Recipient directory $argRecDir does not exist. Exiting."; exit 1;
994 } # Update recPubKeysValid with argRecDir
995 magicParseRecipientArgs
() {
996 # Desc: Parses recipient arguments specified by '-r' option
997 # Input: vars: OPTION_ENCRYPT from processArguments()
998 # arry: argRecPubKeys from processArguments()
999 # Output: vars: CMD_ENCRYPT, CMD_ENCRYPT_SUFFIX
1000 # arry: recPubKeysValid
1001 # Depends: checkapp(), checkAgePubkey(), validateInput(), processArguments()
1004 if [[ "$OPTION_ENCRYPT" = "true" ]]; then # Check if encryption option active.
1005 if checkapp age
; then # Check that age is available.
1006 for pubkey
in "${argRecPubKeys[@]}"; do # Validate recipient pubkey strings by forming test message
1007 vbm
"DEBUG:Testing pubkey string:$pubkey";
1008 if checkAgePubkey
"$pubkey" && \
1009 ( validateInput
"$pubkey" "ssh_pubkey" || validateInput
"$pubkey" "age_pubkey"); then
1010 #### Form age recipient string
1011 recipients
="$recipients""-r '$pubkey' ";
1012 vbm
"STATUS:Added pubkey for forming age recipient string:""$pubkey";
1013 vbm
"DEBUG:recipients:""$recipients";
1014 #### Add validated pubkey to recPubKeysValid array
1015 recPubKeysValid
+=("$pubkey") && vbm
"DEBUG:recPubkeysValid:pubkey added:$pubkey";
1017 yell
"ERROR:Exit code ""$?"". Invalid recipient pubkey string. Exiting."; exit 1;
1020 vbm
"DEBUG:Finished processing argRecPubKeys array";
1022 ## Form age command string
1023 CMD_ENCRYPT
="age ""$recipients " && vbm
"CMD_ENCRYPT:$CMD_ENCRYPT";
1024 CMD_ENCRYPT_SUFFIX
=".age" && vbm
"CMD_ENCRYPT_SUFFIX:$CMD_ENCRYPT_SUFFIX";
1026 yell
"ERROR:Encryption enabled but \"age\" not found. Exiting."; exit 1;
1029 CMD_ENCRYPT
="tee /dev/null " && vbm
"CMD_ENCRYPT:$CMD_ENCRYPT";
1030 CMD_ENCRYPT_SUFFIX
="" && vbm
"CMD_ENCRYPT_SUFFIX:$CMD_ENCRYPT_SUFFIX";
1031 vbm
"DEBUG:Encryption not enabled."
1033 } # Populate recPubKeysValid with argRecPubKeys; form encryption cmd string and filename suffix
1034 magicParseCompressionArg
() {
1035 # Desc: Parses compression arguments specified by '-c' option
1036 # Input: vars: OPTION_COMPRESS
1037 # Output: CMD_COMPRESS, CMD_COMPRESS_SUFFIX
1038 # Depends: checkapp(), vbm(), gzip,
1039 if [[ "$OPTION_COMPRESS" = "true" ]]; then # Check if compression option active
1040 if checkapp
gzip; then # Check if gzip available
1041 CMD_COMPRESS
="gzip " && vbm
"CMD_COMPRESS:$CMD_COMPRESS";
1042 CMD_COMPRESS_SUFFIX
=".gz" && vbm
"CMD_COMPRESS_SUFFIX:$CMD_COMPRESS_SUFFIX";
1044 yell
"ERROR:Compression enabled but \"gzip\" not found. Exiting."; exit 1;
1047 CMD_COMPRESS
="tee /dev/null " && vbm
"CMD_COMPRESS:$CMD_COMPRESS";
1048 CMD_COMPRESS_SUFFIX
="" && vbm
"CMD_COMPRESS_SUFFIX:$CMD_COMPRESS_SUFFIX";
1049 vbm
"DEBUG:Compression not enabled.";
1051 } # Form compression cmd string and filename suffix
1052 magicInitWorkingDir
() {
1053 # Desc: Determine temporary working directory from defaults or user input
1054 # Input: vars: OPTION_TEMPDIR, argTmpDirPriority, DIR_TMP_DEFAULT
1055 # Input: vars: SCRIPT_TIME_START
1056 # Output: vars: DIR_TMP
1057 # Depends: processArguments(), vbm(), yell()
1058 # Parse '-t' option (user-specified temporary working dir)
1059 ## Set DIR_TMP_PARENT to user-specified value if specified
1060 local DIR_TMP_PARENT
1062 if [[ "$OPTION_TMPDIR" = "true" ]]; then
1063 if [[ -d "$argTempDirPriority" ]]; then
1064 DIR_TMP_PARENT
="$argTempDirPriority";
1066 yell
"WARNING:Specified temporary working directory not valid:$OPTION_TMPDIR";
1067 exit 1; # Exit since user requires a specific temp dir and it is not available.
1070 ## Set DIR_TMP_PARENT to default or fallback otherwise
1071 if [[ -d "$DIR_TMP_DEFAULT" ]]; then
1072 DIR_TMP_PARENT
="$DIR_TMP_DEFAULT";
1073 elif [[ -d /tmp
]]; then
1074 yell
"WARNING:$DIR_TMP_DEFAULT not available. Falling back to /tmp .";
1075 DIR_TMP_PARENT
="/tmp";
1077 yell
"ERROR:No valid working directory available. Exiting.";
1081 ## Set DIR_TMP using DIR_TMP_PARENT and nonce (SCRIPT_TIME_START)
1082 DIR_TMP
="$DIR_TMP_PARENT"/"$SCRIPT_TIME_START""..bkgpslog" && vbm
"DEBUG:Set DIR_TMP to:$DIR_TMP"; # Note: removed at end of main().
1083 } # Sets working dir
1087 processArguments
"$@";
1088 ## Act upon arguments
1089 ### Determine working directory
1090 magicInitWorkingDir
;
1091 ### Set output encryption and compression option strings
1092 #### React to "-r" ("encryption recipients") option
1093 magicParseRecipientArgs
;
1094 #### React to "-c" ("compression") option
1095 magicParseCompressionArg
;
1096 #### React to "-R" ("recipient directory") option
1097 magicParseRecipientDir
;
1099 # Check that critical apps and dirs are available, display missing ones.
1100 if ! checkapp gpspipe
tar && ! checkdir
"$DIR_OUT" "DIR_TMP"; then
1101 yell
"ERROR:Critical components missing.";
1102 displayMissing
; yell
"Exiting."; exit 1; fi
1104 # Set script lifespan
1105 setScriptTTL
"$SCRIPT_TTL"; # seconds until next new SCRIPT_TTL (ex: "day" or "hour")
1107 # File name substring: encoded bufferTTL
1108 bufferTTL_STR
="$(timeDuration $BUFFER_TTL)";
1110 # Init temp working dir
1111 try mkdir
"$DIR_TMP" && vbm
"DEBUG:Working dir creatd at:$DIR_TMP";
1113 # Initialize 'tar' archive
1114 ## Define output tar path (note: each day gets *one* tar file (Ex: "20200731..hostname_location.[.gpx.gz].tar"))
1115 PATHOUT_TAR
="$DIR_OUT"/"$(dateShort)"..
"$SCRIPT_HOSTNAME""_location""$CMD_COMPRESS_SUFFIX""$CMD_ENCRYPT_SUFFIX".
tar && \
1116 vbm
"STATUS:Set PATHOUT_TAR to:$PATHOUT_TAR";
1117 ## Check that PATHOUT_TAR is a tar. Rename old and create empty one otherwise.
1118 checkMakeTar
"$PATHOUT_TAR" && vbm
"DEBUG:Confirmed or Created to be a tar:$PATHOUT_TAR";
1119 ## Append VERSION file to PATHOUT_TAR
1122 # Define GPS conversion commands
1123 CMD_CONV_NMEA
="tee /dev/null " && vbm
"STATUS:Set CMD_CONV_NMEA to:$CMD_CONV_NMEA"; # tee as passthrough
1124 CMD_CONV_GPX
="gpsbabel -i nmea -f - -o gpx -F - " && vbm
"STATUS:Set CMD_CONV_GPX to:$CMD_CONV_GPX"; # convert NMEA to GPX
1125 CMD_CONV_KML
="gpsbabel -i nmea -f - -o kml -F - " && vbm
"STATUS:Set CMD_CONV_KML to:$CMD_CONV_KML"; # convert NMEA to KML
1127 # MAIN LOOP:Record gps data until script lifespan ends
1128 while [[ "$SECONDS" -lt "$scriptTTL" ]]; do
1129 magicGatherWriteBuffer
&
1130 sleep "$BUFFER_TTL";
1135 try
rm -r "$DIR_TMP";
1137 vbm
"STATUS:Main function finished.";
1139 #===END Declare local script functions===
1140 #==END Define script parameters==
1143 #==BEGIN Perform work and exit==
1144 main
"$@" # Run main function.
1146 #==END Perform work and exit==