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