feat(bkgpslog):Add functions, group code into functions
[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
6c30388f 10SCRIPT_TTL="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.
24b96ff9 17SCRIPT_VERSION="0.3.9"; # 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
8fbca23d 27
cfc25c90
SBS
28## Initialize variables
29OPTION_VERBOSE=""; OPTION_ENCRYPT=""; OPTION_COMPRESS=""; OPTION_TMPDIR="";
30
8fbca23d
SBS
31#===BEGIN Declare local script functions===
32checkapp() {
33 # Desc: If arg is a command, save result in assoc array 'appRollCall'
34 # Usage: checkapp arg1 arg2 arg3 ...
35 # Input: global assoc. array 'appRollCall'
36 # Output: adds/updates key(value) to global assoc array 'appRollCall'
37 local returnState
38 #echo "DEBUG:$(date +%S.%N)..Starting checkapp function."
39 #echo "DEBUG:args: $@"
40 #echo "DEBUG:returnState:$returnState"
41
42 #===Process Args===
43 for arg in "$@"; do
44 #echo "DEBUG:processing arg:$arg"
0b3dde05 45 if command -v "$arg" 1>/dev/null 2>&1; then # Check if arg is a valid command
8fbca23d
SBS
46 appRollCall[$arg]="true";
47 #echo "DEBUG:appRollCall[$arg]:"${appRollCall[$arg]}
48 if ! [ "$returnState" = "false" ]; then returnState="true"; fi
49 else
50 appRollCall[$arg]="false"; returnState="false";
51 fi
52 done
53
54 #for key in "${!appRollCall[@]}"; do echo "DEBUG:$key => ${appRollCall[$key]}"; done
55 #echo "DEBUG:evaluating returnstate. returnState:"$returnState
56
57 #===Determine function return code===
58 if [ "$returnState" = "true" ]; then
59 #echo "DEBUG:checkapp returns true for $arg";
60 return 0;
61 else
62 #echo "DEBUG:checkapp returns false for $arg";
63 return 1;
64 fi
65} # Check that app exists
66checkfile() {
67 # Desc: If arg is a file path, save result in assoc array 'fileRollCall'
68 # Usage: checkfile arg1 arg2 arg3 ...
69 # Input: global assoc. array 'fileRollCall'
70 # Output: adds/updates key(value) to global assoc array 'fileRollCall';
71 # Output: returns 0 if app found, 1 otherwise
72 local returnState
73
74 #===Process Args===
75 for arg in "$@"; do
76 #echo "DEBUG:processing arg:$arg"
77 if [ -f "$arg" ]; then
78 fileRollCall["$arg"]="true";
79 #echo "DEBUG:fileRollCall[\"$arg\"]:"${fileRollCall["$arg"]}
80 if ! [ "$returnState" = "false" ]; then returnState="true"; fi
81 else
82 fileRollCall["$arg"]="false"; returnState="false";
83 fi
84 done
85
86 #for key in "${!fileRollCall[@]}"; do echo "DEBUG:fileRollCall key [$key] is:${fileRollCall[$key]}"; done
87 #echo "DEBUG:evaluating returnstate. returnState:"$returnState
88
89 #===Determine function return code===
90 if [ "$returnState" = "true" ]; then
91 #echo "DEBUG:checkapp returns true for $arg";
92 return 0;
93 else
94 #echo "DEBUG:checkapp returns false for $arg";
95 return 1;
96 fi
97} # Check that file exists
98checkdir() {
99 # Desc: If arg is a dir path, save result in assoc array 'dirRollCall'
100 # Usage: checkdir arg1 arg2 arg3 ...
101 # Input: global assoc. array 'dirRollCall'
102 # Output: adds/updates key(value) to global assoc array 'dirRollCall';
103 # Output: returns 0 if app found, 1 otherwise
104 local returnState
105
106 #===Process Args===
107 for arg in "$@"; do
108 #echo "DEBUG:processing arg:$arg"
109 if [ -d "$arg" ]; then
110 dirRollCall["$arg"]="true";
111 #echo "DEBUG:dirRollCall[\"$arg\"]:"${dirRollCall["$arg"]}
112 if ! [ "$returnState" = "false" ]; then returnState="true"; fi
bcf09dcc 113 elif [ "$arg" = "" ]; then
8fbca23d 114 dirRollCall["$arg"]="false"; returnState="false";
bcf09dcc
SBS
115 else
116 returnState="false";
8fbca23d
SBS
117 fi
118 done
119
120 #for key in "${!dirRollCall[@]}"; do echo "DEBUG:dirRollCall key [$key] is:${dirRollCall[$key]}"; done
121 #echo "DEBUG:evaluating returnstate. returnState:"$returnState
122
123 #===Determine function return code===
124 if [ "$returnState" = "true" ]; then
125 #echo "DEBUG:checkapp returns true for $arg";
126 return 0;
127 else
128 #echo "DEBUG:checkapp returns false for $arg";
129 return 1;
130 fi
131} # Check that dir exists
032f4b05 132
c609b9c8
SBS
133# Yell, Die, Try Three-Fingered Claw technique
134# Ref/Attrib: https://stackoverflow.com/a/25515370
135yell() { echo "$0: $*" >&2; }
136die() { yell "$*"; exit 111; }
137try() { "$@" || die "cannot $*"; }
138
032f4b05
SBS
139echoerr() {
140 echo "$@" 1>&2; # Define stderr echo function.
141} # Define stderr message function.
142showUsage() {
143 echoerr "USAGE:"
94e094d1 144 echoerr " bkgpslog [ options ]"
032f4b05
SBS
145 echoerr
146 echoerr "OPTIONS:"
147 echoerr " -h, --help"
148 echoerr " Display help information."
032f4b05
SBS
149 echoerr " --version"
150 echoerr " Display script version."
032f4b05
SBS
151 echoerr " -v, --verbose"
152 echoerr " Display debugging info."
17d49005
SBS
153 echoerr " -e, --encrypt"
154 echoerr " Encrypt output."
17d49005 155 echoerr " -r, --recipient [ pubkey string ]"
f7f33d33
SBS
156 echoerr " Specify recipient. May be age or ssh pubkey."
157 echoerr " See https://github.com/FiloSottile/age"
032f4b05
SBS
158 echoerr " -o, --output [ directory ]"
159 echoerr " Specify output directory to save logs."
408a342b
SBS
160 echoerr " -c, --compress"
161 echoerr " Compress output with gzip (before encryption if enabled)."
f7f33d33
SBS
162 echoerr " -z, --time-zone"
163 echoerr " Specify time zone. (ex: \"America/New_York\")"
f7f33d33
SBS
164 echoerr " -t, --temp-dir"
165 echoerr " Specify parent directory for temporary working directory."
166 echoerr " Default: \"/dev/shm\""
320ac29c
SBS
167 echoerr " -R, --recipient-dir"
168 echoerr " Specify directory containing files whose first lines are"
169 echoerr " to be interpreted as pubkey strings (see \'-r\' option)."
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() {
179 echoerr "$SCRIPT_VERSION"
180} # Display script version.
181vbm() {
0b3dde05
SBS
182 # Usage: vbm "DEBUG:verbose message here"
183 # Description: Prints verbose message ("vbm") to stderr if OPTION_VERBOSE is set to "true".
184 # Input:
185 # - OPTION_VERBOSE variable set by processArguments function. (ex: "true", "false")
186 # - "$@" positional arguments fed to this function.
187 # Output: stderr
188 # Script function dependencies: echoerr
189 # External function dependencies: echo
190 # Last modified: 2020-04-11T23:57Z
191 # Last modified by: Steven Baltakatei Sandoval
192 # License: GPLv3+
193 # Ref./Attrib:
194
195 if [ "$OPTION_VERBOSE" = "true" ]; then
196 FUNCTION_TIME=$(date --iso-8601=ns); # Save current time in nano seconds.
197 echoerr "[$FUNCTION_TIME] ""$*"; # Display argument text.
032f4b05 198 fi
0b3dde05
SBS
199
200 # End function
201 return 0; # Function finished.
032f4b05
SBS
202} # Verbose message display function.
203processArguments() {
204 while [ ! $# -eq 0 ]; do # While number of arguments ($#) is not (!) equal to (-eq) zero (0).
d6896d3b
SBS
205 #echoerr "DEBUG:Starting processArguments while loop."
206 #echoerr "DEBUG:Provided arguments are:""$*"
032f4b05 207 case "$1" in
c1bbf9f7 208 -h | --help) showUsage; exit 1;; # Display usage.
032f4b05 209 --version) showVersion; exit 1;; # Show version
c1bbf9f7 210 -v | --verbose) OPTION_VERBOSE="true"; vbm "DEBUG:Verbose mode enabled.";; # Enable verbose mode.
6c30388f 211 -o | --output) if [ -d "$2" ]; then DIR_OUT="$2"; vbm "DEBUG:DIR_OUT:$DIR_OUT"; shift; fi ;; # Define output directory.
17d49005
SBS
212 -e | --encrypt) OPTION_ENCRYPT="true"; vbm "DEBUG:Encrypted output mode enabled.";;
213 -r | --recipient) # Add 'age' recipient via public key string
320ac29c 214 argRecPubKeys+=("$2"); vbm "STATUS:pubkey added:""$2"; shift;;
408a342b 215 -c | --compress) OPTION_COMPRESS="true"; vbm "DEBUG:Compressed output mode enabled.";;
6a17e8e7 216 -z | --time-zone) try setTimeZoneEV "$2"; shift;;
320ac29c
SBS
217 -t | --temp-dir) OPTION_TMPDIR="true" && argTmpDirPriority="$2"; shift;;
218 -R | --recipient-dir) OPTION_RECDIR="true" && argRecDir="$2"; shift;;
b0da06ca 219 *) echoerr "ERROR: Unrecognized argument: $1"; echoerr "STATUS:All arguments:$*"; exit 1;; # Handle unrecognized options.
032f4b05
SBS
220 esac
221 shift
222 done
223} # Argument Processing
6c30388f
SBS
224setTimeZoneEV(){
225 # Desc: Set time zone environment variable TZ
226 # Usage: setTimeZoneEV arg1
227 # Input: arg1: 'date'-compatible timezone string (ex: "America/New_York")
228 # TZDIR env var (optional; default: "/usr/share/zoneinfo")
229 # Output: exports TZ
230 # exit code 0 on success
231 # exit code 1 on incorrect number of arguments
232 # exit code 2 if unable to validate arg1
233 # Depends: yell, printenv, bash 5
234 # Tested on: Debian 10
235 ARG1="$1"
236 local tzDir returnState
237 if ! [[ $# -eq 1 ]]; then
238 yell "ERROR:Invalid argument count.";
239 return 1;
240 fi
241
242 # Read TZDIR env var if available
243 if printenv TZDIR 1>/dev/null 2>&1; then
244 tzDir="$(printenv TZDIR)";
245 else
246 tzDir="/usr/share/zoneinfo";
247 fi
248
249 # Validate TZ string
250 if ! [[ -f "$tzDir"/"$ARG1" ]]; then
251 yell "ERROR:Invalid time zone argument.";
252 return 2;
253 else
254 # Export ARG1 as TZ environment variable
255 TZ="$ARG1" && export TZ && returnState="true";
256 fi
257
258 # Determine function return code
259 if [ "$returnState" = "true" ]; then
260 return 0;
261 fi
262} # Exports TZ environment variable
c2aaff78
SBS
263timeUntilNextDay(){
264 # Desc: Report seconds until next day.
3395ae22 265 # Version: 1.0.0
c2aaff78 266 # Output: stdout: integer seconds until next day
8fbca23d 267 # Output: exit code 0 if stdout > 0; 1 if stdout = 0; 2 if stdout < 0
c2aaff78
SBS
268 # Usage: timeUntilNextDay
269 # Usage: if ! myTTL="$(timeUntilNextDay)"; then yell "ERROR in if statement"; exit 1; fi
3395ae22
SBS
270 # Depends: date 8, echo 8, yell, try
271
c2aaff78 272 local returnState TIME_CURRENT TIME_NEXT_DAY SECONDS_UNTIL_NEXT_DAY
3395ae22 273
8fbca23d 274 TIME_CURRENT="$(date --iso-8601=seconds)" ; # Produce `date`-parsable current timestamp with resolution of 1 second.
c2aaff78
SBS
275 TIME_NEXT_DAY="$(date -d "$TIME_CURRENT next day" --iso-8601=date)"; # Produce timestamp of beginning of tomorrow with resolution of 1 second.
276 SECONDS_UNTIL_NEXT_DAY="$(( $(date +%s -d "$TIME_NEXT_DAY") - $(date +%s -d "$TIME_CURRENT") ))" ; # Calculate seconds until closest future midnight (res. 1 second).
277 if [[ "$SECONDS_UNTIL_NEXT_DAY" -gt 0 ]]; then
278 returnState="true";
279 elif [[ "$SECONDS_UNTIL_NEXT_DAY" -eq 0 ]]; then
3395ae22 280 returnState="warning_zero";
c2aaff78
SBS
281 yell "WARNING:Reported time until next day exactly zero.";
282 elif [[ "$SECONDS_UNTIL_NEXT_DAY" -lt 0 ]]; then
3395ae22 283 returnState="warning_negative";
c2aaff78
SBS
284 yell "WARNING:Reported time until next day is negative.";
285 fi
286
287 try echo "$SECONDS_UNTIL_NEXT_DAY"; # Report
288
3395ae22 289 # Determine function return code
c2aaff78
SBS
290 if [[ "$returnState" = "true" ]]; then
291 return 0;
3395ae22 292 elif [[ "$returnState" = "warning_zero" ]]; then
c2aaff78 293 return 1;
3395ae22 294 elif [[ "$returnState" = "warning_negative" ]]; then
c2aaff78
SBS
295 return 2;
296 fi
297} # Report seconds until next day
298timeUntilNextHour(){
299 # Desc: Report seconds until next hour
3395ae22 300 # Version 1.0.0
c2aaff78
SBS
301 # Output: stdout: integer seconds until next hour
302 # Output: exit code 0 if stdout > 0; 1 if stdout = 0; 2 if stdout < 0
303 # Usage: timeUntilNextHour
304 # Usage: if ! myTTL="$(timeUntilNextHour)"; then yell "ERROR in if statement"; exit 1; fi
3395ae22 305
c2aaff78
SBS
306 local returnState TIME_CURRENT TIME_NEXT_HOUR SECONDS_UNTIL_NEXT_HOUR
307 TIME_CURRENT="$(date --iso-8601=seconds)"; # Produce `date`-parsable current timestamp with resolution of 1 second.
308 TIME_NEXT_HOUR="$(date -d "$TIME_CURRENT next hour" --iso-8601=hours)"; # Produce `date`-parsable current time stamp with resolution of 1 second.
309 SECONDS_UNTIL_NEXT_HOUR="$(( $(date +%s -d "$TIME_NEXT_HOUR") - $(date +%s -d "$TIME_CURRENT") ))"; # Calculate seconds until next hour (res. 1 second).
310 if [[ "$SECONDS_UNTIL_NEXT_HOUR" -gt 0 ]]; then
8fbca23d 311 returnState="true";
c2aaff78 312 elif [[ "$SECONDS_UNTIL_NEXT_HOUR" -eq 0 ]]; then
3395ae22 313 returnState="warning_zero";
c2aaff78
SBS
314 yell "WARNING:Reported time until next hour exactly zero.";
315 elif [[ "$SECONDS_UNTIL_NEXT_HOUR" -lt 0 ]]; then
3395ae22 316 returnState="warning_negative";
c2aaff78 317 yell "WARNING:Reported time until next hour is negative.";
8fbca23d 318 fi
032f4b05 319
c2aaff78 320 try echo "$SECONDS_UNTIL_NEXT_HOUR"; # Report
8fbca23d 321
3395ae22 322 # Determine function return code
8fbca23d
SBS
323 if [[ "$returnState" = "true" ]]; then
324 return 0;
3395ae22 325 elif [[ "$returnState" = "warning_zero" ]]; then
8fbca23d 326 return 1;
3395ae22 327 elif [[ "$returnState" = "warning_negative" ]]; then
8fbca23d
SBS
328 return 2;
329 fi
c2aaff78 330} # Report seconds until next hour
8fbca23d
SBS
331dateTimeShort(){
332 # Desc: Timestamp without separators (YYYYmmddTHHMMSS+zzzz)
e47e8048
SBS
333 # Usage: dateTimeShort ([str date])
334 # Version 1.1.0
335 # Input: arg1: 'date'-parsable timestamp string (optional)
8fbca23d 336 # Output: stdout: timestamp (ISO-8601, no separators)
e47e8048 337 # Depends: yell
c2aaff78 338 local TIME_CURRENT TIME_CURRENT_SHORT
e47e8048
SBS
339
340 argTime="$1";
341 # Get Current Time
8fbca23d 342 TIME_CURRENT="$(date --iso-8601=seconds)" ; # Produce `date`-parsable current timestamp with resolution of 1 second.
e47e8048
SBS
343 # Decide to parse current or supplied date
344 ## Check if time argument empty
345 if [[ -z "$argTime" ]]; then
346 ## T: Time argument empty, use current time
347 TIME_INPUT="$TIME_CURRENT";
348 else
349 ## F: Time argument exists, validate time
350 if date --date="$argTime" 1>/dev/null 2>&1; then
351 ### T: Time argument is valid; use it
352 TIME_INPUT="$argTime";
353 else
354 ### F: Time argument not valid; exit
355 yell "ERROR:Invalid time argument supplied. Exiting."; exit 1;
356 fi
357 fi
358 # Construct and deliver separator-les date string
359 TIME_CURRENT_SHORT="$(date -d "$TIME_INPUT" +%Y%m%dT%H%M%S%z)";
8fbca23d 360 echo "$TIME_CURRENT_SHORT";
6c30388f
SBS
361} # Get YYYYmmddTHHMMSS±zzzz
362dateShort(){
363 # Desc: Date without separators (YYYYmmdd)
e47e8048
SBS
364 # Usage: dateShort ([str date])
365 # Version: 1.1.0
366 # Input: arg1: 'date'-parsable timestamp string (optional)
6c30388f 367 # Output: stdout: date (ISO-8601, no separators)
e47e8048 368 # Depends: yell
6c30388f 369 local TIME_CURRENT DATE_CURRENT_SHORT
e47e8048
SBS
370
371 argTime="$1";
372 # Get Current Time
6c30388f 373 TIME_CURRENT="$(date --iso-8601=seconds)" ; # Produce `date`-parsable current timestamp with resolution of 1 second.
e47e8048
SBS
374 # Decide to parse current or supplied date
375 ## Check if time argument empty
376 if [[ -z "$argTime" ]]; then
377 ## T: Time argument empty, use current time
378 TIME_INPUT="$TIME_CURRENT";
379 else
380 ## F: Time argument exists, validate time
381 if date --date="$argTime" 1>/dev/null 2>&1; then
382 ### T: Time argument is valid; use it
383 TIME_INPUT="$argTime";
384 else
385 ### F: Time argument not valid; exit
386 yell "ERROR:Invalid time argument supplied. Exiting."; exit 1;
387 fi
388 fi
389 # Construct and deliver separator-les date string
390 DATE_CURRENT_SHORT="$(date -d "$TIME_INPUT" +%Y%m%d)"; # Produce separator-less current date with resolution 1 day.
6c30388f
SBS
391 echo "$DATE_CURRENT_SHORT";
392} # Get YYYYmmdd
393timeDuration(){
a7b4a273 394 # Desc: Given seconds, output ISO-8601 duration string
6c30388f
SBS
395 # Ref/Attrib: ISO-8601:2004(E), §4.4.4.2 Representations of time intervals by duration and context information
396 # Note: "1 month" ("P1M") is assumed to be "30 days" (see ISO-8601:2004(E), §2.2.1.2)
a7b4a273
SBS
397 # Usage: timeDuration [1:seconds] ([2:precision])
398 # Version: 1.0.3
6c30388f
SBS
399 # Input: arg1: seconds as base 10 integer >= 0 (ex: 3601)
400 # arg2: precision level (optional; default=2)
401 # Output: stdout: ISO-8601 duration string (ex: "P1H1S", "P2Y10M15DT10H30M20S")
a7b4a273
SBS
402 # exit code 0: success
403 # exit code 1: error_input
404 # exit code 2: error_unknown
6c30388f 405 # Example: 'timeDuration 111111 3' yields 'P1DT6H51M'
3395ae22 406 # Depends: date 8 (gnucoreutils), yell,
a7b4a273 407 local returnState argSeconds argPrecision remainder precision witherPrecision
3395ae22
SBS
408 local fullYears fullMonths fullDays fullHours fullMinutes fullSeconds
409 local displayYears displayMonths displayDays displayHours displayMinutes displaySeconds
410 local hasYears hasMonths hasDays hasHours hasMinutes hasSeconds
411
a7b4a273
SBS
412 argSeconds="$1"; # read arg1 (seconds)
413 argPrecision="$2"; # read arg2 (precision)
6c30388f 414 precision=2; # set default precision
6c30388f
SBS
415
416 # Check that between one and two arguments is supplied
417 if ! { [[ $# -ge 1 ]] && [[ $# -le 2 ]]; }; then
418 yell "ERROR:Invalid number of arguments:$# . Exiting.";
a7b4a273 419 returnState="error_input"; fi
6c30388f 420
a7b4a273 421 # Check that argSeconds provided
6c30388f 422 if [[ $# -ge 1 ]]; then
a7b4a273
SBS
423 ## Check that argSeconds is a positive integer
424 if [[ "$argSeconds" =~ ^[[:digit:]]+$ ]]; then
3395ae22 425 :
6c30388f 426 else
a7b4a273
SBS
427 yell "ERROR:argSeconds not a digit.";
428 returnState="error_input";
6c30388f
SBS
429 fi
430 else
431 yell "ERROR:No argument provided. Exiting.";
432 exit 1;
433 fi
434
a7b4a273 435 # Consider whether argPrecision was provided
6c30388f 436 if [[ $# -eq 2 ]]; then
a7b4a273
SBS
437 # Check that argPrecision is a positive integer
438 if [[ "$argPrecision" =~ ^[[:digit:]]+$ ]] && [[ "$argPrecision" -gt 0 ]]; then
439 precision="$argPrecision";
6c30388f 440 else
a7b4a273
SBS
441 yell "ERROR:argPrecision not a positive integer. (is $argPrecision ). Leaving early.";
442 returnState="error_input";
6c30388f
SBS
443 fi;
444 else
3395ae22 445 :
6c30388f
SBS
446 fi;
447
a7b4a273 448 remainder="$argSeconds" ; # seconds
6c30388f
SBS
449 ## Calculate full years Y, update remainder
450 fullYears=$(( remainder / (365*24*60*60) ));
451 remainder=$(( remainder - (fullYears*365*24*60*60) ));
452 ## Calculate full months M, update remainder
453 fullMonths=$(( remainder / (30*24*60*60) ));
454 remainder=$(( remainder - (fullMonths*30*24*60*60) ));
455 ## Calculate full days D, update remainder
456 fullDays=$(( remainder / (24*60*60) ));
457 remainder=$(( remainder - (fullDays*24*60*60) ));
458 ## Calculate full hours H, update remainder
459 fullHours=$(( remainder / (60*60) ));
460 remainder=$(( remainder - (fullHours*60*60) ));
461 ## Calculate full minutes M, update remainder
462 fullMinutes=$(( remainder / (60) ));
463 remainder=$(( remainder - (fullMinutes*60) ));
464 ## Calculate full seconds S, update remainder
465 fullSeconds=$(( remainder / (1) ));
466 remainder=$(( remainder - (remainder*1) ));
467 ## Check which fields filled
468 if [[ $fullYears -gt 0 ]]; then hasYears="true"; else hasYears="false"; fi
469 if [[ $fullMonths -gt 0 ]]; then hasMonths="true"; else hasMonths="false"; fi
470 if [[ $fullDays -gt 0 ]]; then hasDays="true"; else hasDays="false"; fi
471 if [[ $fullHours -gt 0 ]]; then hasHours="true"; else hasHours="false"; fi
472 if [[ $fullMinutes -gt 0 ]]; then hasMinutes="true"; else hasMinutes="false"; fi
473 if [[ $fullSeconds -gt 0 ]]; then hasSeconds="true"; else hasSeconds="false"; fi
474
475 ## Determine which fields to display (see ISO-8601:2004 §4.4.3.2)
476 witherPrecision="false"
477
478 ### Years
479 if $hasYears && [[ $precision -gt 0 ]]; then
480 displayYears="true";
481 witherPrecision="true";
482 else
483 displayYears="false";
484 fi;
485 if $witherPrecision; then ((precision--)); fi;
486
487 ### Months
488 if $hasMonths && [[ $precision -gt 0 ]]; then
489 displayMonths="true";
490 witherPrecision="true";
491 else
492 displayMonths="false";
493 fi;
494 if $witherPrecision && [[ $precision -gt 0 ]]; then
495 displayMonths="true";
496 fi;
497 if $witherPrecision; then ((precision--)); fi;
498
499 ### Days
500 if $hasDays && [[ $precision -gt 0 ]]; then
501 displayDays="true";
502 witherPrecision="true";
503 else
504 displayDays="false";
505 fi;
506 if $witherPrecision && [[ $precision -gt 0 ]]; then
507 displayDays="true";
508 fi;
509 if $witherPrecision; then ((precision--)); fi;
510
511 ### Hours
512 if $hasHours && [[ $precision -gt 0 ]]; then
513 displayHours="true";
514 witherPrecision="true";
515 else
516 displayHours="false";
517 fi;
518 if $witherPrecision && [[ $precision -gt 0 ]]; then
519 displayHours="true";
520 fi;
521 if $witherPrecision; then ((precision--)); fi;
522
523 ### Minutes
524 if $hasMinutes && [[ $precision -gt 0 ]]; then
525 displayMinutes="true";
526 witherPrecision="true";
527 else
528 displayMinutes="false";
529 fi;
530 if $witherPrecision && [[ $precision -gt 0 ]]; then
531 displayMinutes="true";
532 fi;
533 if $witherPrecision; then ((precision--)); fi;
534
535 ### Seconds
536
537 if $hasSeconds && [[ $precision -gt 0 ]]; then
538 displaySeconds="true";
539 witherPrecision="true";
540 else
541 displaySeconds="false";
542 fi;
543 if $witherPrecision && [[ $precision -gt 0 ]]; then
544 displaySeconds="true";
545 fi;
546 if $witherPrecision; then ((precision--)); fi;
547
6c30388f
SBS
548 ## Determine whether or not the "T" separator is needed to separate date and time elements
549 if ( $displayHours || $displayMinutes || $displaySeconds); then
550 displayDateTime="true"; else displayDateTime="false"; fi
551
552 ## Construct duration output string
553 OUTPUT="P"
554 if $displayYears; then
555 OUTPUT=$OUTPUT$fullYears"Y"; fi
556 if $displayMonths; then
557 OUTPUT=$OUTPUT$fullMonths"M"; fi
558 if $displayDays; then
559 OUTPUT=$OUTPUT$fullDays"D"; fi
560 if $displayDateTime; then
561 OUTPUT=$OUTPUT"T"; fi
562 if $displayHours; then
563 OUTPUT=$OUTPUT$fullHours"H"; fi
564 if $displayMinutes; then
565 OUTPUT=$OUTPUT$fullMinutes"M"; fi
566 if $displaySeconds; then
567 OUTPUT=$OUTPUT$fullSeconds"S"; fi
568
569 ## Output duration string to stdout
a7b4a273 570 echo "$OUTPUT" && returnState="true";
6c30388f
SBS
571
572 #===Determine function return code===
573 if [ "$returnState" = "true" ]; then
574 return 0;
a7b4a273
SBS
575 elif [ "$returnState" = "error_input" ]; then
576 yell "ERROR:input";
6c30388f 577 return 1;
a7b4a273
SBS
578 else
579 yell "ERROR:Unknown";
580 return 2;
6c30388f
SBS
581 fi
582
583} # Get duration (ex: PT10M4S )
584displayMissing() {
585 # Desc: Displays missing apps, files, and dirs
586 # Usage: displayMissing
587 # Input: associative arrays: appRollCall, fileRollCall, dirRollCall
588 # Output: stderr messages
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
640setScriptTTL() {
641 #Desc: Sets script TTL
642 #Usage: setScriptTTL arg1
643 #Input: arg1: "day" or "hour"
644 #Output: scriptTTL
645 #Depends: timeUntilNextHour or timeUntilNextDay
646 local ARG1
647 ARG1="$1"
648 if [[ "$ARG1" = "day" ]]; then
649 # Set script lifespan to end at start of next day
650 if ! scriptTTL="$(timeUntilNextDay)"; then
651 if [[ "$scriptTTL" -eq 0 ]]; then
652 ((scriptTTL++)); # Add 1 because 0 would cause 'timeout' to never timeout.
653 else
654 yell "ERROR: timeUntilNextDay exit code $?"; exit 1;
655 fi;
656 fi;
657 elif [[ "$ARG1" = "hour" ]]; then
658 # Set script lifespan to end at start of next hour
659 if ! scriptTTL="$(timeUntilNextHour)"; then
660 if [[ "$scriptTTL" -eq 0 ]]; then
661 ((scriptTTL++)); # Add 1 because 0 would cause 'timeout' to never timeout.
662 else
663 yell "ERROR: timeUntilNextHour exit code $?"; exit 1;
664 fi;
665 fi;
666 else
667 yell "ERROR:Invalid argument for setScriptTTL function."; exit 1;
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 ]
2fc10824 673 # Version: 1.0.1
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
f6fb18bd 678 # Depends: try, tar, date
2fc10824
SBS
679 local PATH_TAR returnFlag0 returnFlag1 returnFlag2
680 PATH_TAR="$1"
f6fb18bd
SBS
681
682 # Check if file is a valid tar archive
683 if tar --list --file="$PATH_TAR" 1>/dev/null 2>&1; then
684 ## T1: return success
2fc10824 685 returnFlag0="tar valid";
f6fb18bd
SBS
686 else
687 ## F1: Check if file exists
688 if [[ -f "$PATH_TAR" ]]; then
689 ### T: Rename file
2fc10824
SBS
690 try mv "$PATH_TAR" "$PATH_TAR""--broken--""$(date +%Y%m%dT%H%M%S)" && \
691 returnFlag1="tar moved";
f6fb18bd
SBS
692 else
693 ### F: -
694 :
695 fi
696 ## F2: Create tar archive, return 0
2fc10824
SBS
697 try tar --create --file="$PATH_TAR" --files-from=/dev/null && \
698 returnFlag2="tar created";
699 fi
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
f6fb18bd
SBS
708 fi
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]...)
f665ce95 713 # Version: 1.0.3
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" &
66af6225 724 # Depends: bash 5
f665ce95 725 # Ref/Attrib: Using 'eval' to construct command strings https://askubuntu.com/a/476533
66af6225 726
d9fe1eba 727 # Save function name
d7138c7f
SBS
728 local FN="${FUNCNAME[0]}";
729 #yell "DEBUG:STATUS:$FN:Finished appendArgTar()."
d9fe1eba 730
66af6225 731 # Set file name
d9fe1eba 732 if ! [ -z "$2" ]; then FILENAME="$2"; else yell "ERROR:$FN:Not enough arguments."; exit 1; fi
66af6225
SBS
733
734 # Check tar path is a file
d9fe1eba 735 if [ -f "$3" ]; then TAR_PATH="$3"; else yell "ERROR:$FN:Tar archive arg not a file."; exit 1; fi
66af6225
SBS
736
737 # Check temp dir arg
d9fe1eba 738 if ! [ -z "$4" ]; then TMP_DIR="$4"; else yell "ERROR:$FN:No temporary working dir set."; exit 1; fi
66af6225
SBS
739
740 # Set command strings
741 if ! [ -z "$5" ]; then CMD1="$5"; else CMD1="tee /dev/null "; fi # command string 1
742 if ! [ -z "$6" ]; then CMD2="$6"; else CMD2="tee /dev/null "; fi # command string 2
743 if ! [ -z "$7" ]; then CMD3="$7"; else CMD3="tee /dev/null "; fi # command string 3
744 if ! [ -z "$8" ]; then CMD4="$8"; else CMD4="tee /dev/null "; fi # command string 4
745
f665ce95
SBS
746 # Input command
747 CMD0="echo \"\$1\""
748
d7138c7f 749 # # Debug
f665ce95 750 # yell "DEBUG:STATUS:$FN:CMD0:$CMD0"
d7138c7f
SBS
751 # yell "DEBUG:STATUS:$FN:CMD1:$CMD1"
752 # yell "DEBUG:STATUS:$FN:CMD2:$CMD2"
753 # yell "DEBUG:STATUS:$FN:CMD3:$CMD3"
754 # yell "DEBUG:STATUS:$FN:CMD4:$CMD4"
755 # yell "DEBUG:STATUS:$FN:FILENAME:$FILENAME"
756 # yell "DEBUG:STATUS:$FN:TAR_PATH:$TAR_PATH"
757 # yell "DEBUG:STATUS:$FN:TMP_DIR:$TMP_DIR"
758
66af6225 759 # Write to temporary working dir
f665ce95 760 eval "$CMD0"" | ""$CMD1"" | ""$CMD2"" | ""$CMD3"" | ""$CMD4" > "$TMP_DIR"/"$FILENAME";
66af6225
SBS
761
762 # Append to tar
763 try tar --append --directory="$TMP_DIR" --file="$TAR_PATH" "$FILENAME";
d7138c7f 764 #yell "DEBUG:STATUS:$FN:Finished appendArgTar()."
66af6225 765} # Append Bash var to file appended to Tar archive
e47e8048
SBS
766appendFileTar(){
767 # Desc: Processes first file and then appends to tar
768 # Usage: appendFileTar [file path] [name of file to be inserted] [tar path] [temp dir] ([cmd1] [cmd2] [cmd3] [cmd4]...)
f665ce95 769 # Version: 1.0.2
e47e8048
SBS
770 # Input: arg1: path of file to be (processed and) written
771 # arg2: name to use for file inserted into tar
772 # arg3: tar archive path (must exist first)
773 # arg4: temporary working dir
774 # arg5+: command strings (ex: "gpsbabel -i nmea -f - -o kml -F - ")
775 # Output: file written to disk
776 # Example: decrypt multiple large files in parallel
777 # appendFileTar /tmp/largefile1.gpg "largefile1" $HOME/archive.tar /tmp "gpg --decrypt" &
778 # appendFileTar /tmp/largefile2.gpg "largefile2" $HOME/archive.tar /tmp "gpg --decrypt" &
779 # appendFileTar /tmp/largefile3.gpg "largefile3" $HOME/archive.tar /tmp "gpg --decrypt" &
780 # Depends: bash 5
781
782 # Save function name
783 local FN="${FUNCNAME[0]}";
784 #yell "DEBUG:STATUS:$FN:Finished appendFileTar()."
785
786 # Set file name
787 if ! [ -z "$2" ]; then FILENAME="$2"; else yell "ERROR:$FN:Not enough arguments."; exit 1; fi
788 # Check tar path is a file
789 if [ -f "$3" ]; then TAR_PATH="$3"; else yell "ERROR:$FN:Tar archive arg not a file."; exit 1; fi
790 # Check temp dir arg
791 if ! [ -z "$4" ]; then TMP_DIR="$4"; else yell "ERROR:$FN:No temporary working dir set."; exit 1; fi
792 # Set command strings
793 if ! [ -z "$5" ]; then CMD1="$5"; else CMD1="tee /dev/null "; fi # command string 1
794 if ! [ -z "$6" ]; then CMD2="$6"; else CMD2="tee /dev/null "; fi # command string 2
795 if ! [ -z "$7" ]; then CMD3="$7"; else CMD3="tee /dev/null "; fi # command string 3
796 if ! [ -z "$8" ]; then CMD4="$8"; else CMD4="tee /dev/null "; fi # command string 4
f665ce95
SBS
797
798 # Input command string
799 CMD0="cat \"\$1\""
800
e47e8048 801 # # Debug
f665ce95 802 # yell "DEBUG:STATUS:$FN:CMD0:$CMD0"
e47e8048
SBS
803 # yell "DEBUG:STATUS:$FN:CMD1:$CMD1"
804 # yell "DEBUG:STATUS:$FN:CMD2:$CMD2"
805 # yell "DEBUG:STATUS:$FN:CMD3:$CMD3"
806 # yell "DEBUG:STATUS:$FN:CMD4:$CMD4"
807 # yell "DEBUG:STATUS:$FN:FILENAME:$FILENAME"
808 # yell "DEBUG:STATUS:$FN:TAR_PATH:$TAR_PATH"
809 # yell "DEBUG:STATUS:$FN:TMP_DIR:$TMP_DIR"
810
811 # Write to temporary working dir
f665ce95 812 eval "$CMD0 | $CMD1 | $CMD2 | $CMD3 | $CMD4" > "$TMP_DIR"/"$FILENAME";
e47e8048
SBS
813
814 # Append to tar
815 try tar --append --directory="$TMP_DIR" --file="$TAR_PATH" "$FILENAME";
816 #yell "DEBUG:STATUS:$FN:Finished appendFileTar()."
817} # Append file to Tar archive
320ac29c
SBS
818checkAgePubkey() {
819 # Desc: Checks if string is an age-compatible pubkey
820 # Usage: checkAgePubkey [str pubkey]
821 # Version: 0.1.2
822 # Input: arg1: string
823 # Output: return code 0: string is age-compatible pubkey
824 # return code 1: string is NOT an age-compatible pubkey
825 # age stderr (ex: there is stderr if invalid string provided)
826 # Depends: age (v0.1.0-beta2; https://github.com/FiloSottile/age/releases/tag/v1.0.0-beta2 )
827
828 argPubkey="$1";
829
830 if echo "test" | age -a -r "$argPubkey" 1>/dev/null; then
831 return 0;
832 else
833 return 1;
834 fi;
835} # Check age pubkey
d6ba4173
SBS
836validateInput() {
837 # Desc: Validates Input
838 # Usage: validateInput [str input] [str input type]
839 # Version: 0.2.1
840 # Input: arg1: string to validate
841 # arg2: string specifying input type (ex:"ssh_pubkey")
842 # Output: return code 0: if input string matched specified string type
843 # Depends: bash 5, yell
844
845 # Save function name
846 local FN="${FUNCNAME[0]}";
847
848 # Process arguments
849 argInput="$1";
850 argType="$2";
851 if [[ $# -gt 2 ]]; then yell "ERROR:$0:$FN:Too many arguments."; exit 1; fi;
852
853 # Check for blank
854 if [[ -z "$argInput" ]]; then return 1; fi
855
856 # Define input types
857 ## ssh_pubkey
858 ### Check for alnum/dash base64 (ex: "ssh-rsa AAAAB3NzaC1yc2EAAA")
859 if [[ "$argType" = "ssh_pubkey" ]]; then
860 if [[ "$argInput" =~ ^[[:alnum:]-]*[\ ]*[[:alnum:]+/=]*$ ]]; then
861 return 0; fi; fi;
862
863 ## age_pubkey
864 ### Check for age1[:bech32:]
865 if [[ "$argType" = "age_pubkey" ]]; then
866 if [[ "$argInput" =~ ^age1[qpzry9x8gf2tvdw0s3jn54khce6mua7l]*$ ]]; then
867 return 0; fi; fi
868
869 # Return error if no condition matched.
870 return 1;
871} # Validates strings
3395ae22
SBS
872magicWriteVersion() {
873 # Desc: Appends time-stamped VERSION to PATHOUT_TAR
874 # Usage: magicWriteVersion
ff22a93f 875 # Version: 0.1.0
3395ae22 876 # Input: CONTENT_VERSION, FILEOUT_VERSION, PATHOUT_TAR, DIR_TMP
ff22a93f
SBS
877 # Input: SCRIPT_VERSION, SCRIPT_URL, AGE_VERSION, AGE_URL, SCRIPT_HOSTNAME
878 # Output: appends tar PATHOUT_TAR
3395ae22 879 # Depends: dateTimeShort, appendArgTar
ff22a93f 880 local CONTENT_VERSION pubKeyIndex
3395ae22 881
ff22a93f 882 # Set VERSION file name
3395ae22 883 FILEOUT_VERSION="$(dateTimeShort)..VERSION";
ff22a93f
SBS
884
885 # Gather VERSION data in CONTENT_VERSION
886 CONTENT_VERSION="SCRIPT_VERSION=$SCRIPT_VERSION";
aa2d7921
SBS
887 #CONTENT_VERSION="$CONTENT_VERSION""\\n";
888 CONTENT_VERSION="$CONTENT_VERSION""\\n""SCRIPT_NAME=$SCRIPT_NAME";
889 CONTENT_VERSION="$CONTENT_VERSION""\\n""SCRIPT_URL=$SCRIPT_URL";
890 CONTENT_VERSION="$CONTENT_VERSION""\\n""AGE_VERSION=$AGE_VERSION";
891 CONTENT_VERSION="$CONTENT_VERSION""\\n""AGE_URL=$AGE_URL";
892 CONTENT_VERSION="$CONTENT_VERSION""\\n""DATE=$(date --iso-8601=seconds)";
893 CONTENT_VERSION="$CONTENT_VERSION""\\n""HOSTNAME=$SCRIPT_HOSTNAME";
ff22a93f
SBS
894 ## Add list of recipient pubkeys
895 for pubkey in "${recPubKeysValid[@]}"; do
896 ((pubKeyIndex++))
aa2d7921 897 CONTENT_VERSION="$CONTENT_VERSION""\\n""PUBKEY_$pubKeyIndex=$pubkey";
ff22a93f
SBS
898 done
899 ## Process newline escapes
4c0c5850
SBS
900 CONTENT_VERSION="$(echo -e "$CONTENT_VERSION")"
901
ff22a93f 902 # Write CONTENT_VERSION as file FILEOUT_VERSION and write-append to PATHOUT_TAR
3395ae22 903 appendArgTar "$CONTENT_VERSION" "$FILEOUT_VERSION" "$PATHOUT_TAR" "$DIR_TMP";
ff22a93f 904
3395ae22 905} # bkgpslog: write version data to PATHOUT_TAR via appendArgTar()
811f50f2 906magicGatherWriteBuffer() {
66af6225 907 # Desc: bkgpslog-specific meta function for writing data to DIR_TMP then appending each file to PATHOUT_TAR
e47e8048
SBS
908 # Inputs: PATHOUT_TAR FILEOUT_{NMEA,GPX,KML} CMD_CONV_{NMEA,GPX,KML} CMD_{COMPRESS,ENCRYPT} DIR_TMP,
909 # Inputs: BUFFER_TTL bufferTTL_STR SCRIPT_HOSTNAME CMD_COMPRESS_SUFFIX CMD_ENCRYPT_SUFFIX
f6fb18bd 910 # Depends: yell, try, vbm, appendArgTar, tar
1b0e6227 911 local FN="${FUNCNAME[0]}";
872c737e 912 wait; # Wait to avoid collision with older magicWriteBuffer() instances (see https://www.tldp.org/LDP/abs/html/x9644.html )
811f50f2
SBS
913 # Create buffer file with unique name
914 PATHOUT_BUFFER="$DIR_TMP/buffer$SECONDS";
915 # Fill buffer
916 timeout "$BUFFER_TTL"s gpspipe -r -o "$PATHOUT_BUFFER" ;
11f61f1a 917 timeBufferStart="$(dateTimeShort "$(date --date="$BUFFER_TTL seconds ago")")"; # Note start time
e47e8048
SBS
918 vbm "DEBUG:STATUS:$FN:Started magicWriteBuffer().";
919 # Determine file paths (time is start of buffer period)
920 FILEOUT_BASENAME="$timeBufferStart""--""$bufferTTL_STR""..""$SCRIPT_HOSTNAME""_location" && vbm "STATUS:Set FILEOUT_BASENAME to:$FILEOUT_BASENAME";
921 ## Files saved to DIR_TMP
922 FILEOUT_NMEA="$FILEOUT_BASENAME".nmea"$CMD_COMPRESS_SUFFIX""$CMD_ENCRYPT_SUFFIX" && vbm "STATUS:Set FILEOUT_NMEA to:$FILEOUT_NMEA";
923 FILEOUT_GPX="$FILEOUT_BASENAME".gpx"$CMD_COMPRESS_SUFFIX""$CMD_ENCRYPT_SUFFIX" && vbm "STATUS:Set FILEOUT_GPX to:$FILEOUT_GPX";
924 FILEOUT_KML="$FILEOUT_BASENAME".kml"$CMD_COMPRESS_SUFFIX""$CMD_ENCRYPT_SUFFIX" && vbm "STATUS:Set FILEOUT_KML to:$FILEOUT_KML";
925 PATHOUT_NMEA="$DIR_TMP"/"$FILEOUT_NMEA" && vbm "STATUS:Set PATHOUT_NMEA to:$PATHOUT_NMEA";
926 PATHOUT_GPX="$DIR_TMP"/"$FILEOUT_GPX" && vbm "STATUS:Set PATHOUT_GPX to:$PATHOUT_GPX";
927 PATHOUT_KML="$DIR_TMP"/"$FILEOUT_KML" && vbm "STATUS:Set PATHOUT_KML to:$PATHOUT_KML";
928 ## Files saved to disk (DIR_OUT)
929 ### one file per day (Ex: "20200731..hostname_location.[.gpx.gz].tar")
930 PATHOUT_TAR="$DIR_OUT"/"$(dateShort "$(date --date="$BUFFER_TTL seconds ago")")".."$SCRIPT_HOSTNAME""_location""$CMD_COMPRESS_SUFFIX""$CMD_ENCRYPT_SUFFIX".tar && \
931 vbm "STATUS:Set PATHOUT_TAR to:$PATHOUT_TAR";
932 # DEBUG: check vars
933 vbm "STATUS:DIR_TMP :$DIR_TMP";
934 vbm "STATUS:PATHOUT_TAR :$PATHOUT_TAR";
935 vbm "STATUS:PATHOUT_NMEA:$PATHOUT_NMEA";
936 vbm "STATUS:PATHOUT_GPX:$PATHOUT_GPX";
937 vbm "STATUS:PATHOUT_KML:$PATHOUT_KML";
938
939
3395ae22
SBS
940 # Validate PATHOUT_TAR as tar.
941 checkMakeTar "$PATHOUT_TAR";
942 ## Add VERSION file if checkMakeTar had to create a tar (exited 1) or replace one (exited 2)
943 if [[ $? -eq 1 ]] || [[ $? -eq 2 ]]; then magicWriteVersion; fi
f6fb18bd
SBS
944
945 # Write bufferBash to PATHOUT_TAR
e47e8048
SBS
946 appendFileTar "$PATHOUT_BUFFER" "$FILEOUT_NMEA" "$PATHOUT_TAR" "$DIR_TMP" "$CMD_CONV_NMEA" "$CMD_COMPRESS" "$CMD_ENCRYPT"; # Write NMEA data
947 appendFileTar "$PATHOUT_BUFFER" "$FILEOUT_GPX" "$PATHOUT_TAR" "$DIR_TMP" "$CMD_CONV_GPX" "$CMD_COMPRESS" "$CMD_ENCRYPT"; # Write GPX file
948 appendFileTar "$PATHOUT_BUFFER" "$FILEOUT_KML" "$PATHOUT_TAR" "$DIR_TMP" "$CMD_CONV_KML" "$CMD_COMPRESS" "$CMD_ENCRYPT"; # Write KML file
949
e0452100 950 # Remove secured chunks from DIR_TMP
e47e8048
SBS
951 rm "$PATHOUT_BUFFER" "$PATHOUT_NMEA" "$PATHOUT_GPX" "$PATHOUT_KML";
952 vbm "DEBUG:STATUS:$FN:Finished magicWriteBuffer().";
953} # write buffer to disk
320ac29c
SBS
954magicParseRecipientDir() {
955 # Desc: Updates recPubKeysValid with pubkeys in dir specified by '-R' option ("recipient directory")
956 # Inputs: vars: OPTION_RECDIR, argRecDir,
957 # arry: recPubKeysValid
958 # Outputs: arry: recPubKeysValid (modified with pubkeys in argRecDir if pubkeys valid)
959 # Depends: processArguments,
960 local recFileLine updateRecipients recipientDir
961 declare -a candRecPubKeysValid
cfc25c90 962
320ac29c
SBS
963 if [[ "$OPTION_RECDIR" = "true" ]]; then
964 ### Check that argRecDir is a directory.
965 if [[ -d "$argRecDir" ]]; then
966 recipientDir="$argRecDir";
967 #### Initialize variable indicating outcome of pubkey review
968 unset updateRecipients
969 #### Add existing recipients
970 candRecPubKeysValid=(${recPubKeysValid[@]});
971 #### Parse files in recipientDir
972 for file in "$recipientDir"/*; do
973 ##### Read first line of each file
974 recFileLine="$(cat "$file" | head -n1)";
975 ##### check if first line is a valid pubkey
976 if checkAgePubkey "$recFileLine" && \
977 ( validateInput "$recFileLine" "ssh_pubkey" || validateInput "$recFileLine" "age_pubkey"); then
978 ###### T: add candidate pubkey to candRecPubKeysValid
979 candRecPubKeysValid+=("$recFileLine");
980 else
981 ###### F: throw warning;
982 yell "ERROR:Invalid recipient file detected. Not modifying recipient list."
983 updateRecipients="false";
984 fi;
985 done
986 #### Write updated recPubKeysValid array to recPubKeysValid if no failure detected
987 if ! updateRecipients="false"; then
988 recPubKeysValid=(${candRecPubKeysValid[@]});
989 fi;
cfc25c90 990 else
320ac29c
SBS
991 yell "ERROR:$0:Recipient directory $argRecDir does not exist. Exiting."; exit 1;
992 fi;
993 fi;
994} # Update recPubKeysValid with argRecDir
995magicParseRecipientArgs() {
996 # Desc: Parses recipient arguments specified by '-r' option
997 # Input: vars: OPTION_ENCRYPT from processArguments()
998 # arry: argRecPubKeys from processArguments()
999 # Output: vars: CMD_ENCRYPT, CMD_ENCRYPT_SUFFIX
1000 # arry: recPubKeysValid
1001 # Depends: checkapp(), checkAgePubkey(), validateInput(), processArguments()
1002 local recipients
cfc25c90 1003
17d49005
SBS
1004 if [[ "$OPTION_ENCRYPT" = "true" ]]; then # Check if encryption option active.
1005 if checkapp age; then # Check that age is available.
320ac29c 1006 for pubkey in "${argRecPubKeys[@]}"; do # Validate recipient pubkey strings by forming test message
238775e6 1007 vbm "DEBUG:Testing pubkey string:$pubkey";
320ac29c 1008 if checkAgePubkey "$pubkey" && \
d6ba4173 1009 ( validateInput "$pubkey" "ssh_pubkey" || validateInput "$pubkey" "age_pubkey"); then
ff22a93f 1010 #### Form age recipient string
be9ef23e 1011 recipients="$recipients""-r '$pubkey' ";
405ce7df 1012 vbm "STATUS:Added pubkey for forming age recipient string:""$pubkey";
77361307 1013 vbm "DEBUG:recipients:""$recipients";
ff22a93f
SBS
1014 #### Add validated pubkey to recPubKeysValid array
1015 recPubKeysValid+=("$pubkey") && vbm "DEBUG:recPubkeysValid:pubkey added:$pubkey";
408a342b
SBS
1016 else
1017 yell "ERROR:Exit code ""$?"". Invalid recipient pubkey string. Exiting."; exit 1;
77361307 1018 fi
17d49005 1019 done
320ac29c 1020 vbm "DEBUG:Finished processing argRecPubKeys array";
ff22a93f
SBS
1021
1022 ## Form age command string
be9ef23e
SBS
1023 CMD_ENCRYPT="age ""$recipients " && vbm "CMD_ENCRYPT:$CMD_ENCRYPT";
1024 CMD_ENCRYPT_SUFFIX=".age" && vbm "CMD_ENCRYPT_SUFFIX:$CMD_ENCRYPT_SUFFIX";
17d49005
SBS
1025 else
1026 yell "ERROR:Encryption enabled but \"age\" not found. Exiting."; exit 1;
1027 fi
408a342b 1028 else
be9ef23e
SBS
1029 CMD_ENCRYPT="tee /dev/null " && vbm "CMD_ENCRYPT:$CMD_ENCRYPT";
1030 CMD_ENCRYPT_SUFFIX="" && vbm "CMD_ENCRYPT_SUFFIX:$CMD_ENCRYPT_SUFFIX";
408a342b 1031 vbm "DEBUG:Encryption not enabled."
320ac29c
SBS
1032 fi
1033} # Populate recPubKeysValid with argRecPubKeys; form encryption cmd string and filename suffix
1034magicParseCompressionArg() {
1035 # Desc: Parses compression arguments specified by '-c' option
1036 # Input: vars: OPTION_COMPRESS
1037 # Output: CMD_COMPRESS, CMD_COMPRESS_SUFFIX
1038 # Depends: checkapp(), vbm(), gzip,
408a342b
SBS
1039 if [[ "$OPTION_COMPRESS" = "true" ]]; then # Check if compression option active
1040 if checkapp gzip; then # Check if gzip available
be9ef23e
SBS
1041 CMD_COMPRESS="gzip " && vbm "CMD_COMPRESS:$CMD_COMPRESS";
1042 CMD_COMPRESS_SUFFIX=".gz" && vbm "CMD_COMPRESS_SUFFIX:$CMD_COMPRESS_SUFFIX";
408a342b
SBS
1043 else
1044 yell "ERROR:Compression enabled but \"gzip\" not found. Exiting."; exit 1;
286d9825 1045 fi
408a342b 1046 else
be9ef23e
SBS
1047 CMD_COMPRESS="tee /dev/null " && vbm "CMD_COMPRESS:$CMD_COMPRESS";
1048 CMD_COMPRESS_SUFFIX="" && vbm "CMD_COMPRESS_SUFFIX:$CMD_COMPRESS_SUFFIX";
1b0e6227 1049 vbm "DEBUG:Compression not enabled.";
320ac29c
SBS
1050 fi
1051} # Form compression cmd string and filename suffix
1052magicInitWorkingDir() {
1053 # Desc: Determine temporary working directory from defaults or user input
1054 # Input: vars: OPTION_TEMPDIR, argTmpDirPriority, DIR_TMP_DEFAULT
1055 # Input: vars: SCRIPT_TIME_START
1056 # Output: vars: DIR_TMP
1057 # Depends: processArguments(), vbm(), yell()
1058 # Parse '-t' option (user-specified temporary working dir)
1059 ## Set DIR_TMP_PARENT to user-specified value if specified
1060 local DIR_TMP_PARENT
1061
1062 if [[ "$OPTION_TMPDIR" = "true" ]]; then
1063 if [[ -d "$argTempDirPriority" ]]; then
1064 DIR_TMP_PARENT="$argTempDirPriority";
1065 else
1066 yell "WARNING:Specified temporary working directory not valid:$OPTION_TMPDIR";
1067 exit 1; # Exit since user requires a specific temp dir and it is not available.
1068 fi;
1069 else
1070 ## Set DIR_TMP_PARENT to default or fallback otherwise
1071 if [[ -d "$DIR_TMP_DEFAULT" ]]; then
1072 DIR_TMP_PARENT="$DIR_TMP_DEFAULT";
1073 elif [[ -d /tmp ]]; then
1074 yell "WARNING:$DIR_TMP_DEFAULT not available. Falling back to /tmp .";
1075 DIR_TMP_PARENT="/tmp";
1076 else
1077 yell "ERROR:No valid working directory available. Exiting.";
1078 exit 1;
1079 fi
1080 fi;
1081 ## Set DIR_TMP using DIR_TMP_PARENT and nonce (SCRIPT_TIME_START)
1082 DIR_TMP="$DIR_TMP_PARENT"/"$SCRIPT_TIME_START""..bkgpslog" && vbm "DEBUG:Set DIR_TMP to:$DIR_TMP"; # Note: removed at end of main().
1083} # Sets working dir
032f4b05 1084
320ac29c
SBS
1085main() {
1086 # Process arguments
1087 processArguments "$@";
1088 ## Act upon arguments
1089 ### Determine working directory
1090 magicInitWorkingDir;
1091 ### Set output encryption and compression option strings
1092 #### React to "-r" ("encryption recipients") option
1093 magicParseRecipientArgs;
1094 #### React to "-c" ("compression") option
1095 magicParseCompressionArg;
1096 #### React to "-R" ("recipient directory") option
1097 magicParseRecipientDir;
1098
1099 # Check that critical apps and dirs are available, display missing ones.
1100 if ! checkapp gpspipe tar && ! checkdir "$DIR_OUT" "DIR_TMP"; then
6c30388f
SBS
1101 yell "ERROR:Critical components missing.";
1102 displayMissing; yell "Exiting."; exit 1; fi
1103
1104 # Set script lifespan
3395ae22 1105 setScriptTTL "$SCRIPT_TTL"; # seconds until next new SCRIPT_TTL (ex: "day" or "hour")
6c30388f
SBS
1106
1107 # File name substring: encoded bufferTTL
1108 bufferTTL_STR="$(timeDuration $BUFFER_TTL)";
1109
1110 # Init temp working dir
3a7b92f8 1111 try mkdir "$DIR_TMP" && vbm "DEBUG:Working dir creatd at:$DIR_TMP";
6c30388f
SBS
1112
1113 # Initialize 'tar' archive
1114 ## Define output tar path (note: each day gets *one* tar file (Ex: "20200731..hostname_location.[.gpx.gz].tar"))
9efeccb5
SBS
1115 PATHOUT_TAR="$DIR_OUT"/"$(dateShort)".."$SCRIPT_HOSTNAME""_location""$CMD_COMPRESS_SUFFIX""$CMD_ENCRYPT_SUFFIX".tar && \
1116 vbm "STATUS:Set PATHOUT_TAR to:$PATHOUT_TAR";
f6fb18bd
SBS
1117 ## Check that PATHOUT_TAR is a tar. Rename old and create empty one otherwise.
1118 checkMakeTar "$PATHOUT_TAR" && vbm "DEBUG:Confirmed or Created to be a tar:$PATHOUT_TAR";
6c30388f 1119 ## Append VERSION file to PATHOUT_TAR
3395ae22 1120 magicWriteVersion;
e47e8048
SBS
1121
1122 # Define GPS conversion commands
1123 CMD_CONV_NMEA="tee /dev/null " && vbm "STATUS:Set CMD_CONV_NMEA to:$CMD_CONV_NMEA"; # tee as passthrough
1124 CMD_CONV_GPX="gpsbabel -i nmea -f - -o gpx -F - " && vbm "STATUS:Set CMD_CONV_GPX to:$CMD_CONV_GPX"; # convert NMEA to GPX
1125 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 1126
e47e8048 1127 # MAIN LOOP:Record gps data until script lifespan ends
6c30388f 1128 while [[ "$SECONDS" -lt "$scriptTTL" ]]; do
811f50f2 1129 magicGatherWriteBuffer &
238775e6 1130 sleep "$BUFFER_TTL";
811f50f2 1131 done
cfc25c90 1132
e47e8048
SBS
1133 # Cleanup
1134 ## Remove DIR_TMP
cfc25c90 1135 try rm -r "$DIR_TMP";
0aabe6a3 1136
1b0e6227 1137 vbm "STATUS:Main function finished.";
8fbca23d
SBS
1138} # Main function.
1139#===END Declare local script functions===
1140#==END Define script parameters==
032f4b05
SBS
1141
1142
8fbca23d
SBS
1143#==BEGIN Perform work and exit==
1144main "$@" # Run main function.
1145exit 0;
1146#==END Perform work and exit==