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.";; # Enable encryption
213 -r |
--recipient) OPTION_RECIPIENTS
="true"; argRecPubKeys
+=("$2"); vbm
"STATUS:pubkey added:""$2"; shift;; # Add recipients
214 -c |
--compress) OPTION_COMPRESS
="true"; vbm
"DEBUG:Compressed output mode enabled.";; # Enable compression
215 -z |
--time-zone) try setTimeZoneEV
"$2"; shift;; # Set timestamp timezone
216 -t |
--temp-dir) OPTION_TMPDIR
="true" && argTempDirPriority
="$2"; shift;; # Set time zone
217 -R |
--recipient-dir) OPTION_RECIPIENTS
="true"; OPTION_RECDIR
="true" && argRecDir
="$2"; shift;; # Add recipient watch dir
218 *) echoerr
"ERROR: Unrecognized argument: $1"; echoerr
"STATUS:All arguments:$*"; exit 1;; # Handle unrecognized options.
222 } # Argument Processing
224 # Desc: Set time zone environment variable TZ
225 # Usage: setTimeZoneEV arg1
226 # Input: arg1: 'date'-compatible timezone string (ex: "America/New_York")
227 # TZDIR env var (optional; default: "/usr/share/zoneinfo")
229 # exit code 0 on success
230 # exit code 1 on incorrect number of arguments
231 # exit code 2 if unable to validate arg1
232 # Depends: yell, printenv, bash 5
233 # Tested on: Debian 10
235 local tzDir returnState
236 if ! [[ $# -eq 1 ]]; then
237 yell
"ERROR:Invalid argument count.";
241 # Read TZDIR env var if available
242 if printenv TZDIR
1>/dev
/null
2>&1; then
243 tzDir
="$(printenv TZDIR)";
245 tzDir
="/usr/share/zoneinfo";
249 if ! [[ -f "$tzDir"/"$ARG1" ]]; then
250 yell
"ERROR:Invalid time zone argument.";
253 # Export ARG1 as TZ environment variable
254 TZ
="$ARG1" && export TZ
&& returnState
="true";
257 # Determine function return code
258 if [ "$returnState" = "true" ]; then
261 } # Exports TZ environment variable
263 # Desc: Report seconds until next day.
265 # Output: stdout: integer seconds until next day
266 # Output: exit code 0 if stdout > 0; 1 if stdout = 0; 2 if stdout < 0
267 # Usage: timeUntilNextDay
268 # Usage: if ! myTTL="$(timeUntilNextDay)"; then yell "ERROR in if statement"; exit 1; fi
269 # Depends: date 8, echo 8, yell, try
271 local returnState TIME_CURRENT TIME_NEXT_DAY SECONDS_UNTIL_NEXT_DAY
273 TIME_CURRENT
="$(date --iso-8601=seconds)" ; # Produce `date`-parsable current timestamp with resolution of 1 second.
274 TIME_NEXT_DAY
="$(date -d "$TIME_CURRENT next day
" --iso-8601=date)"; # Produce timestamp of beginning of tomorrow with resolution of 1 second.
275 SECONDS_UNTIL_NEXT_DAY
="$(( $(date +%s -d "$TIME_NEXT_DAY") - $(date +%s -d "$TIME_CURRENT") ))" ; # Calculate seconds until closest future midnight (res. 1 second).
276 if [[ "$SECONDS_UNTIL_NEXT_DAY" -gt 0 ]]; then
278 elif [[ "$SECONDS_UNTIL_NEXT_DAY" -eq 0 ]]; then
279 returnState
="warning_zero";
280 yell
"WARNING:Reported time until next day exactly zero.";
281 elif [[ "$SECONDS_UNTIL_NEXT_DAY" -lt 0 ]]; then
282 returnState
="warning_negative";
283 yell
"WARNING:Reported time until next day is negative.";
286 try
echo "$SECONDS_UNTIL_NEXT_DAY"; # Report
288 # Determine function return code
289 if [[ "$returnState" = "true" ]]; then
291 elif [[ "$returnState" = "warning_zero" ]]; then
293 elif [[ "$returnState" = "warning_negative" ]]; then
296 } # Report seconds until next day
298 # Desc: Report seconds until next hour
300 # Output: stdout: integer seconds until next hour
301 # Output: exit code 0 if stdout > 0; 1 if stdout = 0; 2 if stdout < 0
302 # Usage: timeUntilNextHour
303 # Usage: if ! myTTL="$(timeUntilNextHour)"; then yell "ERROR in if statement"; exit 1; fi
305 local returnState TIME_CURRENT TIME_NEXT_HOUR SECONDS_UNTIL_NEXT_HOUR
306 TIME_CURRENT
="$(date --iso-8601=seconds)"; # Produce `date`-parsable current timestamp with resolution of 1 second.
307 TIME_NEXT_HOUR
="$(date -d "$TIME_CURRENT next hour
" --iso-8601=hours)"; # Produce `date`-parsable current time stamp with resolution of 1 second.
308 SECONDS_UNTIL_NEXT_HOUR
="$(( $(date +%s -d "$TIME_NEXT_HOUR") - $(date +%s -d "$TIME_CURRENT") ))"; # Calculate seconds until next hour (res. 1 second).
309 if [[ "$SECONDS_UNTIL_NEXT_HOUR" -gt 0 ]]; then
311 elif [[ "$SECONDS_UNTIL_NEXT_HOUR" -eq 0 ]]; then
312 returnState
="warning_zero";
313 yell
"WARNING:Reported time until next hour exactly zero.";
314 elif [[ "$SECONDS_UNTIL_NEXT_HOUR" -lt 0 ]]; then
315 returnState
="warning_negative";
316 yell
"WARNING:Reported time until next hour is negative.";
319 try
echo "$SECONDS_UNTIL_NEXT_HOUR"; # Report
321 # Determine function return code
322 if [[ "$returnState" = "true" ]]; then
324 elif [[ "$returnState" = "warning_zero" ]]; then
326 elif [[ "$returnState" = "warning_negative" ]]; then
329 } # Report seconds until next hour
331 # Desc: Timestamp without separators (YYYYmmddTHHMMSS+zzzz)
332 # Usage: dateTimeShort ([str date])
334 # Input: arg1: 'date'-parsable timestamp string (optional)
335 # Output: stdout: timestamp (ISO-8601, no separators)
337 local TIME_CURRENT TIME_CURRENT_SHORT
341 TIME_CURRENT
="$(date --iso-8601=seconds)" ; # Produce `date`-parsable current timestamp with resolution of 1 second.
342 # Decide to parse current or supplied date
343 ## Check if time argument empty
344 if [[ -z "$argTime" ]]; then
345 ## T: Time argument empty, use current time
346 TIME_INPUT
="$TIME_CURRENT";
348 ## F: Time argument exists, validate time
349 if date --date="$argTime" 1>/dev
/null
2>&1; then
350 ### T: Time argument is valid; use it
351 TIME_INPUT
="$argTime";
353 ### F: Time argument not valid; exit
354 yell
"ERROR:Invalid time argument supplied. Exiting."; exit 1;
357 # Construct and deliver separator-les date string
358 TIME_CURRENT_SHORT
="$(date -d "$TIME_INPUT" +%Y%m%dT%H%M%S%z)";
359 echo "$TIME_CURRENT_SHORT";
360 } # Get YYYYmmddTHHMMSS±zzzz
362 # Desc: Date without separators (YYYYmmdd)
363 # Usage: dateShort ([str date])
365 # Input: arg1: 'date'-parsable timestamp string (optional)
366 # Output: stdout: date (ISO-8601, no separators)
368 local TIME_CURRENT DATE_CURRENT_SHORT
372 TIME_CURRENT
="$(date --iso-8601=seconds)" ; # Produce `date`-parsable current timestamp with resolution of 1 second.
373 # Decide to parse current or supplied date
374 ## Check if time argument empty
375 if [[ -z "$argTime" ]]; then
376 ## T: Time argument empty, use current time
377 TIME_INPUT
="$TIME_CURRENT";
379 ## F: Time argument exists, validate time
380 if date --date="$argTime" 1>/dev
/null
2>&1; then
381 ### T: Time argument is valid; use it
382 TIME_INPUT
="$argTime";
384 ### F: Time argument not valid; exit
385 yell
"ERROR:Invalid time argument supplied. Exiting."; exit 1;
388 # Construct and deliver separator-les date string
389 DATE_CURRENT_SHORT
="$(date -d "$TIME_INPUT" +%Y%m%d)"; # Produce separator-less current date with resolution 1 day.
390 echo "$DATE_CURRENT_SHORT";
393 # Desc: Given seconds, output ISO-8601 duration string
394 # Ref/Attrib: ISO-8601:2004(E), §4.4.4.2 Representations of time intervals by duration and context information
395 # Note: "1 month" ("P1M") is assumed to be "30 days" (see ISO-8601:2004(E), §2.2.1.2)
396 # Usage: timeDuration [1:seconds] ([2:precision])
398 # Input: arg1: seconds as base 10 integer >= 0 (ex: 3601)
399 # arg2: precision level (optional; default=2)
400 # Output: stdout: ISO-8601 duration string (ex: "P1H1S", "P2Y10M15DT10H30M20S")
401 # exit code 0: success
402 # exit code 1: error_input
403 # exit code 2: error_unknown
404 # Example: 'timeDuration 111111 3' yields 'P1DT6H51M'
405 # Depends: date 8 (gnucoreutils), yell,
406 local returnState argSeconds argPrecision remainder precision witherPrecision
407 local fullYears fullMonths fullDays fullHours fullMinutes fullSeconds
408 local displayYears displayMonths displayDays displayHours displayMinutes displaySeconds
409 local hasYears hasMonths hasDays hasHours hasMinutes hasSeconds
411 argSeconds
="$1"; # read arg1 (seconds)
412 argPrecision
="$2"; # read arg2 (precision)
413 precision
=2; # set default precision
415 # Check that between one and two arguments is supplied
416 if ! { [[ $# -ge 1 ]] && [[ $# -le 2 ]]; }; then
417 yell
"ERROR:Invalid number of arguments:$# . Exiting.";
418 returnState
="error_input"; fi
420 # Check that argSeconds provided
421 if [[ $# -ge 1 ]]; then
422 ## Check that argSeconds is a positive integer
423 if [[ "$argSeconds" =~ ^
[[:digit
:]]+$
]]; then
426 yell
"ERROR:argSeconds not a digit.";
427 returnState
="error_input";
430 yell
"ERROR:No argument provided. Exiting.";
434 # Consider whether argPrecision was provided
435 if [[ $# -eq 2 ]]; then
436 # Check that argPrecision is a positive integer
437 if [[ "$argPrecision" =~ ^
[[:digit
:]]+$
]] && [[ "$argPrecision" -gt 0 ]]; then
438 precision
="$argPrecision";
440 yell
"ERROR:argPrecision not a positive integer. (is $argPrecision ). Leaving early.";
441 returnState
="error_input";
447 remainder
="$argSeconds" ; # seconds
448 ## Calculate full years Y, update remainder
449 fullYears
=$
(( remainder
/ (365*24*60*60) ));
450 remainder
=$
(( remainder
- (fullYears
*365*24*60*60) ));
451 ## Calculate full months M, update remainder
452 fullMonths
=$
(( remainder
/ (30*24*60*60) ));
453 remainder
=$
(( remainder
- (fullMonths
*30*24*60*60) ));
454 ## Calculate full days D, update remainder
455 fullDays
=$
(( remainder
/ (24*60*60) ));
456 remainder
=$
(( remainder
- (fullDays
*24*60*60) ));
457 ## Calculate full hours H, update remainder
458 fullHours
=$
(( remainder
/ (60*60) ));
459 remainder
=$
(( remainder
- (fullHours
*60*60) ));
460 ## Calculate full minutes M, update remainder
461 fullMinutes
=$
(( remainder
/ (60) ));
462 remainder
=$
(( remainder
- (fullMinutes
*60) ));
463 ## Calculate full seconds S, update remainder
464 fullSeconds
=$
(( remainder
/ (1) ));
465 remainder
=$
(( remainder
- (remainder
*1) ));
466 ## Check which fields filled
467 if [[ $fullYears -gt 0 ]]; then hasYears
="true"; else hasYears
="false"; fi
468 if [[ $fullMonths -gt 0 ]]; then hasMonths
="true"; else hasMonths
="false"; fi
469 if [[ $fullDays -gt 0 ]]; then hasDays
="true"; else hasDays
="false"; fi
470 if [[ $fullHours -gt 0 ]]; then hasHours
="true"; else hasHours
="false"; fi
471 if [[ $fullMinutes -gt 0 ]]; then hasMinutes
="true"; else hasMinutes
="false"; fi
472 if [[ $fullSeconds -gt 0 ]]; then hasSeconds
="true"; else hasSeconds
="false"; fi
474 ## Determine which fields to display (see ISO-8601:2004 §4.4.3.2)
475 witherPrecision
="false"
478 if $hasYears && [[ $precision -gt 0 ]]; then
480 witherPrecision
="true";
482 displayYears
="false";
484 if $witherPrecision; then ((precision--
)); fi;
487 if $hasMonths && [[ $precision -gt 0 ]]; then
488 displayMonths
="true";
489 witherPrecision
="true";
491 displayMonths
="false";
493 if $witherPrecision && [[ $precision -gt 0 ]]; then
494 displayMonths
="true";
496 if $witherPrecision; then ((precision--
)); fi;
499 if $hasDays && [[ $precision -gt 0 ]]; then
501 witherPrecision
="true";
505 if $witherPrecision && [[ $precision -gt 0 ]]; then
508 if $witherPrecision; then ((precision--
)); fi;
511 if $hasHours && [[ $precision -gt 0 ]]; then
513 witherPrecision
="true";
515 displayHours
="false";
517 if $witherPrecision && [[ $precision -gt 0 ]]; then
520 if $witherPrecision; then ((precision--
)); fi;
523 if $hasMinutes && [[ $precision -gt 0 ]]; then
524 displayMinutes
="true";
525 witherPrecision
="true";
527 displayMinutes
="false";
529 if $witherPrecision && [[ $precision -gt 0 ]]; then
530 displayMinutes
="true";
532 if $witherPrecision; then ((precision--
)); fi;
536 if $hasSeconds && [[ $precision -gt 0 ]]; then
537 displaySeconds
="true";
538 witherPrecision
="true";
540 displaySeconds
="false";
542 if $witherPrecision && [[ $precision -gt 0 ]]; then
543 displaySeconds
="true";
545 if $witherPrecision; then ((precision--
)); fi;
547 ## Determine whether or not the "T" separator is needed to separate date and time elements
548 if ( $displayHours ||
$displayMinutes ||
$displaySeconds); then
549 displayDateTime
="true"; else displayDateTime
="false"; fi
551 ## Construct duration output string
553 if $displayYears; then
554 OUTPUT
=$OUTPUT$fullYears"Y"; fi
555 if $displayMonths; then
556 OUTPUT
=$OUTPUT$fullMonths"M"; fi
557 if $displayDays; then
558 OUTPUT
=$OUTPUT$fullDays"D"; fi
559 if $displayDateTime; then
560 OUTPUT
=$OUTPUT"T"; fi
561 if $displayHours; then
562 OUTPUT
=$OUTPUT$fullHours"H"; fi
563 if $displayMinutes; then
564 OUTPUT
=$OUTPUT$fullMinutes"M"; fi
565 if $displaySeconds; then
566 OUTPUT
=$OUTPUT$fullSeconds"S"; fi
568 ## Output duration string to stdout
569 echo "$OUTPUT" && returnState
="true";
571 #===Determine function return code===
572 if [ "$returnState" = "true" ]; then
574 elif [ "$returnState" = "error_input" ]; then
578 yell
"ERROR:Unknown";
582 } # Get duration (ex: PT10M4S )
584 # Desc: Displays missing apps, files, and dirs
585 # Usage: displayMissing
586 # Input: associative arrays: appRollCall, fileRollCall, dirRollCall
587 # Output: stderr messages
588 #==BEGIN Display errors==
589 #===BEGIN Display Missing Apps===
590 missingApps
="Missing apps :"
591 #for key in "${!appRollCall[@]}"; do echo "DEBUG:$key => ${appRollCall[$key]}"; done
592 for key
in "${!appRollCall[@]}"; do
593 value
="${appRollCall[$key]}"
594 if [ "$value" = "false" ]; then
595 #echo "DEBUG:Missing apps: $key => $value";
596 missingApps
="$missingApps""$key "
600 if [ "$appMissing" = "true" ]; then # Only indicate if an app is missing.
601 echo "$missingApps" 1>&2;
603 #===END Display Missing Apps===
605 #===BEGIN Display Missing Files===
606 missingFiles
="Missing files:"
607 #for key in "${!fileRollCall[@]}"; do echo "DEBUG:$key => ${fileRollCall[$key]}"; done
608 for key
in "${!fileRollCall[@]}"; do
609 value
="${fileRollCall[$key]}"
610 if [ "$value" = "false" ]; then
611 #echo "DEBUG:Missing files: $key => $value";
612 missingFiles
="$missingFiles""$key "
616 if [ "$fileMissing" = "true" ]; then # Only indicate if an app is missing.
617 echo "$missingFiles" 1>&2;
619 #===END Display Missing Files===
621 #===BEGIN Display Missing Directories===
622 missingDirs
="Missing dirs:"
623 #for key in "${!dirRollCall[@]}"; do echo "DEBUG:$key => ${dirRollCall[$key]}"; done
624 for key
in "${!dirRollCall[@]}"; do
625 value
="${dirRollCall[$key]}"
626 if [ "$value" = "false" ]; then
627 #echo "DEBUG:Missing dirs: $key => $value";
628 missingDirs
="$missingDirs""$key "
632 if [ "$dirMissing" = "true" ]; then # Only indicate if an dir is missing.
633 echo "$missingDirs" 1>&2;
635 #===END Display Missing Directories===
637 #==END Display errors==
638 } # Display missing apps, files, dirs
640 #Desc: Sets script TTL
641 #Usage: setScriptTTL arg1
642 #Input: arg1: "day" or "hour"
644 #Depends: timeUntilNextHour or timeUntilNextDay
647 if [[ "$ARG1" = "day" ]]; then
648 # Set script lifespan to end at start of next day
649 if ! scriptTTL
="$(timeUntilNextDay)"; then
650 if [[ "$scriptTTL" -eq 0 ]]; then
651 ((scriptTTL
++)); # Add 1 because 0 would cause 'timeout' to never timeout.
653 yell
"ERROR: timeUntilNextDay exit code $?"; exit 1;
656 elif [[ "$ARG1" = "hour" ]]; then
657 # Set script lifespan to end at start of next hour
658 if ! scriptTTL
="$(timeUntilNextHour)"; then
659 if [[ "$scriptTTL" -eq 0 ]]; then
660 ((scriptTTL
++)); # Add 1 because 0 would cause 'timeout' to never timeout.
662 yell
"ERROR: timeUntilNextHour exit code $?"; exit 1;
666 yell
"ERROR:Invalid argument for setScriptTTL function."; exit 1;
668 } # Seconds until next (day|hour).
670 # Desc: Checks that a valid tar archive exists, creates one otherwise
671 # Usage: checkMakeTar [ path ]
673 # Input: arg1: path of tar archive
674 # Output: exit code 0 : tar readable
675 # exit code 1 : tar missing; created
676 # exit code 2 : tar not readable; moved; replaced
677 # Depends: try, tar, date
678 local PATH_TAR returnFlag0 returnFlag1 returnFlag2
681 # Check if file is a valid tar archive
682 if tar --list --file="$PATH_TAR" 1>/dev
/null
2>&1; then
683 ## T1: return success
684 returnFlag0
="tar valid";
686 ## F1: Check if file exists
687 if [[ -f "$PATH_TAR" ]]; then
689 try
mv "$PATH_TAR" "$PATH_TAR""--broken--""$(date +%Y%m%dT%H%M%S)" && \
690 returnFlag1
="tar moved";
695 ## F2: Create tar archive, return 0
696 try
tar --create --file="$PATH_TAR" --files-from=/dev
/null
&& \
697 returnFlag2
="tar created";
700 # Determine function return code
701 if [[ "$returnFlag0" = "tar valid" ]]; then
703 elif [[ "$returnFlag2" = "tar created" ]] && ! [[ "$returnFlag1" = "tar moved" ]]; then
704 return 1; # tar missing so created
705 elif [[ "$returnFlag2" = "tar created" ]] && [[ "$returnFlag1" = "tar moved" ]]; then
706 return 2; # tar not readable so moved; replaced
708 } # checks if arg1 is tar; creates one otherwise
710 # Desc: Writes first argument to temporary file with arguments as options, then appends file to tar
711 # Usage: appendArgTar "$(echo "Data to be written.")" [name of file to be inserted] [tar path] [temp dir] ([cmd1] [cmd2] [cmd3] [cmd4]...)
713 # Input: arg1: data to be written
714 # arg2: file name of file to be inserted into tar
715 # arg3: tar archive path (must exist first)
716 # arg4: temporary working dir
717 # arg5+: command strings (ex: "gpsbabel -i nmea -f - -o kml -F - ")
718 # Output: file written to disk
719 # Example: decrypt multiple large files in parallel
720 # appendArgTar "$(cat /tmp/largefile1.gpg)" "largefile1" $HOME/archive.tar /tmp "gpg --decrypt" &
721 # appendArgTar "$(cat /tmp/largefile2.gpg)" "largefile2" $HOME/archive.tar /tmp "gpg --decrypt" &
722 # appendArgTar "$(cat /tmp/largefile3.gpg)" "largefile3" $HOME/archive.tar /tmp "gpg --decrypt" &
724 # Ref/Attrib: Using 'eval' to construct command strings https://askubuntu.com/a/476533
727 local FN
="${FUNCNAME[0]}";
728 #yell "DEBUG:STATUS:$FN:Finished appendArgTar()."
731 if ! [ -z "$2" ]; then FILENAME
="$2"; else yell
"ERROR:$FN:Not enough arguments."; exit 1; fi
733 # Check tar path is a file
734 if [ -f "$3" ]; then TAR_PATH
="$3"; else yell
"ERROR:$FN:Tar archive arg not a file."; exit 1; fi
737 if ! [ -z "$4" ]; then TMP_DIR
="$4"; else yell
"ERROR:$FN:No temporary working dir set."; exit 1; fi
739 # Set command strings
740 if ! [ -z "$5" ]; then CMD1
="$5"; else CMD1
="tee /dev/null "; fi # command string 1
741 if ! [ -z "$6" ]; then CMD2
="$6"; else CMD2
="tee /dev/null "; fi # command string 2
742 if ! [ -z "$7" ]; then CMD3
="$7"; else CMD3
="tee /dev/null "; fi # command string 3
743 if ! [ -z "$8" ]; then CMD4
="$8"; else CMD4
="tee /dev/null "; fi # command string 4
749 # yell "DEBUG:STATUS:$FN:CMD0:$CMD0"
750 # yell "DEBUG:STATUS:$FN:CMD1:$CMD1"
751 # yell "DEBUG:STATUS:$FN:CMD2:$CMD2"
752 # yell "DEBUG:STATUS:$FN:CMD3:$CMD3"
753 # yell "DEBUG:STATUS:$FN:CMD4:$CMD4"
754 # yell "DEBUG:STATUS:$FN:FILENAME:$FILENAME"
755 # yell "DEBUG:STATUS:$FN:TAR_PATH:$TAR_PATH"
756 # yell "DEBUG:STATUS:$FN:TMP_DIR:$TMP_DIR"
758 # Write to temporary working dir
759 eval "$CMD0"" | ""$CMD1"" | ""$CMD2"" | ""$CMD3"" | ""$CMD4" > "$TMP_DIR"/"$FILENAME";
762 try
tar --append --directory="$TMP_DIR" --file="$TAR_PATH" "$FILENAME";
763 #yell "DEBUG:STATUS:$FN:Finished appendArgTar()."
764 } # Append Bash var to file appended to Tar archive
766 # Desc: Processes first file and then appends to tar
767 # Usage: appendFileTar [file path] [name of file to be inserted] [tar path] [temp dir] ([cmd1] [cmd2] [cmd3] [cmd4]...)
769 # Input: arg1: path of file to be (processed and) written
770 # arg2: name to use for file inserted into tar
771 # arg3: tar archive path (must exist first)
772 # arg4: temporary working dir
773 # arg5+: command strings (ex: "gpsbabel -i nmea -f - -o kml -F - ")
774 # Output: file written to disk
775 # Example: decrypt multiple large files in parallel
776 # appendFileTar /tmp/largefile1.gpg "largefile1" $HOME/archive.tar /tmp "gpg --decrypt" &
777 # appendFileTar /tmp/largefile2.gpg "largefile2" $HOME/archive.tar /tmp "gpg --decrypt" &
778 # appendFileTar /tmp/largefile3.gpg "largefile3" $HOME/archive.tar /tmp "gpg --decrypt" &
782 local FN
="${FUNCNAME[0]}";
783 #yell "DEBUG:STATUS:$FN:Finished appendFileTar()."
786 if ! [ -z "$2" ]; then FILENAME
="$2"; else yell
"ERROR:$FN:Not enough arguments."; exit 1; fi
787 # Check tar path is a file
788 if [ -f "$3" ]; then TAR_PATH
="$3"; else yell
"ERROR:$FN:Tar archive arg not a file."; exit 1; fi
790 if ! [ -z "$4" ]; then TMP_DIR
="$4"; else yell
"ERROR:$FN:No temporary working dir set."; exit 1; fi
791 # Set command strings
792 if ! [ -z "$5" ]; then CMD1
="$5"; else CMD1
="tee /dev/null "; fi # command string 1
793 if ! [ -z "$6" ]; then CMD2
="$6"; else CMD2
="tee /dev/null "; fi # command string 2
794 if ! [ -z "$7" ]; then CMD3
="$7"; else CMD3
="tee /dev/null "; fi # command string 3
795 if ! [ -z "$8" ]; then CMD4
="$8"; else CMD4
="tee /dev/null "; fi # command string 4
797 # Input command string
801 # yell "DEBUG:STATUS:$FN:CMD0:$CMD0"
802 # yell "DEBUG:STATUS:$FN:CMD1:$CMD1"
803 # yell "DEBUG:STATUS:$FN:CMD2:$CMD2"
804 # yell "DEBUG:STATUS:$FN:CMD3:$CMD3"
805 # yell "DEBUG:STATUS:$FN:CMD4:$CMD4"
806 # yell "DEBUG:STATUS:$FN:FILENAME:$FILENAME"
807 # yell "DEBUG:STATUS:$FN:TAR_PATH:$TAR_PATH"
808 # yell "DEBUG:STATUS:$FN:TMP_DIR:$TMP_DIR"
810 # Write to temporary working dir
811 eval "$CMD0 | $CMD1 | $CMD2 | $CMD3 | $CMD4" > "$TMP_DIR"/"$FILENAME";
814 try
tar --append --directory="$TMP_DIR" --file="$TAR_PATH" "$FILENAME";
815 #yell "DEBUG:STATUS:$FN:Finished appendFileTar()."
816 } # Append file to Tar archive
818 # Desc: Checks if string is an age-compatible pubkey
819 # Usage: checkAgePubkey [str pubkey]
821 # Input: arg1: string
822 # Output: return code 0: string is age-compatible pubkey
823 # return code 1: string is NOT an age-compatible pubkey
824 # age stderr (ex: there is stderr if invalid string provided)
825 # Depends: age (v0.1.0-beta2; https://github.com/FiloSottile/age/releases/tag/v1.0.0-beta2 )
829 if echo "test" | age
-a -r "$argPubkey" 1>/dev
/null
; then
836 # Desc: Validates Input
837 # Usage: validateInput [str input] [str input type]
839 # Input: arg1: string to validate
840 # arg2: string specifying input type (ex:"ssh_pubkey")
841 # Output: return code 0: if input string matched specified string type
842 # Depends: bash 5, yell
845 local FN
="${FUNCNAME[0]}";
850 if [[ $# -gt 2 ]]; then yell
"ERROR:$0:$FN:Too many arguments."; exit 1; fi;
853 if [[ -z "$argInput" ]]; then return 1; fi
857 ### Check for alnum/dash base64 (ex: "ssh-rsa AAAAB3NzaC1yc2EAAA")
858 if [[ "$argType" = "ssh_pubkey" ]]; then
859 if [[ "$argInput" =~ ^
[[:alnum
:]-]*[\
]*[[:alnum
:]+/=]*$
]]; then
863 ### Check for age1[:bech32:]
864 if [[ "$argType" = "age_pubkey" ]]; then
865 if [[ "$argInput" =~ ^age1
[qpzry9x8gf2tvdw0s3jn54khce6mua7l
]*$
]]; then
868 # Return error if no condition matched.
870 } # Validates strings
871 magicWriteVersion
() {
872 # Desc: Appends time-stamped VERSION to PATHOUT_TAR
873 # Usage: magicWriteVersion
875 # Input: CONTENT_VERSION, FILEOUT_VERSION, PATHOUT_TAR, DIR_TMP
876 # Input: SCRIPT_VERSION, SCRIPT_URL, AGE_VERSION, AGE_URL, SCRIPT_HOSTNAME
877 # Output: appends tar PATHOUT_TAR
878 # Depends: dateTimeShort, appendArgTar
879 local CONTENT_VERSION pubKeyIndex
881 # Set VERSION file name
882 FILEOUT_VERSION
="$(dateTimeShort)..VERSION";
884 # Gather VERSION data in CONTENT_VERSION
885 CONTENT_VERSION
="SCRIPT_VERSION=$SCRIPT_VERSION";
886 #CONTENT_VERSION="$CONTENT_VERSION""\\n";
887 CONTENT_VERSION
="$CONTENT_VERSION""\\n""SCRIPT_NAME=$SCRIPT_NAME";
888 CONTENT_VERSION
="$CONTENT_VERSION""\\n""SCRIPT_URL=$SCRIPT_URL";
889 CONTENT_VERSION
="$CONTENT_VERSION""\\n""AGE_VERSION=$AGE_VERSION";
890 CONTENT_VERSION
="$CONTENT_VERSION""\\n""AGE_URL=$AGE_URL";
891 CONTENT_VERSION
="$CONTENT_VERSION""\\n""DATE=$(date --iso-8601=seconds)";
892 CONTENT_VERSION
="$CONTENT_VERSION""\\n""HOSTNAME=$SCRIPT_HOSTNAME";
893 ## Add list of recipient pubkeys
894 for pubkey
in "${recPubKeysValid[@]}"; do
896 CONTENT_VERSION
="$CONTENT_VERSION""\\n""PUBKEY_$pubKeyIndex=$pubkey";
898 ## Process newline escapes
899 CONTENT_VERSION
="$(echo -e "$CONTENT_VERSION")"
901 # Write CONTENT_VERSION as file FILEOUT_VERSION and write-append to PATHOUT_TAR
902 appendArgTar
"$CONTENT_VERSION" "$FILEOUT_VERSION" "$PATHOUT_TAR" "$DIR_TMP";
904 } # bkgpslog: write version data to PATHOUT_TAR via appendArgTar()
905 magicGatherWriteBuffer
() {
906 # Desc: bkgpslog-specific meta function for writing data to DIR_TMP then appending each file to PATHOUT_TAR
907 # Inputs: PATHOUT_TAR FILEOUT_{NMEA,GPX,KML} CMD_CONV_{NMEA,GPX,KML} CMD_{COMPRESS,ENCRYPT} DIR_TMP,
908 # Inputs: BUFFER_TTL bufferTTL_STR SCRIPT_HOSTNAME CMD_COMPRESS_SUFFIX CMD_ENCRYPT_SUFFIX
909 # Depends: yell, try, vbm, appendArgTar, tar
910 local FN
="${FUNCNAME[0]}";
911 wait; # Wait to avoid collision with older magicWriteBuffer() instances (see https://www.tldp.org/LDP/abs/html/x9644.html )
912 # Create buffer file with unique name
913 PATHOUT_BUFFER
="$DIR_TMP/buffer$SECONDS";
915 timeout
"$BUFFER_TTL"s gpspipe
-r -o "$PATHOUT_BUFFER" ;
916 timeBufferStart
="$(dateTimeShort "$
(date --date="$BUFFER_TTL seconds ago")")"; # Note start time
917 vbm
"DEBUG:STATUS:$FN:Started magicWriteBuffer().";
918 # Determine file paths (time is start of buffer period)
919 FILEOUT_BASENAME
="$timeBufferStart""--""$bufferTTL_STR""..""$SCRIPT_HOSTNAME""_location" && vbm
"STATUS:Set FILEOUT_BASENAME to:$FILEOUT_BASENAME";
920 ## Files saved to DIR_TMP
921 FILEOUT_NMEA
="$FILEOUT_BASENAME".nmea
"$CMD_COMPRESS_SUFFIX""$CMD_ENCRYPT_SUFFIX" && vbm
"STATUS:Set FILEOUT_NMEA to:$FILEOUT_NMEA";
922 FILEOUT_GPX
="$FILEOUT_BASENAME".gpx
"$CMD_COMPRESS_SUFFIX""$CMD_ENCRYPT_SUFFIX" && vbm
"STATUS:Set FILEOUT_GPX to:$FILEOUT_GPX";
923 FILEOUT_KML
="$FILEOUT_BASENAME".kml
"$CMD_COMPRESS_SUFFIX""$CMD_ENCRYPT_SUFFIX" && vbm
"STATUS:Set FILEOUT_KML to:$FILEOUT_KML";
924 PATHOUT_NMEA
="$DIR_TMP"/"$FILEOUT_NMEA" && vbm
"STATUS:Set PATHOUT_NMEA to:$PATHOUT_NMEA";
925 PATHOUT_GPX
="$DIR_TMP"/"$FILEOUT_GPX" && vbm
"STATUS:Set PATHOUT_GPX to:$PATHOUT_GPX";
926 PATHOUT_KML
="$DIR_TMP"/"$FILEOUT_KML" && vbm
"STATUS:Set PATHOUT_KML to:$PATHOUT_KML";
927 ## Files saved to disk (DIR_OUT)
928 ### one file per day (Ex: "20200731..hostname_location.[.gpx.gz].tar")
929 PATHOUT_TAR
="$DIR_OUT"/"$(dateShort "$
(date --date="$BUFFER_TTL seconds ago")")"..
"$SCRIPT_HOSTNAME""_location""$CMD_COMPRESS_SUFFIX""$CMD_ENCRYPT_SUFFIX".
tar && \
930 vbm
"STATUS:Set PATHOUT_TAR to:$PATHOUT_TAR";
932 vbm
"STATUS:DIR_TMP :$DIR_TMP";
933 vbm
"STATUS:PATHOUT_TAR :$PATHOUT_TAR";
934 vbm
"STATUS:PATHOUT_NMEA:$PATHOUT_NMEA";
935 vbm
"STATUS:PATHOUT_GPX:$PATHOUT_GPX";
936 vbm
"STATUS:PATHOUT_KML:$PATHOUT_KML";
939 # Validate PATHOUT_TAR as tar.
940 checkMakeTar
"$PATHOUT_TAR";
941 ## Add VERSION file if checkMakeTar had to create a tar (exited 1) or replace one (exited 2)
942 if [[ $?
-eq 1 ]] ||
[[ $?
-eq 2 ]]; then magicWriteVersion
; fi
944 # Write bufferBash to PATHOUT_TAR
945 appendFileTar
"$PATHOUT_BUFFER" "$FILEOUT_NMEA" "$PATHOUT_TAR" "$DIR_TMP" "$CMD_CONV_NMEA" "$CMD_COMPRESS" "$CMD_ENCRYPT"; # Write NMEA data
946 appendFileTar
"$PATHOUT_BUFFER" "$FILEOUT_GPX" "$PATHOUT_TAR" "$DIR_TMP" "$CMD_CONV_GPX" "$CMD_COMPRESS" "$CMD_ENCRYPT"; # Write GPX file
947 appendFileTar
"$PATHOUT_BUFFER" "$FILEOUT_KML" "$PATHOUT_TAR" "$DIR_TMP" "$CMD_CONV_KML" "$CMD_COMPRESS" "$CMD_ENCRYPT"; # Write KML file
949 # Remove secured chunks from DIR_TMP
950 rm "$PATHOUT_BUFFER" "$PATHOUT_NMEA" "$PATHOUT_GPX" "$PATHOUT_KML";
951 vbm
"DEBUG:STATUS:$FN:Finished magicWriteBuffer().";
952 } # write buffer to disk
953 magicParseRecipientDir
() {
954 # Desc: Updates recPubKeysValid with pubkeys in dir specified by '-R' option ("recipient directory")
955 # Inputs: vars: OPTION_RECDIR, argRecDir, OPTION_ENCRYPTION
956 # arry: recPubKeysValid
957 # Outputs: arry: recPubKeysValid
958 # Depends: processArguments,
959 local recFileLine updateRecipients recipientDir
960 declare -a candRecPubKeysValid
962 # Check that '-e' and '-R' set
963 if [[ "$OPTION_ENCRYPTION" = "true" ]] && [[ "$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 # Handle case if '-e' set but '-R' not set
995 if [[ "$OPTION_ENCRYPTION" = "true" ]] && [[ ! "$OPTION_RECDIR" = "true" ]]; then
996 yell
"ERROR: \'-e\' set but \'-R\' is not set."; fi;
997 # Handle case if '-R' set but '-e' not set
998 if [[ ! "$OPTION_ENCRYPTION" = "true" ]] && [[ "$OPTION_RECDIR" = "true" ]]; then
999 yell
"ERROR: \'-R\' is set but \'-e\' is not set."; fi;
1000 } # Update recPubKeysValid with argRecDir
1001 magicParseRecipientArgs
() {
1002 # Desc: Parses recipient arguments specified by '-r' option
1003 # Input: vars: OPTION_ENCRYPT from processArguments()
1004 # arry: argRecPubKeys from processArguments()
1005 # Output: vars: CMD_ENCRYPT, CMD_ENCRYPT_SUFFIX
1006 # arry: recPubKeysValid
1007 # Depends: checkapp(), checkAgePubkey(), validateInput(), processArguments()
1010 # Check if encryption option active.
1011 if [[ "$OPTION_ENCRYPT" = "true" ]] && [[ "$OPTION_RECIPIENTS" = "true" ]]; then
1012 if checkapp age
; then # Check that age is available.
1013 for pubkey
in "${argRecPubKeys[@]}"; do # Validate recipient pubkey strings by forming test message
1014 vbm
"DEBUG:Testing pubkey string:$pubkey";
1015 if checkAgePubkey
"$pubkey" && \
1016 ( validateInput
"$pubkey" "ssh_pubkey" || validateInput
"$pubkey" "age_pubkey"); then
1017 #### Form age recipient string
1018 recipients
="$recipients""-r '$pubkey' ";
1019 vbm
"STATUS:Added pubkey for forming age recipient string:""$pubkey";
1020 vbm
"DEBUG:recipients:""$recipients";
1021 #### Add validated pubkey to recPubKeysValid array
1022 recPubKeysValid
+=("$pubkey") && vbm
"DEBUG:recPubkeysValid:pubkey added:$pubkey";
1024 yell
"ERROR:Exit code ""$?"". Invalid recipient pubkey string. Exiting."; exit 1;
1027 vbm
"DEBUG:Finished processing argRecPubKeys array";
1029 ## Form age command string
1030 CMD_ENCRYPT
="age ""$recipients " && vbm
"CMD_ENCRYPT:$CMD_ENCRYPT";
1031 CMD_ENCRYPT_SUFFIX
=".age" && vbm
"CMD_ENCRYPT_SUFFIX:$CMD_ENCRYPT_SUFFIX";
1033 yell
"ERROR:Encryption enabled but \"age\" not found. Exiting."; exit 1;
1036 CMD_ENCRYPT
="tee /dev/null " && vbm
"CMD_ENCRYPT:$CMD_ENCRYPT";
1037 CMD_ENCRYPT_SUFFIX
="" && vbm
"CMD_ENCRYPT_SUFFIX:$CMD_ENCRYPT_SUFFIX";
1038 vbm
"DEBUG:Encryption not enabled."
1040 # Catch case if '-e' is set but '-r' or '-R' is not
1041 if [[ "$OPTION_ENCRYPT" = "true" ]] && [[ ! "$OPTION_RECIPIENTS" = "true" ]]; then
1042 yell
"ERROR:\'-e\' set but no \'-r\' or \'-R\' set."; exit 1; fi;
1043 # Catch case if '-r' or '-R' set but '-e' is not
1044 if [[ ! "$OPTION_ENCRYPT" = "true" ]] && [[ "$OPTION_RECIPIENTS" = "true" ]]; then
1045 yell
"ERROR:\'-r\' or \'-R\' set but \'-e\' is not set."; exit 1; fi;
1046 } # Populate recPubKeysValid with argRecPubKeys; form encryption cmd string and filename suffix
1047 magicParseCompressionArg
() {
1048 # Desc: Parses compression arguments specified by '-c' option
1049 # Input: vars: OPTION_COMPRESS
1050 # Output: CMD_COMPRESS, CMD_COMPRESS_SUFFIX
1051 # Depends: checkapp(), vbm(), gzip,
1052 if [[ "$OPTION_COMPRESS" = "true" ]]; then # Check if compression option active
1053 if checkapp
gzip; then # Check if gzip available
1054 CMD_COMPRESS
="gzip " && vbm
"CMD_COMPRESS:$CMD_COMPRESS";
1055 CMD_COMPRESS_SUFFIX
=".gz" && vbm
"CMD_COMPRESS_SUFFIX:$CMD_COMPRESS_SUFFIX";
1057 yell
"ERROR:Compression enabled but \"gzip\" not found. Exiting."; exit 1;
1060 CMD_COMPRESS
="tee /dev/null " && vbm
"CMD_COMPRESS:$CMD_COMPRESS";
1061 CMD_COMPRESS_SUFFIX
="" && vbm
"CMD_COMPRESS_SUFFIX:$CMD_COMPRESS_SUFFIX";
1062 vbm
"DEBUG:Compression not enabled.";
1064 } # Form compression cmd string and filename suffix
1065 magicInitWorkingDir
() {
1066 # Desc: Determine temporary working directory from defaults or user input
1067 # Usage: magicInitWorkignDir
1068 # Input: vars: OPTION_TEMPDIR, argTempDirPriority, DIR_TMP_DEFAULT
1069 # Input: vars: SCRIPT_TIME_START
1070 # Output: vars: DIR_TMP
1071 # Depends: processArguments(), vbm(), yell()
1072 # Parse '-t' option (user-specified temporary working dir)
1073 ## Set DIR_TMP_PARENT to user-specified value if specified
1074 local DIR_TMP_PARENT
1076 if [[ "$OPTION_TMPDIR" = "true" ]]; then
1077 if [[ -d "$argTempDirPriority" ]]; then
1078 DIR_TMP_PARENT
="$argTempDirPriority";
1080 yell
"WARNING:Specified temporary working directory not valid:$argTempDirPriority";
1081 exit 1; # Exit since user requires a specific temp dir and it is not available.
1084 ## Set DIR_TMP_PARENT to default or fallback otherwise
1085 if [[ -d "$DIR_TMP_DEFAULT" ]]; then
1086 DIR_TMP_PARENT
="$DIR_TMP_DEFAULT";
1087 elif [[ -d /tmp
]]; then
1088 yell
"WARNING:$DIR_TMP_DEFAULT not available. Falling back to /tmp .";
1089 DIR_TMP_PARENT
="/tmp";
1091 yell
"ERROR:No valid working directory available. Exiting.";
1095 ## Set DIR_TMP using DIR_TMP_PARENT and nonce (SCRIPT_TIME_START)
1096 DIR_TMP
="$DIR_TMP_PARENT"/"$SCRIPT_TIME_START""..bkgpslog" && vbm
"DEBUG:Set DIR_TMP to:$DIR_TMP"; # Note: removed at end of main().
1097 } # Sets working dir
1101 processArguments
"$@";
1102 ## Act upon arguments
1103 ### Determine working directory
1104 magicInitWorkingDir
; # Sets DIR_TMP from argTempDirPriority
1105 ### Set output encryption and compression option strings
1106 #### React to "-r" ("encryption recipients") option
1107 magicParseRecipientArgs
; # Updates recPubKeysValid, CMD_ENCRYPT[_SUFFIX]
1108 #### React to "-c" ("compression") option
1109 magicParseCompressionArg
; # Updates CMD_COMPRESS[_SUFFIX]
1110 #### React to "-R" ("recipient directory") option
1111 magicParseRecipientDir
; # Updates recPubKeysValid
1113 # Check that critical apps and dirs are available, display missing ones.
1114 if ! checkapp gpspipe
tar && ! checkdir
"$DIR_OUT" "DIR_TMP"; then
1115 yell
"ERROR:Critical components missing.";
1116 displayMissing
; yell
"Exiting."; exit 1; fi
1118 # Set script lifespan
1119 setScriptTTL
"$SCRIPT_TTL"; # seconds until next new SCRIPT_TTL (ex: "day" or "hour")
1121 # File name substring: encoded bufferTTL
1122 bufferTTL_STR
="$(timeDuration $BUFFER_TTL)";
1124 # Init temp working dir
1125 try mkdir
"$DIR_TMP" && vbm
"DEBUG:Working dir creatd at:$DIR_TMP";
1127 # Initialize 'tar' archive
1128 ## Define output tar path (note: each day gets *one* tar file (Ex: "20200731..hostname_location.[.gpx.gz].tar"))
1129 PATHOUT_TAR
="$DIR_OUT"/"$(dateShort)"..
"$SCRIPT_HOSTNAME""_location""$CMD_COMPRESS_SUFFIX""$CMD_ENCRYPT_SUFFIX".
tar && \
1130 vbm
"STATUS:Set PATHOUT_TAR to:$PATHOUT_TAR";
1131 ## Check that PATHOUT_TAR is a tar. Rename old and create empty one otherwise.
1132 checkMakeTar
"$PATHOUT_TAR" && vbm
"DEBUG:Confirmed or Created to be a tar:$PATHOUT_TAR";
1133 ## Append VERSION file to PATHOUT_TAR
1136 # Define GPS conversion commands
1137 CMD_CONV_NMEA
="tee /dev/null " && vbm
"STATUS:Set CMD_CONV_NMEA to:$CMD_CONV_NMEA"; # tee as passthrough
1138 CMD_CONV_GPX
="gpsbabel -i nmea -f - -o gpx -F - " && vbm
"STATUS:Set CMD_CONV_GPX to:$CMD_CONV_GPX"; # convert NMEA to GPX
1139 CMD_CONV_KML
="gpsbabel -i nmea -f - -o kml -F - " && vbm
"STATUS:Set CMD_CONV_KML to:$CMD_CONV_KML"; # convert NMEA to KML
1141 # MAIN LOOP:Record gps data until script lifespan ends
1142 while [[ "$SECONDS" -lt "$scriptTTL" ]]; do
1143 magicGatherWriteBuffer
&
1144 sleep "$BUFFER_TTL";
1149 try
rm -r "$DIR_TMP";
1151 vbm
"STATUS:Main function finished.";
1153 #===END Declare local script functions===
1154 #==END Define script parameters==
1157 #==BEGIN Perform work and exit==
1158 main
"$@" # Run main function.
1160 #==END Perform work and exit==