style(bkgpslog):Minor boilerplate modification
[EVA-2020-02.git] / exec / bkgpslog
CommitLineData
032f4b05 1#!/bin/bash
d20a8771 2# Desc: Records gps data
032f4b05 3
8fbca23d 4#==BEGIN Define script parameters==
6c30388f 5## Logging Behavior parameters
3fd6a69e 6BUFFER_TTL="300"; # time between file writes
2af0d7d9 7SCRIPT_TTL_TE="day"; # (day|hour)
aa2a49f1
SBS
8#### TZ="UTC"; export TZ; # Default time zone; overridden by '--time-zone=[str]' option
9DIR_TMP_DEFAULT="/dev/shm"; # Default parent of working directory
6c30388f 10
aa2a49f1
SBS
11SCRIPT_TIME_START=$(date +%Y%m%dT%H%M%S.%N);
12PATH="$HOME/.local/bin:$PATH"; # Add "$(systemd-path user-binaries)" path in case apps saved there
13SCRIPT_HOSTNAME=$(hostname); # Save hostname of system running this script.
e56d6109 14SCRIPT_VERSION="0.5.6"; # Define version of script.
ff22a93f
SBS
15SCRIPT_NAME="bkgpslog"; # Define basename of script file.
16SCRIPT_URL="https://gitlab.com/baltakatei/ninfacyzga-01"; # Define wesite hosting this script.
17AGE_VERSION="1.0.0-beta2"; # Define version of age (encryption program)
18AGE_URL="https://github.com/FiloSottile/age/releases/tag/v1.0.0-beta2"; # Define website hosting age.
8fbca23d
SBS
19
20declare -Ag appRollCall # Associative array for storing app status
21declare -Ag fileRollCall # Associative array for storing file status
22declare -Ag dirRollCall # Associative array for storing dir status
320ac29c 23declare -a argRecPubKeys # for processArguments function
52f05d07 24# declare -a errorHistory # for correcting buffer lag
8fbca23d 25
cfc25c90
SBS
26## Initialize variables
27OPTION_VERBOSE=""; OPTION_ENCRYPT=""; OPTION_COMPRESS=""; OPTION_TMPDIR="";
7970872b 28errReset=0; BUFFER_TTL_ADJ_FLOAT="";
cfc25c90 29
8fbca23d
SBS
30#===BEGIN Declare local script functions===
31checkapp() {
32 # Desc: If arg is a command, save result in assoc array 'appRollCall'
33 # Usage: checkapp arg1 arg2 arg3 ...
34 # Input: global assoc. array 'appRollCall'
35 # Output: adds/updates key(value) to global assoc array 'appRollCall'
36 local returnState
37 #echo "DEBUG:$(date +%S.%N)..Starting checkapp function."
38 #echo "DEBUG:args: $@"
39 #echo "DEBUG:returnState:$returnState"
40
41 #===Process Args===
42 for arg in "$@"; do
43 #echo "DEBUG:processing arg:$arg"
0b3dde05 44 if command -v "$arg" 1>/dev/null 2>&1; then # Check if arg is a valid command
8fbca23d
SBS
45 appRollCall[$arg]="true";
46 #echo "DEBUG:appRollCall[$arg]:"${appRollCall[$arg]}
47 if ! [ "$returnState" = "false" ]; then returnState="true"; fi
48 else
49 appRollCall[$arg]="false"; returnState="false";
50 fi
51 done
52
53 #for key in "${!appRollCall[@]}"; do echo "DEBUG:$key => ${appRollCall[$key]}"; done
54 #echo "DEBUG:evaluating returnstate. returnState:"$returnState
55
56 #===Determine function return code===
57 if [ "$returnState" = "true" ]; then
58 #echo "DEBUG:checkapp returns true for $arg";
59 return 0;
60 else
61 #echo "DEBUG:checkapp returns false for $arg";
62 return 1;
63 fi
64} # Check that app exists
65checkfile() {
66 # Desc: If arg is a file path, save result in assoc array 'fileRollCall'
67 # Usage: checkfile arg1 arg2 arg3 ...
68 # Input: global assoc. array 'fileRollCall'
69 # Output: adds/updates key(value) to global assoc array 'fileRollCall';
70 # Output: returns 0 if app found, 1 otherwise
71 local returnState
72
73 #===Process Args===
74 for arg in "$@"; do
75 #echo "DEBUG:processing arg:$arg"
76 if [ -f "$arg" ]; then
77 fileRollCall["$arg"]="true";
78 #echo "DEBUG:fileRollCall[\"$arg\"]:"${fileRollCall["$arg"]}
79 if ! [ "$returnState" = "false" ]; then returnState="true"; fi
80 else
81 fileRollCall["$arg"]="false"; returnState="false";
82 fi
83 done
84
85 #for key in "${!fileRollCall[@]}"; do echo "DEBUG:fileRollCall key [$key] is:${fileRollCall[$key]}"; done
86 #echo "DEBUG:evaluating returnstate. returnState:"$returnState
87
88 #===Determine function return code===
89 if [ "$returnState" = "true" ]; then
90 #echo "DEBUG:checkapp returns true for $arg";
91 return 0;
92 else
93 #echo "DEBUG:checkapp returns false for $arg";
94 return 1;
95 fi
96} # Check that file exists
97checkdir() {
98 # Desc: If arg is a dir path, save result in assoc array 'dirRollCall'
99 # Usage: checkdir arg1 arg2 arg3 ...
100 # Input: global assoc. array 'dirRollCall'
101 # Output: adds/updates key(value) to global assoc array 'dirRollCall';
102 # Output: returns 0 if app found, 1 otherwise
103 local returnState
104
105 #===Process Args===
106 for arg in "$@"; do
107 #echo "DEBUG:processing arg:$arg"
108 if [ -d "$arg" ]; then
109 dirRollCall["$arg"]="true";
110 #echo "DEBUG:dirRollCall[\"$arg\"]:"${dirRollCall["$arg"]}
111 if ! [ "$returnState" = "false" ]; then returnState="true"; fi
bcf09dcc 112 elif [ "$arg" = "" ]; then
8fbca23d 113 dirRollCall["$arg"]="false"; returnState="false";
bcf09dcc
SBS
114 else
115 returnState="false";
8fbca23d
SBS
116 fi
117 done
118
119 #for key in "${!dirRollCall[@]}"; do echo "DEBUG:dirRollCall key [$key] is:${dirRollCall[$key]}"; done
120 #echo "DEBUG:evaluating returnstate. returnState:"$returnState
121
122 #===Determine function return code===
123 if [ "$returnState" = "true" ]; then
124 #echo "DEBUG:checkapp returns true for $arg";
125 return 0;
126 else
127 #echo "DEBUG:checkapp returns false for $arg";
128 return 1;
129 fi
130} # Check that dir exists
032f4b05 131
c609b9c8
SBS
132# Yell, Die, Try Three-Fingered Claw technique
133# Ref/Attrib: https://stackoverflow.com/a/25515370
134yell() { echo "$0: $*" >&2; }
135die() { yell "$*"; exit 111; }
136try() { "$@" || die "cannot $*"; }
137
032f4b05
SBS
138echoerr() {
139 echo "$@" 1>&2; # Define stderr echo function.
140} # Define stderr message function.
141showUsage() {
142 echoerr "USAGE:"
94e094d1 143 echoerr " bkgpslog [ options ]"
032f4b05
SBS
144 echoerr
145 echoerr "OPTIONS:"
146 echoerr " -h, --help"
147 echoerr " Display help information."
032f4b05
SBS
148 echoerr " --version"
149 echoerr " Display script version."
032f4b05
SBS
150 echoerr " -v, --verbose"
151 echoerr " Display debugging info."
17d49005
SBS
152 echoerr " -e, --encrypt"
153 echoerr " Encrypt output."
2af0d7d9 154 echoerr " -r, --recipient [ string pubkey ]"
f7f33d33 155 echoerr " Specify recipient. May be age or ssh pubkey."
2af0d7d9 156 echoerr " May be specified multiple times for multiple pubkeys."
f7f33d33 157 echoerr " See https://github.com/FiloSottile/age"
2af0d7d9 158 echoerr " -o, --output [ path dir ]"
032f4b05 159 echoerr " Specify output directory to save logs."
408a342b
SBS
160 echoerr " -c, --compress"
161 echoerr " Compress output with gzip (before encryption if enabled)."
f7f33d33
SBS
162 echoerr " -z, --time-zone"
163 echoerr " Specify time zone. (ex: \"America/New_York\")"
2af0d7d9 164 echoerr " -t, --temp-dir [path dir]"
f7f33d33
SBS
165 echoerr " Specify parent directory for temporary working directory."
166 echoerr " Default: \"/dev/shm\""
2af0d7d9 167 echoerr " -R, --recipient-dir [path dir]"
320ac29c 168 echoerr " Specify directory containing files whose first lines are"
2af0d7d9
SBS
169 echoerr " to be interpreted as pubkey strings (see \\'-r\\' option)."
170 echoerr " -b, --buffer-ttl [integer]"
171 echoerr " Specify custom buffer period in seconds (default: 300 seconds)"
172 echoerr " -B, --script-ttl [integer]"
173 echoerr " Specify custom script time-to-live in seconds (default: \"day\")"
f7f33d33 174 echoerr
17d49005 175 echoerr "EXAMPLE: (bash script lines)"
320ac29c
SBS
176 echoerr "/bin/bash bkgpslog -v -e -c \\"
177 echoerr "-z \"UTC\" -t \"/dev/shm\" \\"
17d49005
SBS
178 echoerr "-r age1mrmfnwhtlprn4jquex0ukmwcm7y2nxlphuzgsgv8ew2k9mewy3rs8u7su5 \\"
179 echoerr "-r age1ala848kqrvxc88rzaauc6vc5v0fqrvef9dxyk79m0vjea3hagclswu0lgq \\"
180 echoerr "-o ~/Sync/Location"
032f4b05
SBS
181} # Display information on how to use this script.
182showVersion() {
183 echoerr "$SCRIPT_VERSION"
184} # Display script version.
185vbm() {
0b3dde05
SBS
186 # Usage: vbm "DEBUG:verbose message here"
187 # Description: Prints verbose message ("vbm") to stderr if OPTION_VERBOSE is set to "true".
188 # Input:
189 # - OPTION_VERBOSE variable set by processArguments function. (ex: "true", "false")
190 # - "$@" positional arguments fed to this function.
191 # Output: stderr
192 # Script function dependencies: echoerr
193 # External function dependencies: echo
194 # Last modified: 2020-04-11T23:57Z
195 # Last modified by: Steven Baltakatei Sandoval
196 # License: GPLv3+
197 # Ref./Attrib:
198
199 if [ "$OPTION_VERBOSE" = "true" ]; then
200 FUNCTION_TIME=$(date --iso-8601=ns); # Save current time in nano seconds.
201 echoerr "[$FUNCTION_TIME] ""$*"; # Display argument text.
032f4b05 202 fi
0b3dde05
SBS
203
204 # End function
205 return 0; # Function finished.
032f4b05
SBS
206} # Verbose message display function.
207processArguments() {
208 while [ ! $# -eq 0 ]; do # While number of arguments ($#) is not (!) equal to (-eq) zero (0).
d6896d3b
SBS
209 #echoerr "DEBUG:Starting processArguments while loop."
210 #echoerr "DEBUG:Provided arguments are:""$*"
032f4b05 211 case "$1" in
c1bbf9f7 212 -h | --help) showUsage; exit 1;; # Display usage.
032f4b05 213 --version) showVersion; exit 1;; # Show version
c1bbf9f7 214 -v | --verbose) OPTION_VERBOSE="true"; vbm "DEBUG:Verbose mode enabled.";; # Enable verbose mode.
6c30388f 215 -o | --output) if [ -d "$2" ]; then DIR_OUT="$2"; vbm "DEBUG:DIR_OUT:$DIR_OUT"; shift; fi ;; # Define output directory.
6a2cd888
SBS
216 -e | --encrypt) OPTION_ENCRYPT="true"; vbm "DEBUG:Encrypted output mode enabled.";; # Enable encryption
217 -r | --recipient) OPTION_RECIPIENTS="true"; argRecPubKeys+=("$2"); vbm "STATUS:pubkey added:""$2"; shift;; # Add recipients
218 -c | --compress) OPTION_COMPRESS="true"; vbm "DEBUG:Compressed output mode enabled.";; # Enable compression
219 -z | --time-zone) try setTimeZoneEV "$2"; shift;; # Set timestamp timezone
c9c573da 220 -t | --temp-dir) OPTION_TMPDIR="true" && argTempDirPriority="$2"; shift;; # Set time zone
6a2cd888 221 -R | --recipient-dir) OPTION_RECIPIENTS="true"; OPTION_RECDIR="true" && argRecDir="$2"; shift;; # Add recipient watch dir
2af0d7d9
SBS
222 -b | --buffer-ttl) OPTION_CUSTOM_BUFFERTTL="true" && argCustomBufferTTL="$2"; shift;; # Set custom buffer period (default: 300 seconds)
223 -B | --script-ttl) OPTION_CUSTOM_SCRIPTTTL_TE="true" && argCustomScriptTTL="$2"; shift;; # Set custom script TTL (default: "day")
b0da06ca 224 *) echoerr "ERROR: Unrecognized argument: $1"; echoerr "STATUS:All arguments:$*"; exit 1;; # Handle unrecognized options.
032f4b05
SBS
225 esac
226 shift
227 done
228} # Argument Processing
6c30388f
SBS
229setTimeZoneEV(){
230 # Desc: Set time zone environment variable TZ
231 # Usage: setTimeZoneEV arg1
232 # Input: arg1: 'date'-compatible timezone string (ex: "America/New_York")
233 # TZDIR env var (optional; default: "/usr/share/zoneinfo")
234 # Output: exports TZ
235 # exit code 0 on success
236 # exit code 1 on incorrect number of arguments
237 # exit code 2 if unable to validate arg1
238 # Depends: yell, printenv, bash 5
239 # Tested on: Debian 10
240 ARG1="$1"
241 local tzDir returnState
242 if ! [[ $# -eq 1 ]]; then
243 yell "ERROR:Invalid argument count.";
244 return 1;
245 fi
246
247 # Read TZDIR env var if available
248 if printenv TZDIR 1>/dev/null 2>&1; then
249 tzDir="$(printenv TZDIR)";
250 else
251 tzDir="/usr/share/zoneinfo";
252 fi
253
254 # Validate TZ string
255 if ! [[ -f "$tzDir"/"$ARG1" ]]; then
256 yell "ERROR:Invalid time zone argument.";
257 return 2;
258 else
259 # Export ARG1 as TZ environment variable
260 TZ="$ARG1" && export TZ && returnState="true";
261 fi
262
263 # Determine function return code
264 if [ "$returnState" = "true" ]; then
265 return 0;
266 fi
267} # Exports TZ environment variable
c2aaff78
SBS
268timeUntilNextDay(){
269 # Desc: Report seconds until next day.
3395ae22 270 # Version: 1.0.0
c2aaff78 271 # Output: stdout: integer seconds until next day
8fbca23d 272 # Output: exit code 0 if stdout > 0; 1 if stdout = 0; 2 if stdout < 0
c2aaff78
SBS
273 # Usage: timeUntilNextDay
274 # Usage: if ! myTTL="$(timeUntilNextDay)"; then yell "ERROR in if statement"; exit 1; fi
3395ae22
SBS
275 # Depends: date 8, echo 8, yell, try
276
c2aaff78 277 local returnState TIME_CURRENT TIME_NEXT_DAY SECONDS_UNTIL_NEXT_DAY
3395ae22 278
8fbca23d 279 TIME_CURRENT="$(date --iso-8601=seconds)" ; # Produce `date`-parsable current timestamp with resolution of 1 second.
c2aaff78
SBS
280 TIME_NEXT_DAY="$(date -d "$TIME_CURRENT next day" --iso-8601=date)"; # Produce timestamp of beginning of tomorrow with resolution of 1 second.
281 SECONDS_UNTIL_NEXT_DAY="$(( $(date +%s -d "$TIME_NEXT_DAY") - $(date +%s -d "$TIME_CURRENT") ))" ; # Calculate seconds until closest future midnight (res. 1 second).
282 if [[ "$SECONDS_UNTIL_NEXT_DAY" -gt 0 ]]; then
283 returnState="true";
284 elif [[ "$SECONDS_UNTIL_NEXT_DAY" -eq 0 ]]; then
3395ae22 285 returnState="warning_zero";
c2aaff78
SBS
286 yell "WARNING:Reported time until next day exactly zero.";
287 elif [[ "$SECONDS_UNTIL_NEXT_DAY" -lt 0 ]]; then
3395ae22 288 returnState="warning_negative";
c2aaff78
SBS
289 yell "WARNING:Reported time until next day is negative.";
290 fi
291
292 try echo "$SECONDS_UNTIL_NEXT_DAY"; # Report
293
3395ae22 294 # Determine function return code
c2aaff78
SBS
295 if [[ "$returnState" = "true" ]]; then
296 return 0;
3395ae22 297 elif [[ "$returnState" = "warning_zero" ]]; then
c2aaff78 298 return 1;
3395ae22 299 elif [[ "$returnState" = "warning_negative" ]]; then
c2aaff78
SBS
300 return 2;
301 fi
302} # Report seconds until next day
303timeUntilNextHour(){
304 # Desc: Report seconds until next hour
3395ae22 305 # Version 1.0.0
c2aaff78
SBS
306 # Output: stdout: integer seconds until next hour
307 # Output: exit code 0 if stdout > 0; 1 if stdout = 0; 2 if stdout < 0
308 # Usage: timeUntilNextHour
309 # Usage: if ! myTTL="$(timeUntilNextHour)"; then yell "ERROR in if statement"; exit 1; fi
3395ae22 310
c2aaff78
SBS
311 local returnState TIME_CURRENT TIME_NEXT_HOUR SECONDS_UNTIL_NEXT_HOUR
312 TIME_CURRENT="$(date --iso-8601=seconds)"; # Produce `date`-parsable current timestamp with resolution of 1 second.
313 TIME_NEXT_HOUR="$(date -d "$TIME_CURRENT next hour" --iso-8601=hours)"; # Produce `date`-parsable current time stamp with resolution of 1 second.
314 SECONDS_UNTIL_NEXT_HOUR="$(( $(date +%s -d "$TIME_NEXT_HOUR") - $(date +%s -d "$TIME_CURRENT") ))"; # Calculate seconds until next hour (res. 1 second).
315 if [[ "$SECONDS_UNTIL_NEXT_HOUR" -gt 0 ]]; then
8fbca23d 316 returnState="true";
c2aaff78 317 elif [[ "$SECONDS_UNTIL_NEXT_HOUR" -eq 0 ]]; then
3395ae22 318 returnState="warning_zero";
c2aaff78
SBS
319 yell "WARNING:Reported time until next hour exactly zero.";
320 elif [[ "$SECONDS_UNTIL_NEXT_HOUR" -lt 0 ]]; then
3395ae22 321 returnState="warning_negative";
c2aaff78 322 yell "WARNING:Reported time until next hour is negative.";
8fbca23d 323 fi
032f4b05 324
c2aaff78 325 try echo "$SECONDS_UNTIL_NEXT_HOUR"; # Report
8fbca23d 326
3395ae22 327 # Determine function return code
8fbca23d
SBS
328 if [[ "$returnState" = "true" ]]; then
329 return 0;
3395ae22 330 elif [[ "$returnState" = "warning_zero" ]]; then
8fbca23d 331 return 1;
3395ae22 332 elif [[ "$returnState" = "warning_negative" ]]; then
8fbca23d
SBS
333 return 2;
334 fi
c2aaff78 335} # Report seconds until next hour
8fbca23d
SBS
336dateTimeShort(){
337 # Desc: Timestamp without separators (YYYYmmddTHHMMSS+zzzz)
e47e8048
SBS
338 # Usage: dateTimeShort ([str date])
339 # Version 1.1.0
340 # Input: arg1: 'date'-parsable timestamp string (optional)
8fbca23d 341 # Output: stdout: timestamp (ISO-8601, no separators)
e47e8048 342 # Depends: yell
cf85eedd 343 local TIME_CURRENT TIME_CURRENT_SHORT argTime
e47e8048
SBS
344
345 argTime="$1";
346 # Get Current Time
8fbca23d 347 TIME_CURRENT="$(date --iso-8601=seconds)" ; # Produce `date`-parsable current timestamp with resolution of 1 second.
e47e8048
SBS
348 # Decide to parse current or supplied date
349 ## Check if time argument empty
350 if [[ -z "$argTime" ]]; then
351 ## T: Time argument empty, use current time
352 TIME_INPUT="$TIME_CURRENT";
353 else
354 ## F: Time argument exists, validate time
355 if date --date="$argTime" 1>/dev/null 2>&1; then
356 ### T: Time argument is valid; use it
357 TIME_INPUT="$argTime";
358 else
359 ### F: Time argument not valid; exit
fc4840f5 360 yell "ERROR:Invalid time argument supplied: \"$argTime\""; yell "Exiting."; exit 1;
e47e8048
SBS
361 fi
362 fi
363 # Construct and deliver separator-les date string
364 TIME_CURRENT_SHORT="$(date -d "$TIME_INPUT" +%Y%m%dT%H%M%S%z)";
8fbca23d 365 echo "$TIME_CURRENT_SHORT";
6c30388f
SBS
366} # Get YYYYmmddTHHMMSS±zzzz
367dateShort(){
368 # Desc: Date without separators (YYYYmmdd)
e47e8048
SBS
369 # Usage: dateShort ([str date])
370 # Version: 1.1.0
371 # Input: arg1: 'date'-parsable timestamp string (optional)
6c30388f 372 # Output: stdout: date (ISO-8601, no separators)
e47e8048 373 # Depends: yell
6c30388f 374 local TIME_CURRENT DATE_CURRENT_SHORT
e47e8048
SBS
375
376 argTime="$1";
377 # Get Current Time
6c30388f 378 TIME_CURRENT="$(date --iso-8601=seconds)" ; # Produce `date`-parsable current timestamp with resolution of 1 second.
e47e8048
SBS
379 # Decide to parse current or supplied date
380 ## Check if time argument empty
381 if [[ -z "$argTime" ]]; then
382 ## T: Time argument empty, use current time
383 TIME_INPUT="$TIME_CURRENT";
384 else
385 ## F: Time argument exists, validate time
386 if date --date="$argTime" 1>/dev/null 2>&1; then
387 ### T: Time argument is valid; use it
388 TIME_INPUT="$argTime";
389 else
390 ### F: Time argument not valid; exit
e56d6109 391 yell "ERROR:Invalid time argument supplied: \"$argTime\""; yell "Exiting."; exit 1;
e47e8048
SBS
392 fi
393 fi
394 # Construct and deliver separator-les date string
395 DATE_CURRENT_SHORT="$(date -d "$TIME_INPUT" +%Y%m%d)"; # Produce separator-less current date with resolution 1 day.
6c30388f
SBS
396 echo "$DATE_CURRENT_SHORT";
397} # Get YYYYmmdd
398timeDuration(){
a7b4a273 399 # Desc: Given seconds, output ISO-8601 duration string
6c30388f
SBS
400 # Ref/Attrib: ISO-8601:2004(E), §4.4.4.2 Representations of time intervals by duration and context information
401 # Note: "1 month" ("P1M") is assumed to be "30 days" (see ISO-8601:2004(E), §2.2.1.2)
a7b4a273
SBS
402 # Usage: timeDuration [1:seconds] ([2:precision])
403 # Version: 1.0.3
6c30388f
SBS
404 # Input: arg1: seconds as base 10 integer >= 0 (ex: 3601)
405 # arg2: precision level (optional; default=2)
406 # Output: stdout: ISO-8601 duration string (ex: "P1H1S", "P2Y10M15DT10H30M20S")
a7b4a273
SBS
407 # exit code 0: success
408 # exit code 1: error_input
409 # exit code 2: error_unknown
6c30388f 410 # Example: 'timeDuration 111111 3' yields 'P1DT6H51M'
3395ae22 411 # Depends: date 8 (gnucoreutils), yell,
a7b4a273 412 local returnState argSeconds argPrecision remainder precision witherPrecision
3395ae22
SBS
413 local fullYears fullMonths fullDays fullHours fullMinutes fullSeconds
414 local displayYears displayMonths displayDays displayHours displayMinutes displaySeconds
415 local hasYears hasMonths hasDays hasHours hasMinutes hasSeconds
416
a7b4a273
SBS
417 argSeconds="$1"; # read arg1 (seconds)
418 argPrecision="$2"; # read arg2 (precision)
6c30388f 419 precision=2; # set default precision
6c30388f
SBS
420
421 # Check that between one and two arguments is supplied
422 if ! { [[ $# -ge 1 ]] && [[ $# -le 2 ]]; }; then
423 yell "ERROR:Invalid number of arguments:$# . Exiting.";
a7b4a273 424 returnState="error_input"; fi
6c30388f 425
a7b4a273 426 # Check that argSeconds provided
6c30388f 427 if [[ $# -ge 1 ]]; then
a7b4a273
SBS
428 ## Check that argSeconds is a positive integer
429 if [[ "$argSeconds" =~ ^[[:digit:]]+$ ]]; then
3395ae22 430 :
6c30388f 431 else
a7b4a273
SBS
432 yell "ERROR:argSeconds not a digit.";
433 returnState="error_input";
6c30388f
SBS
434 fi
435 else
436 yell "ERROR:No argument provided. Exiting.";
437 exit 1;
438 fi
439
a7b4a273 440 # Consider whether argPrecision was provided
6c30388f 441 if [[ $# -eq 2 ]]; then
a7b4a273
SBS
442 # Check that argPrecision is a positive integer
443 if [[ "$argPrecision" =~ ^[[:digit:]]+$ ]] && [[ "$argPrecision" -gt 0 ]]; then
444 precision="$argPrecision";
6c30388f 445 else
a7b4a273
SBS
446 yell "ERROR:argPrecision not a positive integer. (is $argPrecision ). Leaving early.";
447 returnState="error_input";
6c30388f
SBS
448 fi;
449 else
3395ae22 450 :
6c30388f
SBS
451 fi;
452
a7b4a273 453 remainder="$argSeconds" ; # seconds
6c30388f
SBS
454 ## Calculate full years Y, update remainder
455 fullYears=$(( remainder / (365*24*60*60) ));
456 remainder=$(( remainder - (fullYears*365*24*60*60) ));
457 ## Calculate full months M, update remainder
458 fullMonths=$(( remainder / (30*24*60*60) ));
459 remainder=$(( remainder - (fullMonths*30*24*60*60) ));
460 ## Calculate full days D, update remainder
461 fullDays=$(( remainder / (24*60*60) ));
462 remainder=$(( remainder - (fullDays*24*60*60) ));
463 ## Calculate full hours H, update remainder
464 fullHours=$(( remainder / (60*60) ));
465 remainder=$(( remainder - (fullHours*60*60) ));
466 ## Calculate full minutes M, update remainder
467 fullMinutes=$(( remainder / (60) ));
468 remainder=$(( remainder - (fullMinutes*60) ));
469 ## Calculate full seconds S, update remainder
470 fullSeconds=$(( remainder / (1) ));
471 remainder=$(( remainder - (remainder*1) ));
472 ## Check which fields filled
473 if [[ $fullYears -gt 0 ]]; then hasYears="true"; else hasYears="false"; fi
474 if [[ $fullMonths -gt 0 ]]; then hasMonths="true"; else hasMonths="false"; fi
475 if [[ $fullDays -gt 0 ]]; then hasDays="true"; else hasDays="false"; fi
476 if [[ $fullHours -gt 0 ]]; then hasHours="true"; else hasHours="false"; fi
477 if [[ $fullMinutes -gt 0 ]]; then hasMinutes="true"; else hasMinutes="false"; fi
478 if [[ $fullSeconds -gt 0 ]]; then hasSeconds="true"; else hasSeconds="false"; fi
479
480 ## Determine which fields to display (see ISO-8601:2004 §4.4.3.2)
481 witherPrecision="false"
482
483 ### Years
484 if $hasYears && [[ $precision -gt 0 ]]; then
485 displayYears="true";
486 witherPrecision="true";
487 else
488 displayYears="false";
489 fi;
490 if $witherPrecision; then ((precision--)); fi;
491
492 ### Months
493 if $hasMonths && [[ $precision -gt 0 ]]; then
494 displayMonths="true";
495 witherPrecision="true";
496 else
497 displayMonths="false";
498 fi;
499 if $witherPrecision && [[ $precision -gt 0 ]]; then
500 displayMonths="true";
501 fi;
502 if $witherPrecision; then ((precision--)); fi;
503
504 ### Days
505 if $hasDays && [[ $precision -gt 0 ]]; then
506 displayDays="true";
507 witherPrecision="true";
508 else
509 displayDays="false";
510 fi;
511 if $witherPrecision && [[ $precision -gt 0 ]]; then
512 displayDays="true";
513 fi;
514 if $witherPrecision; then ((precision--)); fi;
515
516 ### Hours
517 if $hasHours && [[ $precision -gt 0 ]]; then
518 displayHours="true";
519 witherPrecision="true";
520 else
521 displayHours="false";
522 fi;
523 if $witherPrecision && [[ $precision -gt 0 ]]; then
524 displayHours="true";
525 fi;
526 if $witherPrecision; then ((precision--)); fi;
527
528 ### Minutes
529 if $hasMinutes && [[ $precision -gt 0 ]]; then
530 displayMinutes="true";
531 witherPrecision="true";
532 else
533 displayMinutes="false";
534 fi;
535 if $witherPrecision && [[ $precision -gt 0 ]]; then
536 displayMinutes="true";
537 fi;
538 if $witherPrecision; then ((precision--)); fi;
539
540 ### Seconds
541
542 if $hasSeconds && [[ $precision -gt 0 ]]; then
543 displaySeconds="true";
544 witherPrecision="true";
545 else
546 displaySeconds="false";
547 fi;
548 if $witherPrecision && [[ $precision -gt 0 ]]; then
549 displaySeconds="true";
550 fi;
551 if $witherPrecision; then ((precision--)); fi;
552
6c30388f
SBS
553 ## Determine whether or not the "T" separator is needed to separate date and time elements
554 if ( $displayHours || $displayMinutes || $displaySeconds); then
555 displayDateTime="true"; else displayDateTime="false"; fi
556
557 ## Construct duration output string
558 OUTPUT="P"
559 if $displayYears; then
560 OUTPUT=$OUTPUT$fullYears"Y"; fi
561 if $displayMonths; then
562 OUTPUT=$OUTPUT$fullMonths"M"; fi
563 if $displayDays; then
564 OUTPUT=$OUTPUT$fullDays"D"; fi
565 if $displayDateTime; then
566 OUTPUT=$OUTPUT"T"; fi
567 if $displayHours; then
568 OUTPUT=$OUTPUT$fullHours"H"; fi
569 if $displayMinutes; then
570 OUTPUT=$OUTPUT$fullMinutes"M"; fi
571 if $displaySeconds; then
572 OUTPUT=$OUTPUT$fullSeconds"S"; fi
573
574 ## Output duration string to stdout
a7b4a273 575 echo "$OUTPUT" && returnState="true";
6c30388f
SBS
576
577 #===Determine function return code===
578 if [ "$returnState" = "true" ]; then
579 return 0;
a7b4a273
SBS
580 elif [ "$returnState" = "error_input" ]; then
581 yell "ERROR:input";
6c30388f 582 return 1;
a7b4a273
SBS
583 else
584 yell "ERROR:Unknown";
585 return 2;
6c30388f
SBS
586 fi
587
588} # Get duration (ex: PT10M4S )
589displayMissing() {
590 # Desc: Displays missing apps, files, and dirs
591 # Usage: displayMissing
592 # Input: associative arrays: appRollCall, fileRollCall, dirRollCall
593 # Output: stderr messages
594 #==BEGIN Display errors==
595 #===BEGIN Display Missing Apps===
596 missingApps="Missing apps :"
597 #for key in "${!appRollCall[@]}"; do echo "DEBUG:$key => ${appRollCall[$key]}"; done
598 for key in "${!appRollCall[@]}"; do
599 value="${appRollCall[$key]}"
600 if [ "$value" = "false" ]; then
601 #echo "DEBUG:Missing apps: $key => $value";
602 missingApps="$missingApps""$key "
603 appMissing="true"
604 fi
605 done
606 if [ "$appMissing" = "true" ]; then # Only indicate if an app is missing.
607 echo "$missingApps" 1>&2;
608 fi
609 #===END Display Missing Apps===
610
611 #===BEGIN Display Missing Files===
612 missingFiles="Missing files:"
613 #for key in "${!fileRollCall[@]}"; do echo "DEBUG:$key => ${fileRollCall[$key]}"; done
614 for key in "${!fileRollCall[@]}"; do
615 value="${fileRollCall[$key]}"
616 if [ "$value" = "false" ]; then
617 #echo "DEBUG:Missing files: $key => $value";
618 missingFiles="$missingFiles""$key "
619 fileMissing="true"
620 fi
621 done
622 if [ "$fileMissing" = "true" ]; then # Only indicate if an app is missing.
623 echo "$missingFiles" 1>&2;
624 fi
625 #===END Display Missing Files===
626
627 #===BEGIN Display Missing Directories===
628 missingDirs="Missing dirs:"
629 #for key in "${!dirRollCall[@]}"; do echo "DEBUG:$key => ${dirRollCall[$key]}"; done
630 for key in "${!dirRollCall[@]}"; do
631 value="${dirRollCall[$key]}"
632 if [ "$value" = "false" ]; then
633 #echo "DEBUG:Missing dirs: $key => $value";
634 missingDirs="$missingDirs""$key "
635 dirMissing="true"
636 fi
637 done
638 if [ "$dirMissing" = "true" ]; then # Only indicate if an dir is missing.
639 echo "$missingDirs" 1>&2;
640 fi
641 #===END Display Missing Directories===
642
643 #==END Display errors==
644} # Display missing apps, files, dirs
2af0d7d9
SBS
645magicSetScriptTTL() {
646 #Desc: Sets script_TTL seconds from provided time_element string argument
647 #Usage: magicSetScriptTTL [str time_element]
648 #Input: arg1: string (Ex: SCRIPT_TTL_TE; "day" or "hour")
649 #Output: var: SCRIPT_TTL (integer seconds)
650 #Depends: timeUntilNextHour, timeUntilNextDay
651 local argTimeElement
652 argTimeElement="$1"
653 if [[ "$argTimeElement" = "day" ]]; then
6c30388f 654 # Set script lifespan to end at start of next day
2af0d7d9
SBS
655 if ! SCRIPT_TTL="$(timeUntilNextDay)"; then
656 if [[ "$SCRIPT_TTL" -eq 0 ]]; then
657 ((SCRIPT_TTL++)); # Add 1 because 0 would cause 'timeout' to never timeout.
6c30388f
SBS
658 else
659 yell "ERROR: timeUntilNextDay exit code $?"; exit 1;
660 fi;
661 fi;
2af0d7d9 662 elif [[ "$argTimeElement" = "hour" ]]; then
6c30388f 663 # Set script lifespan to end at start of next hour
2af0d7d9
SBS
664 if ! SCRIPT_TTL="$(timeUntilNextHour)"; then
665 if [[ "$SCRIPT_TTL" -eq 0 ]]; then
666 ((SCRIPT_TTL++)); # Add 1 because 0 would cause 'timeout' to never timeout.
6c30388f
SBS
667 else
668 yell "ERROR: timeUntilNextHour exit code $?"; exit 1;
669 fi;
670 fi;
671 else
2af0d7d9 672 yell "ERROR:Invalid argument for setScriptTTL function:$argTimeElement"; exit 1;
6c30388f
SBS
673 fi
674} # Seconds until next (day|hour).
f6fb18bd
SBS
675checkMakeTar() {
676 # Desc: Checks that a valid tar archive exists, creates one otherwise
677 # Usage: checkMakeTar [ path ]
2fc10824 678 # Version: 1.0.1
f6fb18bd 679 # Input: arg1: path of tar archive
2fc10824
SBS
680 # Output: exit code 0 : tar readable
681 # exit code 1 : tar missing; created
682 # exit code 2 : tar not readable; moved; replaced
f6fb18bd 683 # Depends: try, tar, date
2fc10824
SBS
684 local PATH_TAR returnFlag0 returnFlag1 returnFlag2
685 PATH_TAR="$1"
f6fb18bd
SBS
686
687 # Check if file is a valid tar archive
688 if tar --list --file="$PATH_TAR" 1>/dev/null 2>&1; then
689 ## T1: return success
2fc10824 690 returnFlag0="tar valid";
f6fb18bd
SBS
691 else
692 ## F1: Check if file exists
693 if [[ -f "$PATH_TAR" ]]; then
694 ### T: Rename file
2fc10824
SBS
695 try mv "$PATH_TAR" "$PATH_TAR""--broken--""$(date +%Y%m%dT%H%M%S)" && \
696 returnFlag1="tar moved";
f6fb18bd
SBS
697 else
698 ### F: -
699 :
700 fi
701 ## F2: Create tar archive, return 0
2fc10824
SBS
702 try tar --create --file="$PATH_TAR" --files-from=/dev/null && \
703 returnFlag2="tar created";
704 fi
705
706 # Determine function return code
707 if [[ "$returnFlag0" = "tar valid" ]]; then
f6fb18bd 708 return 0;
2fc10824
SBS
709 elif [[ "$returnFlag2" = "tar created" ]] && ! [[ "$returnFlag1" = "tar moved" ]]; then
710 return 1; # tar missing so created
711 elif [[ "$returnFlag2" = "tar created" ]] && [[ "$returnFlag1" = "tar moved" ]]; then
712 return 2; # tar not readable so moved; replaced
f6fb18bd
SBS
713 fi
714} # checks if arg1 is tar; creates one otherwise
66af6225
SBS
715appendArgTar(){
716 # Desc: Writes first argument to temporary file with arguments as options, then appends file to tar
e47e8048 717 # Usage: appendArgTar "$(echo "Data to be written.")" [name of file to be inserted] [tar path] [temp dir] ([cmd1] [cmd2] [cmd3] [cmd4]...)
f665ce95 718 # Version: 1.0.3
66af6225
SBS
719 # Input: arg1: data to be written
720 # arg2: file name of file to be inserted into tar
721 # arg3: tar archive path (must exist first)
722 # arg4: temporary working dir
723 # arg5+: command strings (ex: "gpsbabel -i nmea -f - -o kml -F - ")
724 # Output: file written to disk
725 # Example: decrypt multiple large files in parallel
726 # appendArgTar "$(cat /tmp/largefile1.gpg)" "largefile1" $HOME/archive.tar /tmp "gpg --decrypt" &
727 # appendArgTar "$(cat /tmp/largefile2.gpg)" "largefile2" $HOME/archive.tar /tmp "gpg --decrypt" &
728 # appendArgTar "$(cat /tmp/largefile3.gpg)" "largefile3" $HOME/archive.tar /tmp "gpg --decrypt" &
66af6225 729 # Depends: bash 5
f665ce95 730 # Ref/Attrib: Using 'eval' to construct command strings https://askubuntu.com/a/476533
66af6225 731
d9fe1eba 732 # Save function name
d7138c7f
SBS
733 local FN="${FUNCNAME[0]}";
734 #yell "DEBUG:STATUS:$FN:Finished appendArgTar()."
d9fe1eba 735
66af6225 736 # Set file name
d9fe1eba 737 if ! [ -z "$2" ]; then FILENAME="$2"; else yell "ERROR:$FN:Not enough arguments."; exit 1; fi
66af6225
SBS
738
739 # Check tar path is a file
d9fe1eba 740 if [ -f "$3" ]; then TAR_PATH="$3"; else yell "ERROR:$FN:Tar archive arg not a file."; exit 1; fi
66af6225
SBS
741
742 # Check temp dir arg
d9fe1eba 743 if ! [ -z "$4" ]; then TMP_DIR="$4"; else yell "ERROR:$FN:No temporary working dir set."; exit 1; fi
66af6225
SBS
744
745 # Set command strings
746 if ! [ -z "$5" ]; then CMD1="$5"; else CMD1="tee /dev/null "; fi # command string 1
747 if ! [ -z "$6" ]; then CMD2="$6"; else CMD2="tee /dev/null "; fi # command string 2
748 if ! [ -z "$7" ]; then CMD3="$7"; else CMD3="tee /dev/null "; fi # command string 3
749 if ! [ -z "$8" ]; then CMD4="$8"; else CMD4="tee /dev/null "; fi # command string 4
750
f665ce95
SBS
751 # Input command
752 CMD0="echo \"\$1\""
753
d7138c7f 754 # # Debug
f665ce95 755 # yell "DEBUG:STATUS:$FN:CMD0:$CMD0"
d7138c7f
SBS
756 # yell "DEBUG:STATUS:$FN:CMD1:$CMD1"
757 # yell "DEBUG:STATUS:$FN:CMD2:$CMD2"
758 # yell "DEBUG:STATUS:$FN:CMD3:$CMD3"
759 # yell "DEBUG:STATUS:$FN:CMD4:$CMD4"
760 # yell "DEBUG:STATUS:$FN:FILENAME:$FILENAME"
761 # yell "DEBUG:STATUS:$FN:TAR_PATH:$TAR_PATH"
762 # yell "DEBUG:STATUS:$FN:TMP_DIR:$TMP_DIR"
763
66af6225 764 # Write to temporary working dir
f665ce95 765 eval "$CMD0"" | ""$CMD1"" | ""$CMD2"" | ""$CMD3"" | ""$CMD4" > "$TMP_DIR"/"$FILENAME";
66af6225
SBS
766
767 # Append to tar
768 try tar --append --directory="$TMP_DIR" --file="$TAR_PATH" "$FILENAME";
d7138c7f 769 #yell "DEBUG:STATUS:$FN:Finished appendArgTar()."
66af6225 770} # Append Bash var to file appended to Tar archive
e47e8048
SBS
771appendFileTar(){
772 # Desc: Processes first file and then appends to tar
773 # Usage: appendFileTar [file path] [name of file to be inserted] [tar path] [temp dir] ([cmd1] [cmd2] [cmd3] [cmd4]...)
f665ce95 774 # Version: 1.0.2
e47e8048
SBS
775 # Input: arg1: path of file to be (processed and) written
776 # arg2: name to use for file inserted into tar
777 # arg3: tar archive path (must exist first)
778 # arg4: temporary working dir
779 # arg5+: command strings (ex: "gpsbabel -i nmea -f - -o kml -F - ")
780 # Output: file written to disk
781 # Example: decrypt multiple large files in parallel
782 # appendFileTar /tmp/largefile1.gpg "largefile1" $HOME/archive.tar /tmp "gpg --decrypt" &
783 # appendFileTar /tmp/largefile2.gpg "largefile2" $HOME/archive.tar /tmp "gpg --decrypt" &
784 # appendFileTar /tmp/largefile3.gpg "largefile3" $HOME/archive.tar /tmp "gpg --decrypt" &
785 # Depends: bash 5
786
787 # Save function name
788 local FN="${FUNCNAME[0]}";
789 #yell "DEBUG:STATUS:$FN:Finished appendFileTar()."
790
791 # Set file name
792 if ! [ -z "$2" ]; then FILENAME="$2"; else yell "ERROR:$FN:Not enough arguments."; exit 1; fi
793 # Check tar path is a file
794 if [ -f "$3" ]; then TAR_PATH="$3"; else yell "ERROR:$FN:Tar archive arg not a file."; exit 1; fi
795 # Check temp dir arg
796 if ! [ -z "$4" ]; then TMP_DIR="$4"; else yell "ERROR:$FN:No temporary working dir set."; exit 1; fi
797 # Set command strings
798 if ! [ -z "$5" ]; then CMD1="$5"; else CMD1="tee /dev/null "; fi # command string 1
799 if ! [ -z "$6" ]; then CMD2="$6"; else CMD2="tee /dev/null "; fi # command string 2
800 if ! [ -z "$7" ]; then CMD3="$7"; else CMD3="tee /dev/null "; fi # command string 3
801 if ! [ -z "$8" ]; then CMD4="$8"; else CMD4="tee /dev/null "; fi # command string 4
f665ce95
SBS
802
803 # Input command string
804 CMD0="cat \"\$1\""
805
e47e8048 806 # # Debug
f665ce95 807 # yell "DEBUG:STATUS:$FN:CMD0:$CMD0"
e47e8048
SBS
808 # yell "DEBUG:STATUS:$FN:CMD1:$CMD1"
809 # yell "DEBUG:STATUS:$FN:CMD2:$CMD2"
810 # yell "DEBUG:STATUS:$FN:CMD3:$CMD3"
811 # yell "DEBUG:STATUS:$FN:CMD4:$CMD4"
812 # yell "DEBUG:STATUS:$FN:FILENAME:$FILENAME"
813 # yell "DEBUG:STATUS:$FN:TAR_PATH:$TAR_PATH"
814 # yell "DEBUG:STATUS:$FN:TMP_DIR:$TMP_DIR"
815
816 # Write to temporary working dir
f665ce95 817 eval "$CMD0 | $CMD1 | $CMD2 | $CMD3 | $CMD4" > "$TMP_DIR"/"$FILENAME";
e47e8048
SBS
818
819 # Append to tar
820 try tar --append --directory="$TMP_DIR" --file="$TAR_PATH" "$FILENAME";
821 #yell "DEBUG:STATUS:$FN:Finished appendFileTar()."
822} # Append file to Tar archive
320ac29c
SBS
823checkAgePubkey() {
824 # Desc: Checks if string is an age-compatible pubkey
825 # Usage: checkAgePubkey [str pubkey]
826 # Version: 0.1.2
827 # Input: arg1: string
828 # Output: return code 0: string is age-compatible pubkey
829 # return code 1: string is NOT an age-compatible pubkey
830 # age stderr (ex: there is stderr if invalid string provided)
831 # Depends: age (v0.1.0-beta2; https://github.com/FiloSottile/age/releases/tag/v1.0.0-beta2 )
832
833 argPubkey="$1";
834
835 if echo "test" | age -a -r "$argPubkey" 1>/dev/null; then
836 return 0;
837 else
838 return 1;
839 fi;
840} # Check age pubkey
d6ba4173
SBS
841validateInput() {
842 # Desc: Validates Input
843 # Usage: validateInput [str input] [str input type]
2af0d7d9 844 # Version: 0.3.0
d6ba4173
SBS
845 # Input: arg1: string to validate
846 # arg2: string specifying input type (ex:"ssh_pubkey")
847 # Output: return code 0: if input string matched specified string type
848 # Depends: bash 5, yell
849
850 # Save function name
851 local FN="${FUNCNAME[0]}";
852
853 # Process arguments
854 argInput="$1";
855 argType="$2";
856 if [[ $# -gt 2 ]]; then yell "ERROR:$0:$FN:Too many arguments."; exit 1; fi;
857
858 # Check for blank
859 if [[ -z "$argInput" ]]; then return 1; fi
860
861 # Define input types
862 ## ssh_pubkey
863 ### Check for alnum/dash base64 (ex: "ssh-rsa AAAAB3NzaC1yc2EAAA")
864 if [[ "$argType" = "ssh_pubkey" ]]; then
865 if [[ "$argInput" =~ ^[[:alnum:]-]*[\ ]*[[:alnum:]+/=]*$ ]]; then
2af0d7d9 866 return 0; fi; fi;
d6ba4173
SBS
867
868 ## age_pubkey
869 ### Check for age1[:bech32:]
870 if [[ "$argType" = "age_pubkey" ]]; then
871 if [[ "$argInput" =~ ^age1[qpzry9x8gf2tvdw0s3jn54khce6mua7l]*$ ]]; then
872 return 0; fi; fi
873
2af0d7d9
SBS
874 ## integer
875 if [[ "$argType" = "integer" ]]; then
876 if [[ "$argInput" =~ ^[[:digit:]]*$ ]]; then
877 return 0; fi; fi;
878
879 ## time element (year, month, week, day, hour, minute, second)
880 if [[ "$argType" = "time_element" ]]; then
881 if [[ "$argInput" = "year" ]] || \
882 [[ "$argInput" = "month" ]] || \
883 [[ "$argInput" = "week" ]] || \
884 [[ "$argInput" = "day" ]] || \
885 [[ "$argInput" = "hour" ]] || \
886 [[ "$argInput" = "minute" ]] || \
887 [[ "$argInput" = "second" ]]; then
888 return 0; fi; fi;
889
d6ba4173
SBS
890 # Return error if no condition matched.
891 return 1;
892} # Validates strings
52f05d07
SBS
893timeEpochNS() {
894 # Desc: Get epoch nanoseconds
895 # Usage: timeEpochNS
896 # Version 0.2.2
897 # Input: arg1: 'date'-parsable timestamp string (optional)
898 # Output: Nanoseconds since 1970-01-01
899 # Depends: date 8, yell()
900 # Ref/Attrib: Force base 10 Bash arith with '10#'. https://stackoverflow.com/a/24777667
901 local TIME_CURRENT TIME_INPUT TIME_EPOCH_FLOAT TIME_EPOCH_NSFRAC
902 local TIME_EPOCH_NS
903
904 argTime="$1";
905
906 # Get Current Time
907 TIME_CURRENT="$(date --iso-8601=ns)"; # Produce `date`-parsable current timestamp with resolution of 1 nanosecond.
908
909 # Decide to parse current or supplied time
910 ## Check if time argument empty
911 if [[ -z "$argTime" ]]; then
912 ## T: Time argument empty, use current time
913 TIME_INPUT="$TIME_CURRENT";
914 else
915 ## F: Time argument exists, validate time
916 if date --date="$argTime" 1>/dev/null 2>&1; then
917 ### T: Time argument is valid; use it
918 TIME_INPUT="$argTime";
919 else
920 ### F: Time argument not valid; exit
921 yell "ERROR:Invalid time argument supplied. Exiting."; exit 1;
922 fi;
923 fi;
924 # Construct and deliver nanoseconds since 1970-01-01
925 TIME_EPOCH_FLOAT="$(date --date="$TIME_INPUT" +%s.%N)"; # Save ssss.NNNNNNNNN
926 TIME_EPOCH_INT="$(echo "$TIME_EPOCH_FLOAT" | cut -d. -f1)"; # Get ssss
927 TIME_EPOCH_NSFRAC="$(echo "$TIME_EPOCH_FLOAT" | cut -d. -f2)"; # Get NNNNNNNNN
928 TIME_EPOCH_NS="$(( (10#"$TIME_EPOCH_INT" * 10**9) + (10#"$TIME_EPOCH_NSFRAC") ))";
929 echo "$TIME_EPOCH_NS";
930} # Nanoseconds since 1970-01-01
931magicBufferSleepPID() {
932 # Desc: Compensates for lag so buffer rounds start every BUFFER_TTL seconds
933 # Input: vars: BUFFER_TTL, errResetx10e3, K_P, T_I, T_D
934 # # Input: array: errorHistory
935 # Output: vars: BUFFER_TTL_ADJ_FLOAT
936 # Re/Attrib: https://en.wikipedia.org/wiki/PID_controller#Standard_versus_parallel_(ideal)_form
937 local BUFFER_TTL_NS
938 local timeBufferStartNS timeBufferStartNSExp errNS errNSx10e3
939 local errResetx10e3 errRatex10e3 ADJ BUFFER_TTL_ADJ_NS BUFFER_TTL_ADJ_INT
940 local BUFFER_TTL_ADJ_FLOATFRAC
941 # local errorHistorySize
942
943 # ## Define errorHistorySize
944 # errorHistorySize=100;
945 ## Define BUFFER_TTL in nanoseconds
d59a052d 946 BUFFER_TTL_NS=$((BUFFER_TTL * 10**9)) && vbm "BUFFER_TTL_NS:$BUFFER_TTL_NS";
3370febe
SBS
947
948 ### PID Control factors
949 K_P=1; # Gain for compensating buffer round lag
42c1f429
SBS
950 T_I="$(((4)*BUFFER_TTL_NS/(1)))"; # Consider this number of past nanoseconds to eliminate error
951 T_D="$(((1)*BUFFER_TTL_NS/(1)))"; # Predict value this number of nanoseconds into the future
52f05d07
SBS
952
953 # Calculate Error, errNS, in nanoseconds
954 ## Get current time
7970872b 955 timeBufferStartNS="$(timeEpochNS)" && vbm "timeBufferStartNS :$timeBufferStartNS";
52f05d07 956 ## Calculate expected time (from start time, current buffer round number, nominal BUFFER_TTL)
d59a052d 957 timeBufferStartNSExp="$(( (timeBufferFirstNS) + (BUFFER_TTL_NS * bufferRound) ))" && vbm "timeBufferStartNSExp:$timeBufferStartNSExp";
52f05d07 958 ## Calculate error (diff between timeBufferStartNSExp and timeBufferStartNS; usually negative)
d59a052d 959 errNS="$(( timeBufferStartNSExp - timeBufferStartNS ))" && vbm "errNS:$errNS";
7970872b 960# errNSx10e3="$((errNS*10**3))" && vbm "errNSx10e3:$errNSx10e3";
52f05d07
SBS
961 # ## Append error to errorHistory
962 # errorHistory+=("errNS");
963 # ### Trim errorHistory array if over errorHistorySize
964 # while [[ "${#errorHistory[@]}" -gt "errorHistorySize" ]]; then do
965 # unset "errorHistory[0]"; # remove oldest entry, creating sparse array
966 # errorHistory=("${errorHistory[@]}"); # reindex sparse array
967 # vbm "STATUS:Trimmed errorHistory array. Entry count:${#errorHistory[@]}";
968 # done;
969
970 # Calculate errReset in nanoseconds^2
971 ## errReset = int(errHistory(t),wrt(delta_BUFFER_TTL))
972 ## Integrate errorHistory with respect to time
973 # for value in "${errorHistory[@]}"; do
974 # errReset=$(( errReset + ( value*BUFFER_TTL_NS ) ));
975 # done;
c8ebb71c
SBS
976 vbm "errReset(orig):$errReset"
977 errReset="$(( (errReset + (errNS*BUFFER_TTL_NS)) ))" && vbm "errReset(post):$errReset";
7970872b 978# errResetx10e3="$(( ( errResetx10e3 + ( errNSx10e3 * BUFFER_TTL_NS ) )*10**3 ))" && vbm "errResetx10e3:$errResetx10e3";
52f05d07 979
7970872b 980 # Calculate errRate in nanoseconds per nanosecond
3acde8db 981 errRate="$(( errNS / BUFFER_TTL_NS ))" && vbm "errRate:$errRate";
7970872b 982# errRatex10e3="$(( ( errNSx10e3 ) / BUFFER_TTL_NS ))" && vbm "errRatex10e3:$errRatex10e3";
42c1f429
SBS
983
984 # Debug
985 vbm "errNS :$errNS";
986 vbm "errResetTerm:$((errReset/T_I))";
987 vbm "errRateTerm :$((errRate*T_D))";
52f05d07
SBS
988
989 # Calculate PID control signal
990 ## ADJ = K_P * (errNS + errReset/T_I + errRate*T_D)
7970872b
SBS
991 ADJ="$(( K_P*(errNS + errReset/T_I + errRate*T_D) ))" && vbm "ADJ:$ADJ";
992# ADJ="$((K_P*(errNSx10e3 + (errResetx10e3/T_I) + (errRatex10e3*T_D) )/(10**3)))" && vbm "ADJ:$ADJ";
52f05d07
SBS
993
994 # Calculate BUFFER_TTL_ADJ_FLOAT from ADJ (ns)
995 ## Calculate BUFFER_TTL_ADJ in nanoseconds (BUFFER_TTL_ADJ_NS = BUFFER_TTL_NS + ADJ)
d59a052d 996 BUFFER_TTL_ADJ_NS="$((BUFFER_TTL_NS + ADJ))" && vbm "BUFFER_TTL_ADJ_NS:$BUFFER_TTL_ADJ_NS";
52f05d07 997 ## Calculate integer seconds
d59a052d 998 BUFFER_TTL_ADJ_INT="$((BUFFER_TTL_ADJ_NS/(10**9)))" && vbm "BUFFER_TTL_ADJ_INT:$BUFFER_TTL_ADJ_INT";
c2fcb467
SBS
999 ### Catch negative integer seconds, set minimum of BUFFER_TTL/10 seconds
1000 if [[ "$BUFFER_TTL_ADJ_INT" -le "$((BUFFER_TTL/10))" ]]; then
1001 BUFFER_TTL_ADJ_INT="$((BUFFER_TTL/10))";
1002 yell "WARNING:Buffer lag adjustment yielded negative seconds.";
1003 fi;
52f05d07 1004 ## Calculate nanosecond remainder
1d2b85aa 1005 ### Remove integer
c503eb7b 1006 BUFFER_TTL_ADJ_FLOATFRAC="$((BUFFER_TTL_ADJ_NS - (BUFFER_TTL_ADJ_INT*(10**9)) ))" && vbm "BUFFER_TTL_ADJ_FLOATFRAC:$BUFFER_TTL_ADJ_FLOATFRAC";
1d2b85aa 1007 ### Calc absolute value of fraction (by removing '-' if present; see https://stackoverflow.com/a/47240327
8b52ffca 1008 BUFFER_TTL_ADJ_FLOATFRAC="${BUFFER_TTL_ADJ_FLOATFRAC#-}" && vbm "BUFFER_TTL_ADJ_FLOATFRAC:$BUFFER_TTL_ADJ_FLOATFRAC";
52f05d07 1009 ## Form float BUFFER_TTL_ADJ_FLOAT
d59a052d 1010 BUFFER_TTL_ADJ_FLOAT="$BUFFER_TTL_ADJ_INT"."$BUFFER_TTL_ADJ_FLOATFRAC" && vbm "BUFFER_TTL_ADJ_FLOAT:$BUFFER_TTL_ADJ_FLOAT";
52f05d07
SBS
1011 vbm "STATUS:Calculated adjusted BUFFER_TTL (seconds):$BUFFER_TTL_ADJ_FLOAT";
1012} # Calc BUFFER_TTL_ADJ_FLOAT so buffer starts every BUFFER_TTL seconds
3395ae22
SBS
1013magicWriteVersion() {
1014 # Desc: Appends time-stamped VERSION to PATHOUT_TAR
1015 # Usage: magicWriteVersion
ff22a93f 1016 # Version: 0.1.0
3395ae22 1017 # Input: CONTENT_VERSION, FILEOUT_VERSION, PATHOUT_TAR, DIR_TMP
ff22a93f
SBS
1018 # Input: SCRIPT_VERSION, SCRIPT_URL, AGE_VERSION, AGE_URL, SCRIPT_HOSTNAME
1019 # Output: appends tar PATHOUT_TAR
3395ae22 1020 # Depends: dateTimeShort, appendArgTar
ff22a93f 1021 local CONTENT_VERSION pubKeyIndex
3395ae22 1022
ff22a93f 1023 # Set VERSION file name
3395ae22 1024 FILEOUT_VERSION="$(dateTimeShort)..VERSION";
ff22a93f
SBS
1025
1026 # Gather VERSION data in CONTENT_VERSION
1027 CONTENT_VERSION="SCRIPT_VERSION=$SCRIPT_VERSION";
aa2d7921
SBS
1028 #CONTENT_VERSION="$CONTENT_VERSION""\\n";
1029 CONTENT_VERSION="$CONTENT_VERSION""\\n""SCRIPT_NAME=$SCRIPT_NAME";
1030 CONTENT_VERSION="$CONTENT_VERSION""\\n""SCRIPT_URL=$SCRIPT_URL";
1031 CONTENT_VERSION="$CONTENT_VERSION""\\n""AGE_VERSION=$AGE_VERSION";
1032 CONTENT_VERSION="$CONTENT_VERSION""\\n""AGE_URL=$AGE_URL";
1033 CONTENT_VERSION="$CONTENT_VERSION""\\n""DATE=$(date --iso-8601=seconds)";
1034 CONTENT_VERSION="$CONTENT_VERSION""\\n""HOSTNAME=$SCRIPT_HOSTNAME";
ff22a93f
SBS
1035 ## Add list of recipient pubkeys
1036 for pubkey in "${recPubKeysValid[@]}"; do
1037 ((pubKeyIndex++))
aa2d7921 1038 CONTENT_VERSION="$CONTENT_VERSION""\\n""PUBKEY_$pubKeyIndex=$pubkey";
ff22a93f
SBS
1039 done
1040 ## Process newline escapes
4c0c5850
SBS
1041 CONTENT_VERSION="$(echo -e "$CONTENT_VERSION")"
1042
ff22a93f 1043 # Write CONTENT_VERSION as file FILEOUT_VERSION and write-append to PATHOUT_TAR
3395ae22 1044 appendArgTar "$CONTENT_VERSION" "$FILEOUT_VERSION" "$PATHOUT_TAR" "$DIR_TMP";
ff22a93f 1045
3395ae22 1046} # bkgpslog: write version data to PATHOUT_TAR via appendArgTar()
811f50f2 1047magicGatherWriteBuffer() {
66af6225 1048 # Desc: bkgpslog-specific meta function for writing data to DIR_TMP then appending each file to PATHOUT_TAR
52f05d07
SBS
1049 # Inputs: vars: PATHOUT_TAR FILEOUT_{NMEA,GPX,KML} CMD_CONV_{NMEA,GPX,KML} CMD_{COMPRESS,ENCRYPT} DIR_TMP,
1050 # Inputs: vars: BUFFER_TTL bufferTTL_STR SCRIPT_HOSTNAME CMD_COMPRESS_SUFFIX CMD_ENCRYPT_SUFFIX
1051 # Output: file: (PATHOUT_TAR)
1052 # Depends: yell(), try(), vbm(), appendArgTar(), tar 1, sleep 8, checkMakeTar()
1053 # Depends: magicWriteVersion(), appendFileTar()
1054 local FN
1055
8f51861e 1056 vbm "DEBUG:STATUS:$FN:Started magicGatherWriteBuffer().";
52f05d07
SBS
1057 # Debug:Get function name
1058 FN="${FUNCNAME[0]}";
1059
811f50f2 1060 # Create buffer file with unique name
7df2db7e 1061 PATHOUT_BUFFER="$DIR_TMP/buffer$SECONDS" && vbm "PATHOUT_BUFFER:$PATHOUT_BUFFER";
811f50f2
SBS
1062 # Fill buffer
1063 timeout "$BUFFER_TTL"s gpspipe -r -o "$PATHOUT_BUFFER" ;
8659e486 1064 timeBufferStartLong="$(date --date="$BUFFER_TTL seconds ago" --iso-8601=seconds)" && vbm "timeBufferStartLong:$timeBufferStartLong" || yell "ERROR:timeBufferStartLong fail";
cf85eedd 1065 timeBufferStart="$(dateTimeShort "$timeBufferStartLong" )" && vbm "timeBufferStart:$timeBufferStart" || yell "ERROR:timeBufferStart fail"; # Note start time
e47e8048
SBS
1066 # Determine file paths (time is start of buffer period)
1067 FILEOUT_BASENAME="$timeBufferStart""--""$bufferTTL_STR""..""$SCRIPT_HOSTNAME""_location" && vbm "STATUS:Set FILEOUT_BASENAME to:$FILEOUT_BASENAME";
1068 ## Files saved to DIR_TMP
1069 FILEOUT_NMEA="$FILEOUT_BASENAME".nmea"$CMD_COMPRESS_SUFFIX""$CMD_ENCRYPT_SUFFIX" && vbm "STATUS:Set FILEOUT_NMEA to:$FILEOUT_NMEA";
1070 FILEOUT_GPX="$FILEOUT_BASENAME".gpx"$CMD_COMPRESS_SUFFIX""$CMD_ENCRYPT_SUFFIX" && vbm "STATUS:Set FILEOUT_GPX to:$FILEOUT_GPX";
1071 FILEOUT_KML="$FILEOUT_BASENAME".kml"$CMD_COMPRESS_SUFFIX""$CMD_ENCRYPT_SUFFIX" && vbm "STATUS:Set FILEOUT_KML to:$FILEOUT_KML";
1072 PATHOUT_NMEA="$DIR_TMP"/"$FILEOUT_NMEA" && vbm "STATUS:Set PATHOUT_NMEA to:$PATHOUT_NMEA";
1073 PATHOUT_GPX="$DIR_TMP"/"$FILEOUT_GPX" && vbm "STATUS:Set PATHOUT_GPX to:$PATHOUT_GPX";
1074 PATHOUT_KML="$DIR_TMP"/"$FILEOUT_KML" && vbm "STATUS:Set PATHOUT_KML to:$PATHOUT_KML";
1075 ## Files saved to disk (DIR_OUT)
1076 ### one file per day (Ex: "20200731..hostname_location.[.gpx.gz].tar")
e56d6109 1077 PATHOUT_TAR="$DIR_OUT"/"$(dateShort "$(date --date="$BUFFER_TTL seconds ago" --iso-8601=seconds)")".."$SCRIPT_HOSTNAME""_location""$CMD_COMPRESS_SUFFIX""$CMD_ENCRYPT_SUFFIX".tar && \
e47e8048
SBS
1078 vbm "STATUS:Set PATHOUT_TAR to:$PATHOUT_TAR";
1079 # DEBUG: check vars
cf85eedd
SBS
1080 vbm "STATUS:FN :$FN";
1081 vbm "STATUS:DIR_TMP :$DIR_TMP";
1082 vbm "STATUS:PATHOUT_TAR :$PATHOUT_TAR";
1083 vbm "STATUS:PATHOUT_NMEA :$PATHOUT_NMEA";
1084 vbm "STATUS:PATHOUT_GPX :$PATHOUT_GPX";
1085 vbm "STATUS:PATHOUT_KML :$PATHOUT_KML";
1086 vbm "STATUS:BUFFER_TTL :$BUFFER_TTL";
1087 vbm "STATUS:PATHOUT_BUFFER :$PATHOUT_BUFFER";
1088 vbm "STATUS:timeBufferStart:$timeBufferStart";
1089 vbm "FILEOUT_BASENAME :$FILEOUT_BASENAME";
e47e8048
SBS
1090
1091
3395ae22
SBS
1092 # Validate PATHOUT_TAR as tar.
1093 checkMakeTar "$PATHOUT_TAR";
1094 ## Add VERSION file if checkMakeTar had to create a tar (exited 1) or replace one (exited 2)
cf85eedd 1095 vbm "exit status before magicWriteVersion:$?"
3395ae22 1096 if [[ $? -eq 1 ]] || [[ $? -eq 2 ]]; then magicWriteVersion; fi
f6fb18bd
SBS
1097
1098 # Write bufferBash to PATHOUT_TAR
52f05d07 1099 wait; # Wait to avoid collision with older magicWriteBuffer() instances (see https://www.tldp.org/LDP/abs/html/x9644.html )
e47e8048
SBS
1100 appendFileTar "$PATHOUT_BUFFER" "$FILEOUT_NMEA" "$PATHOUT_TAR" "$DIR_TMP" "$CMD_CONV_NMEA" "$CMD_COMPRESS" "$CMD_ENCRYPT"; # Write NMEA data
1101 appendFileTar "$PATHOUT_BUFFER" "$FILEOUT_GPX" "$PATHOUT_TAR" "$DIR_TMP" "$CMD_CONV_GPX" "$CMD_COMPRESS" "$CMD_ENCRYPT"; # Write GPX file
1102 appendFileTar "$PATHOUT_BUFFER" "$FILEOUT_KML" "$PATHOUT_TAR" "$DIR_TMP" "$CMD_CONV_KML" "$CMD_COMPRESS" "$CMD_ENCRYPT"; # Write KML file
1103
e0452100 1104 # Remove secured chunks from DIR_TMP
e47e8048 1105 rm "$PATHOUT_BUFFER" "$PATHOUT_NMEA" "$PATHOUT_GPX" "$PATHOUT_KML";
8f51861e 1106 vbm "DEBUG:STATUS:$FN:Finished magicGatherWriteBuffer().";
e47e8048 1107} # write buffer to disk
320ac29c
SBS
1108magicParseRecipientDir() {
1109 # Desc: Updates recPubKeysValid with pubkeys in dir specified by '-R' option ("recipient directory")
2038018c 1110 # Inputs: vars: OPTION_RECDIR, argRecDir, OPTION_ENCRYPT
50324e94
SBS
1111 # arry: recPubKeysValid
1112 # Outputs: arry: recPubKeysValid
320ac29c
SBS
1113 # Depends: processArguments,
1114 local recFileLine updateRecipients recipientDir
1115 declare -a candRecPubKeysValid
50324e94
SBS
1116
1117 # Check that '-e' and '-R' set
2038018c 1118 if [[ "$OPTION_ENCRYPT" = "true" ]] && [[ "$OPTION_RECDIR" = "true" ]]; then
320ac29c
SBS
1119 ### Check that argRecDir is a directory.
1120 if [[ -d "$argRecDir" ]]; then
fd5ef60e 1121 recipientDir="$argRecDir" && vbm "STATUS:Recipient watch directory detected:\"$recipientDir\"";
320ac29c
SBS
1122 #### Initialize variable indicating outcome of pubkey review
1123 unset updateRecipients
1124 #### Add existing recipients
6010b172 1125 candRecPubKeysValid=("${recPubKeysValidStatic[@]}");
320ac29c
SBS
1126 #### Parse files in recipientDir
1127 for file in "$recipientDir"/*; do
1128 ##### Read first line of each file
fd5ef60e 1129 recFileLine="$(head -n1 "$file")" && vbm "STATUS:Checking if pubkey:\"$recFileLine\"";
320ac29c
SBS
1130 ##### check if first line is a valid pubkey
1131 if checkAgePubkey "$recFileLine" && \
1132 ( validateInput "$recFileLine" "ssh_pubkey" || validateInput "$recFileLine" "age_pubkey"); then
1133 ###### T: add candidate pubkey to candRecPubKeysValid
fd5ef60e 1134 candRecPubKeysValid+=("$recFileLine") && vbm "STATUS:RecDir pubkey is valid pubkey:\"$recFileLine\"";
320ac29c
SBS
1135 else
1136 ###### F: throw warning;
1137 yell "ERROR:Invalid recipient file detected. Not modifying recipient list."
1138 updateRecipients="false";
1139 fi;
1140 done
1141 #### Write updated recPubKeysValid array to recPubKeysValid if no failure detected
2af0d7d9 1142 if ! [[ "$updateRecipients" = "false" ]]; then
52f05d07 1143 recPubKeysValid=("${candRecPubKeysValid[@]}") && vbm "STATUS:Wrote candRecPubkeysValid to recPubKeysValid:\"${recPubKeysValid[*]}\"";
320ac29c 1144 fi;
cfc25c90 1145 else
320ac29c
SBS
1146 yell "ERROR:$0:Recipient directory $argRecDir does not exist. Exiting."; exit 1;
1147 fi;
1148 fi;
50324e94 1149 # Handle case if '-R' set but '-e' not set
2038018c 1150 if [[ ! "$OPTION_ENCRYPT" = "true" ]] && [[ "$OPTION_RECDIR" = "true" ]]; then
2af0d7d9 1151 yell "ERROR: \\'-R\\' is set but \\'-e\\' is not set."; fi;
320ac29c
SBS
1152} # Update recPubKeysValid with argRecDir
1153magicParseRecipientArgs() {
1154 # Desc: Parses recipient arguments specified by '-r' option
1155 # Input: vars: OPTION_ENCRYPT from processArguments()
1156 # arry: argRecPubKeys from processArguments()
1157 # Output: vars: CMD_ENCRYPT, CMD_ENCRYPT_SUFFIX
6010b172 1158 # arry: recPubKeysValid, recPubKeysValidStatic
320ac29c
SBS
1159 # Depends: checkapp(), checkAgePubkey(), validateInput(), processArguments()
1160 local recipients
cfc25c90 1161
6a2cd888
SBS
1162 # Check if encryption option active.
1163 if [[ "$OPTION_ENCRYPT" = "true" ]] && [[ "$OPTION_RECIPIENTS" = "true" ]]; then
17d49005 1164 if checkapp age; then # Check that age is available.
320ac29c 1165 for pubkey in "${argRecPubKeys[@]}"; do # Validate recipient pubkey strings by forming test message
238775e6 1166 vbm "DEBUG:Testing pubkey string:$pubkey";
320ac29c 1167 if checkAgePubkey "$pubkey" && \
d6ba4173 1168 ( validateInput "$pubkey" "ssh_pubkey" || validateInput "$pubkey" "age_pubkey"); then
ff22a93f 1169 #### Form age recipient string
be9ef23e 1170 recipients="$recipients""-r '$pubkey' ";
405ce7df 1171 vbm "STATUS:Added pubkey for forming age recipient string:""$pubkey";
77361307 1172 vbm "DEBUG:recipients:""$recipients";
ff22a93f
SBS
1173 #### Add validated pubkey to recPubKeysValid array
1174 recPubKeysValid+=("$pubkey") && vbm "DEBUG:recPubkeysValid:pubkey added:$pubkey";
408a342b
SBS
1175 else
1176 yell "ERROR:Exit code ""$?"". Invalid recipient pubkey string. Exiting."; exit 1;
6a2cd888 1177 fi;
17d49005 1178 done
320ac29c 1179 vbm "DEBUG:Finished processing argRecPubKeys array";
52f05d07
SBS
1180 vbm "STATUS:Array of validated pubkeys:${recPubKeysValid[*]}";
1181 recPubKeysValidStatic=("${recPubKeysValid[@]}"); # Save static image of pubkeys validated by this function
ff22a93f
SBS
1182
1183 ## Form age command string
be9ef23e
SBS
1184 CMD_ENCRYPT="age ""$recipients " && vbm "CMD_ENCRYPT:$CMD_ENCRYPT";
1185 CMD_ENCRYPT_SUFFIX=".age" && vbm "CMD_ENCRYPT_SUFFIX:$CMD_ENCRYPT_SUFFIX";
17d49005
SBS
1186 else
1187 yell "ERROR:Encryption enabled but \"age\" not found. Exiting."; exit 1;
6a2cd888 1188 fi;
408a342b 1189 else
be9ef23e
SBS
1190 CMD_ENCRYPT="tee /dev/null " && vbm "CMD_ENCRYPT:$CMD_ENCRYPT";
1191 CMD_ENCRYPT_SUFFIX="" && vbm "CMD_ENCRYPT_SUFFIX:$CMD_ENCRYPT_SUFFIX";
408a342b 1192 vbm "DEBUG:Encryption not enabled."
6a2cd888
SBS
1193 fi;
1194 # Catch case if '-e' is set but '-r' or '-R' is not
1195 if [[ "$OPTION_ENCRYPT" = "true" ]] && [[ ! "$OPTION_RECIPIENTS" = "true" ]]; then
2af0d7d9 1196 yell "ERROR:\\'-e\\' set but no \\'-r\\' or \\'-R\\' set."; exit 1; fi;
6a2cd888
SBS
1197 # Catch case if '-r' or '-R' set but '-e' is not
1198 if [[ ! "$OPTION_ENCRYPT" = "true" ]] && [[ "$OPTION_RECIPIENTS" = "true" ]]; then
2af0d7d9 1199 yell "ERROR:\\'-r\\' or \\'-R\\' set but \\'-e\\' is not set."; exit 1; fi;
320ac29c
SBS
1200} # Populate recPubKeysValid with argRecPubKeys; form encryption cmd string and filename suffix
1201magicParseCompressionArg() {
1202 # Desc: Parses compression arguments specified by '-c' option
1203 # Input: vars: OPTION_COMPRESS
1204 # Output: CMD_COMPRESS, CMD_COMPRESS_SUFFIX
1205 # Depends: checkapp(), vbm(), gzip,
408a342b
SBS
1206 if [[ "$OPTION_COMPRESS" = "true" ]]; then # Check if compression option active
1207 if checkapp gzip; then # Check if gzip available
be9ef23e
SBS
1208 CMD_COMPRESS="gzip " && vbm "CMD_COMPRESS:$CMD_COMPRESS";
1209 CMD_COMPRESS_SUFFIX=".gz" && vbm "CMD_COMPRESS_SUFFIX:$CMD_COMPRESS_SUFFIX";
408a342b
SBS
1210 else
1211 yell "ERROR:Compression enabled but \"gzip\" not found. Exiting."; exit 1;
286d9825 1212 fi
408a342b 1213 else
be9ef23e
SBS
1214 CMD_COMPRESS="tee /dev/null " && vbm "CMD_COMPRESS:$CMD_COMPRESS";
1215 CMD_COMPRESS_SUFFIX="" && vbm "CMD_COMPRESS_SUFFIX:$CMD_COMPRESS_SUFFIX";
1b0e6227 1216 vbm "DEBUG:Compression not enabled.";
320ac29c
SBS
1217 fi
1218} # Form compression cmd string and filename suffix
1219magicInitWorkingDir() {
1220 # Desc: Determine temporary working directory from defaults or user input
c9c573da
SBS
1221 # Usage: magicInitWorkignDir
1222 # Input: vars: OPTION_TEMPDIR, argTempDirPriority, DIR_TMP_DEFAULT
320ac29c
SBS
1223 # Input: vars: SCRIPT_TIME_START
1224 # Output: vars: DIR_TMP
1225 # Depends: processArguments(), vbm(), yell()
1226 # Parse '-t' option (user-specified temporary working dir)
1227 ## Set DIR_TMP_PARENT to user-specified value if specified
1228 local DIR_TMP_PARENT
1229
1230 if [[ "$OPTION_TMPDIR" = "true" ]]; then
1231 if [[ -d "$argTempDirPriority" ]]; then
1232 DIR_TMP_PARENT="$argTempDirPriority";
1233 else
c9c573da 1234 yell "WARNING:Specified temporary working directory not valid:$argTempDirPriority";
320ac29c
SBS
1235 exit 1; # Exit since user requires a specific temp dir and it is not available.
1236 fi;
1237 else
1238 ## Set DIR_TMP_PARENT to default or fallback otherwise
1239 if [[ -d "$DIR_TMP_DEFAULT" ]]; then
1240 DIR_TMP_PARENT="$DIR_TMP_DEFAULT";
1241 elif [[ -d /tmp ]]; then
1242 yell "WARNING:$DIR_TMP_DEFAULT not available. Falling back to /tmp .";
1243 DIR_TMP_PARENT="/tmp";
1244 else
1245 yell "ERROR:No valid working directory available. Exiting.";
1246 exit 1;
1247 fi
1248 fi;
1249 ## Set DIR_TMP using DIR_TMP_PARENT and nonce (SCRIPT_TIME_START)
1250 DIR_TMP="$DIR_TMP_PARENT"/"$SCRIPT_TIME_START""..bkgpslog" && vbm "DEBUG:Set DIR_TMP to:$DIR_TMP"; # Note: removed at end of main().
1251} # Sets working dir
2af0d7d9
SBS
1252magicParseCustomTTL() {
1253 # Desc: Set user-specified TTLs for buffer and script
1254 # Input: vars: argCustomBufferTTL (integer), argCustomScriptTTL_TE (string)
1255 # Input: vars: OPTION_CUSTOM_BUFFERTTL, OPTION_CUSTOM_SCRIPTTTL
1256 # Input: vars: BUFFER_TTL (integer), SCRIPT_TTL_TE (string)
1257 # Output: BUFFER_TTL (integer), SCRIPT_TTL_TE (string)
1258 # Depends validateInput(), showUsage(), yell
1259
1260 # React to '-b, --buffer-ttl' option
1261 if [[ "$OPTION_CUSTOM_BUFFERTTL" = "true" ]]; then
1262 ## T: Check if argCustomBufferTTL is an integer
1263 if validateInput "$argCustomBufferTTL" "integer"; then
1264 ### T: argCustomBufferTTL is an integer
7df2db7e 1265 BUFFER_TTL="$argCustomBufferTTL" && vbm "Custom BUFFER_TTL from -b:$BUFFER_TTL";
2af0d7d9
SBS
1266 else
1267 ### F: argcustomBufferTTL is not an integer
1268 yell "ERROR:Invalid integer argument for custom buffer time-to-live."; showUsage; exit 1;
1269 fi;
1270 ## F: do not change BUFFER_TTL
1271 fi;
1272
1273 # React to '-B, --script-ttl' option
1274 if [[ "$OPTION_CUSTOM_SCRIPTTTL_TE" = "true" ]]; then
1275 ## T: Check if argCustomScriptTTL is a time element (ex: "day", "hour")
1276 if validateInput "$argCustomScriptTTL" "time_element"; then
1277 ### T: argCustomScriptTTL is a time element
7df2db7e 1278 SCRIPT_TTL_TE="$argCustomScriptTTL" && vbm "Custom SCRIPT_TTL_TE from -B:$SCRIPT_TTL_TE";
2af0d7d9
SBS
1279 else
1280 ### F: argcustomScriptTTL is not a time element
1281 yell "ERROR:Invalid time element argument for custom script time-to-live."; showUsage; exit 1;
1282 fi;
1283 ## F: do not change SCRIPT_TTL_TE
1284 fi;
1285} # Sets custom script or buffer TTL if specified
1286
032f4b05 1287
320ac29c 1288main() {
cf85eedd
SBS
1289 # DEBUG: Print environment variables
1290 vbm "echo $(printenv)";
320ac29c
SBS
1291 # Process arguments
1292 processArguments "$@";
1293 ## Act upon arguments
1294 ### Determine working directory
c9c573da 1295 magicInitWorkingDir; # Sets DIR_TMP from argTempDirPriority
320ac29c
SBS
1296 ### Set output encryption and compression option strings
1297 #### React to "-r" ("encryption recipients") option
6010b172 1298 magicParseRecipientArgs; # Updates recPubKeysValid, CMD_ENCRYPT[_SUFFIX] from argRecPubKeys
320ac29c 1299 #### React to "-c" ("compression") option
50324e94 1300 magicParseCompressionArg; # Updates CMD_COMPRESS[_SUFFIX]
320ac29c 1301 #### React to "-R" ("recipient directory") option
50324e94 1302 magicParseRecipientDir; # Updates recPubKeysValid
2af0d7d9
SBS
1303 #### React to custom buffer and script TTL options ("-b", "-B")
1304 magicParseCustomTTL; # Sets custom SCRIPT_TTL_TE and/or BUFFER_TTL if specified
320ac29c
SBS
1305
1306 # Check that critical apps and dirs are available, display missing ones.
1307 if ! checkapp gpspipe tar && ! checkdir "$DIR_OUT" "DIR_TMP"; then
6c30388f
SBS
1308 yell "ERROR:Critical components missing.";
1309 displayMissing; yell "Exiting."; exit 1; fi
1310
2af0d7d9
SBS
1311 # Set script lifespan (SCRIPT_TTL from SCRIPT_TTL_TE)
1312 magicSetScriptTTL "$SCRIPT_TTL_TE";
1313 ## Note: SCRIPT_TTL_TE is time element string (ex: "day") while SCRIPT_TTL is integer seconds
6c30388f 1314
2af0d7d9
SBS
1315 # File name substring (ISO-8601 duration from BUFFER_TTL)
1316 bufferTTL_STR="$(timeDuration "$BUFFER_TTL")";
6c30388f
SBS
1317
1318 # Init temp working dir
3a7b92f8 1319 try mkdir "$DIR_TMP" && vbm "DEBUG:Working dir creatd at:$DIR_TMP";
6c30388f
SBS
1320
1321 # Initialize 'tar' archive
1322 ## Define output tar path (note: each day gets *one* tar file (Ex: "20200731..hostname_location.[.gpx.gz].tar"))
9efeccb5
SBS
1323 PATHOUT_TAR="$DIR_OUT"/"$(dateShort)".."$SCRIPT_HOSTNAME""_location""$CMD_COMPRESS_SUFFIX""$CMD_ENCRYPT_SUFFIX".tar && \
1324 vbm "STATUS:Set PATHOUT_TAR to:$PATHOUT_TAR";
f6fb18bd
SBS
1325 ## Check that PATHOUT_TAR is a tar. Rename old and create empty one otherwise.
1326 checkMakeTar "$PATHOUT_TAR" && vbm "DEBUG:Confirmed or Created to be a tar:$PATHOUT_TAR";
6c30388f 1327 ## Append VERSION file to PATHOUT_TAR
3395ae22 1328 magicWriteVersion;
e47e8048
SBS
1329
1330 # Define GPS conversion commands
1331 CMD_CONV_NMEA="tee /dev/null " && vbm "STATUS:Set CMD_CONV_NMEA to:$CMD_CONV_NMEA"; # tee as passthrough
1332 CMD_CONV_GPX="gpsbabel -i nmea -f - -o gpx -F - " && vbm "STATUS:Set CMD_CONV_GPX to:$CMD_CONV_GPX"; # convert NMEA to GPX
1333 CMD_CONV_KML="gpsbabel -i nmea -f - -o kml -F - " && vbm "STATUS:Set CMD_CONV_KML to:$CMD_CONV_KML"; # convert NMEA to KML
811f50f2 1334
e47e8048 1335 # MAIN LOOP:Record gps data until script lifespan ends
2155200a 1336 timeBufferFirstNS="$(timeEpochNS)"; bufferRound=0; BUFFER_TTL_ADJ_FLOAT="$BUFFER_TTL";
2af0d7d9 1337 while [[ "$SECONDS" -lt "$SCRIPT_TTL" ]]; do
52f05d07 1338 magicParseRecipientDir;
811f50f2 1339 magicGatherWriteBuffer &
7f9e13e2 1340 sleep "$BUFFER_TTL_ADJ_FLOAT"; # adjusted by magicBufferSleepPID
52f05d07 1341 ((bufferRound++));
7f9e13e2 1342 magicBufferSleepPID; # Calculates BUFFER_TTL_ADJ_FLOAT from BUFFER_TTL given buffer expected start time vs. actual
811f50f2 1343 done
cfc25c90 1344
e47e8048
SBS
1345 # Cleanup
1346 ## Remove DIR_TMP
cfc25c90 1347 try rm -r "$DIR_TMP";
0aabe6a3 1348
1b0e6227 1349 vbm "STATUS:Main function finished.";
8fbca23d
SBS
1350} # Main function.
1351#===END Declare local script functions===
1352#==END Define script parameters==
032f4b05
SBS
1353
1354
8fbca23d
SBS
1355#==BEGIN Perform work and exit==
1356main "$@" # Run main function.
1357exit 0;
1358#==END Perform work and exit==
d20a8771
SBS
1359
1360# Author: Steven Baltakatei Sandoval;
1361# License: GPLv3+