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