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