fix(bkgpslog):Check -e, -r, -R option combinations; update usage
[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.
6a2cd888
SBS
212 -e | --encrypt) OPTION_ENCRYPT="true"; vbm "DEBUG:Encrypted output mode enabled.";; # Enable encryption
213 -r | --recipient) OPTION_RECIPIENTS="true"; argRecPubKeys+=("$2"); vbm "STATUS:pubkey added:""$2"; shift;; # Add recipients
214 -c | --compress) OPTION_COMPRESS="true"; vbm "DEBUG:Compressed output mode enabled.";; # Enable compression
215 -z | --time-zone) try setTimeZoneEV "$2"; shift;; # Set timestamp timezone
216 -t | --temp-dir) OPTION_TMPDIR="true" && argTmpDirPriority="$2"; shift;; # Set time zone
217 -R | --recipient-dir) OPTION_RECIPIENTS="true"; OPTION_RECDIR="true" && argRecDir="$2"; shift;; # Add recipient watch dir
b0da06ca 218 *) echoerr "ERROR: Unrecognized argument: $1"; echoerr "STATUS:All arguments:$*"; exit 1;; # Handle unrecognized options.
032f4b05
SBS
219 esac
220 shift
221 done
222} # Argument Processing
6c30388f
SBS
223setTimeZoneEV(){
224 # Desc: Set time zone environment variable TZ
225 # Usage: setTimeZoneEV arg1
226 # Input: arg1: 'date'-compatible timezone string (ex: "America/New_York")
227 # TZDIR env var (optional; default: "/usr/share/zoneinfo")
228 # Output: exports TZ
229 # exit code 0 on success
230 # exit code 1 on incorrect number of arguments
231 # exit code 2 if unable to validate arg1
232 # Depends: yell, printenv, bash 5
233 # Tested on: Debian 10
234 ARG1="$1"
235 local tzDir returnState
236 if ! [[ $# -eq 1 ]]; then
237 yell "ERROR:Invalid argument count.";
238 return 1;
239 fi
240
241 # Read TZDIR env var if available
242 if printenv TZDIR 1>/dev/null 2>&1; then
243 tzDir="$(printenv TZDIR)";
244 else
245 tzDir="/usr/share/zoneinfo";
246 fi
247
248 # Validate TZ string
249 if ! [[ -f "$tzDir"/"$ARG1" ]]; then
250 yell "ERROR:Invalid time zone argument.";
251 return 2;
252 else
253 # Export ARG1 as TZ environment variable
254 TZ="$ARG1" && export TZ && returnState="true";
255 fi
256
257 # Determine function return code
258 if [ "$returnState" = "true" ]; then
259 return 0;
260 fi
261} # Exports TZ environment variable
c2aaff78
SBS
262timeUntilNextDay(){
263 # Desc: Report seconds until next day.
3395ae22 264 # Version: 1.0.0
c2aaff78 265 # Output: stdout: integer seconds until next day
8fbca23d 266 # Output: exit code 0 if stdout > 0; 1 if stdout = 0; 2 if stdout < 0
c2aaff78
SBS
267 # Usage: timeUntilNextDay
268 # Usage: if ! myTTL="$(timeUntilNextDay)"; then yell "ERROR in if statement"; exit 1; fi
3395ae22
SBS
269 # Depends: date 8, echo 8, yell, try
270
c2aaff78 271 local returnState TIME_CURRENT TIME_NEXT_DAY SECONDS_UNTIL_NEXT_DAY
3395ae22 272
8fbca23d 273 TIME_CURRENT="$(date --iso-8601=seconds)" ; # Produce `date`-parsable current timestamp with resolution of 1 second.
c2aaff78
SBS
274 TIME_NEXT_DAY="$(date -d "$TIME_CURRENT next day" --iso-8601=date)"; # Produce timestamp of beginning of tomorrow with resolution of 1 second.
275 SECONDS_UNTIL_NEXT_DAY="$(( $(date +%s -d "$TIME_NEXT_DAY") - $(date +%s -d "$TIME_CURRENT") ))" ; # Calculate seconds until closest future midnight (res. 1 second).
276 if [[ "$SECONDS_UNTIL_NEXT_DAY" -gt 0 ]]; then
277 returnState="true";
278 elif [[ "$SECONDS_UNTIL_NEXT_DAY" -eq 0 ]]; then
3395ae22 279 returnState="warning_zero";
c2aaff78
SBS
280 yell "WARNING:Reported time until next day exactly zero.";
281 elif [[ "$SECONDS_UNTIL_NEXT_DAY" -lt 0 ]]; then
3395ae22 282 returnState="warning_negative";
c2aaff78
SBS
283 yell "WARNING:Reported time until next day is negative.";
284 fi
285
286 try echo "$SECONDS_UNTIL_NEXT_DAY"; # Report
287
3395ae22 288 # Determine function return code
c2aaff78
SBS
289 if [[ "$returnState" = "true" ]]; then
290 return 0;
3395ae22 291 elif [[ "$returnState" = "warning_zero" ]]; then
c2aaff78 292 return 1;
3395ae22 293 elif [[ "$returnState" = "warning_negative" ]]; then
c2aaff78
SBS
294 return 2;
295 fi
296} # Report seconds until next day
297timeUntilNextHour(){
298 # Desc: Report seconds until next hour
3395ae22 299 # Version 1.0.0
c2aaff78
SBS
300 # Output: stdout: integer seconds until next hour
301 # Output: exit code 0 if stdout > 0; 1 if stdout = 0; 2 if stdout < 0
302 # Usage: timeUntilNextHour
303 # Usage: if ! myTTL="$(timeUntilNextHour)"; then yell "ERROR in if statement"; exit 1; fi
3395ae22 304
c2aaff78
SBS
305 local returnState TIME_CURRENT TIME_NEXT_HOUR SECONDS_UNTIL_NEXT_HOUR
306 TIME_CURRENT="$(date --iso-8601=seconds)"; # Produce `date`-parsable current timestamp with resolution of 1 second.
307 TIME_NEXT_HOUR="$(date -d "$TIME_CURRENT next hour" --iso-8601=hours)"; # Produce `date`-parsable current time stamp with resolution of 1 second.
308 SECONDS_UNTIL_NEXT_HOUR="$(( $(date +%s -d "$TIME_NEXT_HOUR") - $(date +%s -d "$TIME_CURRENT") ))"; # Calculate seconds until next hour (res. 1 second).
309 if [[ "$SECONDS_UNTIL_NEXT_HOUR" -gt 0 ]]; then
8fbca23d 310 returnState="true";
c2aaff78 311 elif [[ "$SECONDS_UNTIL_NEXT_HOUR" -eq 0 ]]; then
3395ae22 312 returnState="warning_zero";
c2aaff78
SBS
313 yell "WARNING:Reported time until next hour exactly zero.";
314 elif [[ "$SECONDS_UNTIL_NEXT_HOUR" -lt 0 ]]; then
3395ae22 315 returnState="warning_negative";
c2aaff78 316 yell "WARNING:Reported time until next hour is negative.";
8fbca23d 317 fi
032f4b05 318
c2aaff78 319 try echo "$SECONDS_UNTIL_NEXT_HOUR"; # Report
8fbca23d 320
3395ae22 321 # Determine function return code
8fbca23d
SBS
322 if [[ "$returnState" = "true" ]]; then
323 return 0;
3395ae22 324 elif [[ "$returnState" = "warning_zero" ]]; then
8fbca23d 325 return 1;
3395ae22 326 elif [[ "$returnState" = "warning_negative" ]]; then
8fbca23d
SBS
327 return 2;
328 fi
c2aaff78 329} # Report seconds until next hour
8fbca23d
SBS
330dateTimeShort(){
331 # Desc: Timestamp without separators (YYYYmmddTHHMMSS+zzzz)
e47e8048
SBS
332 # Usage: dateTimeShort ([str date])
333 # Version 1.1.0
334 # Input: arg1: 'date'-parsable timestamp string (optional)
8fbca23d 335 # Output: stdout: timestamp (ISO-8601, no separators)
e47e8048 336 # Depends: yell
c2aaff78 337 local TIME_CURRENT TIME_CURRENT_SHORT
e47e8048
SBS
338
339 argTime="$1";
340 # Get Current Time
8fbca23d 341 TIME_CURRENT="$(date --iso-8601=seconds)" ; # Produce `date`-parsable current timestamp with resolution of 1 second.
e47e8048
SBS
342 # Decide to parse current or supplied date
343 ## Check if time argument empty
344 if [[ -z "$argTime" ]]; then
345 ## T: Time argument empty, use current time
346 TIME_INPUT="$TIME_CURRENT";
347 else
348 ## F: Time argument exists, validate time
349 if date --date="$argTime" 1>/dev/null 2>&1; then
350 ### T: Time argument is valid; use it
351 TIME_INPUT="$argTime";
352 else
353 ### F: Time argument not valid; exit
354 yell "ERROR:Invalid time argument supplied. Exiting."; exit 1;
355 fi
356 fi
357 # Construct and deliver separator-les date string
358 TIME_CURRENT_SHORT="$(date -d "$TIME_INPUT" +%Y%m%dT%H%M%S%z)";
8fbca23d 359 echo "$TIME_CURRENT_SHORT";
6c30388f
SBS
360} # Get YYYYmmddTHHMMSS±zzzz
361dateShort(){
362 # Desc: Date without separators (YYYYmmdd)
e47e8048
SBS
363 # Usage: dateShort ([str date])
364 # Version: 1.1.0
365 # Input: arg1: 'date'-parsable timestamp string (optional)
6c30388f 366 # Output: stdout: date (ISO-8601, no separators)
e47e8048 367 # Depends: yell
6c30388f 368 local TIME_CURRENT DATE_CURRENT_SHORT
e47e8048
SBS
369
370 argTime="$1";
371 # Get Current Time
6c30388f 372 TIME_CURRENT="$(date --iso-8601=seconds)" ; # Produce `date`-parsable current timestamp with resolution of 1 second.
e47e8048
SBS
373 # Decide to parse current or supplied date
374 ## Check if time argument empty
375 if [[ -z "$argTime" ]]; then
376 ## T: Time argument empty, use current time
377 TIME_INPUT="$TIME_CURRENT";
378 else
379 ## F: Time argument exists, validate time
380 if date --date="$argTime" 1>/dev/null 2>&1; then
381 ### T: Time argument is valid; use it
382 TIME_INPUT="$argTime";
383 else
384 ### F: Time argument not valid; exit
385 yell "ERROR:Invalid time argument supplied. Exiting."; exit 1;
386 fi
387 fi
388 # Construct and deliver separator-les date string
389 DATE_CURRENT_SHORT="$(date -d "$TIME_INPUT" +%Y%m%d)"; # Produce separator-less current date with resolution 1 day.
6c30388f
SBS
390 echo "$DATE_CURRENT_SHORT";
391} # Get YYYYmmdd
392timeDuration(){
a7b4a273 393 # Desc: Given seconds, output ISO-8601 duration string
6c30388f
SBS
394 # Ref/Attrib: ISO-8601:2004(E), §4.4.4.2 Representations of time intervals by duration and context information
395 # Note: "1 month" ("P1M") is assumed to be "30 days" (see ISO-8601:2004(E), §2.2.1.2)
a7b4a273
SBS
396 # Usage: timeDuration [1:seconds] ([2:precision])
397 # Version: 1.0.3
6c30388f
SBS
398 # Input: arg1: seconds as base 10 integer >= 0 (ex: 3601)
399 # arg2: precision level (optional; default=2)
400 # Output: stdout: ISO-8601 duration string (ex: "P1H1S", "P2Y10M15DT10H30M20S")
a7b4a273
SBS
401 # exit code 0: success
402 # exit code 1: error_input
403 # exit code 2: error_unknown
6c30388f 404 # Example: 'timeDuration 111111 3' yields 'P1DT6H51M'
3395ae22 405 # Depends: date 8 (gnucoreutils), yell,
a7b4a273 406 local returnState argSeconds argPrecision remainder precision witherPrecision
3395ae22
SBS
407 local fullYears fullMonths fullDays fullHours fullMinutes fullSeconds
408 local displayYears displayMonths displayDays displayHours displayMinutes displaySeconds
409 local hasYears hasMonths hasDays hasHours hasMinutes hasSeconds
410
a7b4a273
SBS
411 argSeconds="$1"; # read arg1 (seconds)
412 argPrecision="$2"; # read arg2 (precision)
6c30388f 413 precision=2; # set default precision
6c30388f
SBS
414
415 # Check that between one and two arguments is supplied
416 if ! { [[ $# -ge 1 ]] && [[ $# -le 2 ]]; }; then
417 yell "ERROR:Invalid number of arguments:$# . Exiting.";
a7b4a273 418 returnState="error_input"; fi
6c30388f 419
a7b4a273 420 # Check that argSeconds provided
6c30388f 421 if [[ $# -ge 1 ]]; then
a7b4a273
SBS
422 ## Check that argSeconds is a positive integer
423 if [[ "$argSeconds" =~ ^[[:digit:]]+$ ]]; then
3395ae22 424 :
6c30388f 425 else
a7b4a273
SBS
426 yell "ERROR:argSeconds not a digit.";
427 returnState="error_input";
6c30388f
SBS
428 fi
429 else
430 yell "ERROR:No argument provided. Exiting.";
431 exit 1;
432 fi
433
a7b4a273 434 # Consider whether argPrecision was provided
6c30388f 435 if [[ $# -eq 2 ]]; then
a7b4a273
SBS
436 # Check that argPrecision is a positive integer
437 if [[ "$argPrecision" =~ ^[[:digit:]]+$ ]] && [[ "$argPrecision" -gt 0 ]]; then
438 precision="$argPrecision";
6c30388f 439 else
a7b4a273
SBS
440 yell "ERROR:argPrecision not a positive integer. (is $argPrecision ). Leaving early.";
441 returnState="error_input";
6c30388f
SBS
442 fi;
443 else
3395ae22 444 :
6c30388f
SBS
445 fi;
446
a7b4a273 447 remainder="$argSeconds" ; # seconds
6c30388f
SBS
448 ## Calculate full years Y, update remainder
449 fullYears=$(( remainder / (365*24*60*60) ));
450 remainder=$(( remainder - (fullYears*365*24*60*60) ));
451 ## Calculate full months M, update remainder
452 fullMonths=$(( remainder / (30*24*60*60) ));
453 remainder=$(( remainder - (fullMonths*30*24*60*60) ));
454 ## Calculate full days D, update remainder
455 fullDays=$(( remainder / (24*60*60) ));
456 remainder=$(( remainder - (fullDays*24*60*60) ));
457 ## Calculate full hours H, update remainder
458 fullHours=$(( remainder / (60*60) ));
459 remainder=$(( remainder - (fullHours*60*60) ));
460 ## Calculate full minutes M, update remainder
461 fullMinutes=$(( remainder / (60) ));
462 remainder=$(( remainder - (fullMinutes*60) ));
463 ## Calculate full seconds S, update remainder
464 fullSeconds=$(( remainder / (1) ));
465 remainder=$(( remainder - (remainder*1) ));
466 ## Check which fields filled
467 if [[ $fullYears -gt 0 ]]; then hasYears="true"; else hasYears="false"; fi
468 if [[ $fullMonths -gt 0 ]]; then hasMonths="true"; else hasMonths="false"; fi
469 if [[ $fullDays -gt 0 ]]; then hasDays="true"; else hasDays="false"; fi
470 if [[ $fullHours -gt 0 ]]; then hasHours="true"; else hasHours="false"; fi
471 if [[ $fullMinutes -gt 0 ]]; then hasMinutes="true"; else hasMinutes="false"; fi
472 if [[ $fullSeconds -gt 0 ]]; then hasSeconds="true"; else hasSeconds="false"; fi
473
474 ## Determine which fields to display (see ISO-8601:2004 §4.4.3.2)
475 witherPrecision="false"
476
477 ### Years
478 if $hasYears && [[ $precision -gt 0 ]]; then
479 displayYears="true";
480 witherPrecision="true";
481 else
482 displayYears="false";
483 fi;
484 if $witherPrecision; then ((precision--)); fi;
485
486 ### Months
487 if $hasMonths && [[ $precision -gt 0 ]]; then
488 displayMonths="true";
489 witherPrecision="true";
490 else
491 displayMonths="false";
492 fi;
493 if $witherPrecision && [[ $precision -gt 0 ]]; then
494 displayMonths="true";
495 fi;
496 if $witherPrecision; then ((precision--)); fi;
497
498 ### Days
499 if $hasDays && [[ $precision -gt 0 ]]; then
500 displayDays="true";
501 witherPrecision="true";
502 else
503 displayDays="false";
504 fi;
505 if $witherPrecision && [[ $precision -gt 0 ]]; then
506 displayDays="true";
507 fi;
508 if $witherPrecision; then ((precision--)); fi;
509
510 ### Hours
511 if $hasHours && [[ $precision -gt 0 ]]; then
512 displayHours="true";
513 witherPrecision="true";
514 else
515 displayHours="false";
516 fi;
517 if $witherPrecision && [[ $precision -gt 0 ]]; then
518 displayHours="true";
519 fi;
520 if $witherPrecision; then ((precision--)); fi;
521
522 ### Minutes
523 if $hasMinutes && [[ $precision -gt 0 ]]; then
524 displayMinutes="true";
525 witherPrecision="true";
526 else
527 displayMinutes="false";
528 fi;
529 if $witherPrecision && [[ $precision -gt 0 ]]; then
530 displayMinutes="true";
531 fi;
532 if $witherPrecision; then ((precision--)); fi;
533
534 ### Seconds
535
536 if $hasSeconds && [[ $precision -gt 0 ]]; then
537 displaySeconds="true";
538 witherPrecision="true";
539 else
540 displaySeconds="false";
541 fi;
542 if $witherPrecision && [[ $precision -gt 0 ]]; then
543 displaySeconds="true";
544 fi;
545 if $witherPrecision; then ((precision--)); fi;
546
6c30388f
SBS
547 ## Determine whether or not the "T" separator is needed to separate date and time elements
548 if ( $displayHours || $displayMinutes || $displaySeconds); then
549 displayDateTime="true"; else displayDateTime="false"; fi
550
551 ## Construct duration output string
552 OUTPUT="P"
553 if $displayYears; then
554 OUTPUT=$OUTPUT$fullYears"Y"; fi
555 if $displayMonths; then
556 OUTPUT=$OUTPUT$fullMonths"M"; fi
557 if $displayDays; then
558 OUTPUT=$OUTPUT$fullDays"D"; fi
559 if $displayDateTime; then
560 OUTPUT=$OUTPUT"T"; fi
561 if $displayHours; then
562 OUTPUT=$OUTPUT$fullHours"H"; fi
563 if $displayMinutes; then
564 OUTPUT=$OUTPUT$fullMinutes"M"; fi
565 if $displaySeconds; then
566 OUTPUT=$OUTPUT$fullSeconds"S"; fi
567
568 ## Output duration string to stdout
a7b4a273 569 echo "$OUTPUT" && returnState="true";
6c30388f
SBS
570
571 #===Determine function return code===
572 if [ "$returnState" = "true" ]; then
573 return 0;
a7b4a273
SBS
574 elif [ "$returnState" = "error_input" ]; then
575 yell "ERROR:input";
6c30388f 576 return 1;
a7b4a273
SBS
577 else
578 yell "ERROR:Unknown";
579 return 2;
6c30388f
SBS
580 fi
581
582} # Get duration (ex: PT10M4S )
583displayMissing() {
584 # Desc: Displays missing apps, files, and dirs
585 # Usage: displayMissing
586 # Input: associative arrays: appRollCall, fileRollCall, dirRollCall
587 # Output: stderr messages
588 #==BEGIN Display errors==
589 #===BEGIN Display Missing Apps===
590 missingApps="Missing apps :"
591 #for key in "${!appRollCall[@]}"; do echo "DEBUG:$key => ${appRollCall[$key]}"; done
592 for key in "${!appRollCall[@]}"; do
593 value="${appRollCall[$key]}"
594 if [ "$value" = "false" ]; then
595 #echo "DEBUG:Missing apps: $key => $value";
596 missingApps="$missingApps""$key "
597 appMissing="true"
598 fi
599 done
600 if [ "$appMissing" = "true" ]; then # Only indicate if an app is missing.
601 echo "$missingApps" 1>&2;
602 fi
603 #===END Display Missing Apps===
604
605 #===BEGIN Display Missing Files===
606 missingFiles="Missing files:"
607 #for key in "${!fileRollCall[@]}"; do echo "DEBUG:$key => ${fileRollCall[$key]}"; done
608 for key in "${!fileRollCall[@]}"; do
609 value="${fileRollCall[$key]}"
610 if [ "$value" = "false" ]; then
611 #echo "DEBUG:Missing files: $key => $value";
612 missingFiles="$missingFiles""$key "
613 fileMissing="true"
614 fi
615 done
616 if [ "$fileMissing" = "true" ]; then # Only indicate if an app is missing.
617 echo "$missingFiles" 1>&2;
618 fi
619 #===END Display Missing Files===
620
621 #===BEGIN Display Missing Directories===
622 missingDirs="Missing dirs:"
623 #for key in "${!dirRollCall[@]}"; do echo "DEBUG:$key => ${dirRollCall[$key]}"; done
624 for key in "${!dirRollCall[@]}"; do
625 value="${dirRollCall[$key]}"
626 if [ "$value" = "false" ]; then
627 #echo "DEBUG:Missing dirs: $key => $value";
628 missingDirs="$missingDirs""$key "
629 dirMissing="true"
630 fi
631 done
632 if [ "$dirMissing" = "true" ]; then # Only indicate if an dir is missing.
633 echo "$missingDirs" 1>&2;
634 fi
635 #===END Display Missing Directories===
636
637 #==END Display errors==
638} # Display missing apps, files, dirs
639setScriptTTL() {
640 #Desc: Sets script TTL
641 #Usage: setScriptTTL arg1
642 #Input: arg1: "day" or "hour"
643 #Output: scriptTTL
644 #Depends: timeUntilNextHour or timeUntilNextDay
645 local ARG1
646 ARG1="$1"
647 if [[ "$ARG1" = "day" ]]; then
648 # Set script lifespan to end at start of next day
649 if ! scriptTTL="$(timeUntilNextDay)"; then
650 if [[ "$scriptTTL" -eq 0 ]]; then
651 ((scriptTTL++)); # Add 1 because 0 would cause 'timeout' to never timeout.
652 else
653 yell "ERROR: timeUntilNextDay exit code $?"; exit 1;
654 fi;
655 fi;
656 elif [[ "$ARG1" = "hour" ]]; then
657 # Set script lifespan to end at start of next hour
658 if ! scriptTTL="$(timeUntilNextHour)"; then
659 if [[ "$scriptTTL" -eq 0 ]]; then
660 ((scriptTTL++)); # Add 1 because 0 would cause 'timeout' to never timeout.
661 else
662 yell "ERROR: timeUntilNextHour exit code $?"; exit 1;
663 fi;
664 fi;
665 else
666 yell "ERROR:Invalid argument for setScriptTTL function."; exit 1;
667 fi
668} # Seconds until next (day|hour).
f6fb18bd
SBS
669checkMakeTar() {
670 # Desc: Checks that a valid tar archive exists, creates one otherwise
671 # Usage: checkMakeTar [ path ]
2fc10824 672 # Version: 1.0.1
f6fb18bd 673 # Input: arg1: path of tar archive
2fc10824
SBS
674 # Output: exit code 0 : tar readable
675 # exit code 1 : tar missing; created
676 # exit code 2 : tar not readable; moved; replaced
f6fb18bd 677 # Depends: try, tar, date
2fc10824
SBS
678 local PATH_TAR returnFlag0 returnFlag1 returnFlag2
679 PATH_TAR="$1"
f6fb18bd
SBS
680
681 # Check if file is a valid tar archive
682 if tar --list --file="$PATH_TAR" 1>/dev/null 2>&1; then
683 ## T1: return success
2fc10824 684 returnFlag0="tar valid";
f6fb18bd
SBS
685 else
686 ## F1: Check if file exists
687 if [[ -f "$PATH_TAR" ]]; then
688 ### T: Rename file
2fc10824
SBS
689 try mv "$PATH_TAR" "$PATH_TAR""--broken--""$(date +%Y%m%dT%H%M%S)" && \
690 returnFlag1="tar moved";
f6fb18bd
SBS
691 else
692 ### F: -
693 :
694 fi
695 ## F2: Create tar archive, return 0
2fc10824
SBS
696 try tar --create --file="$PATH_TAR" --files-from=/dev/null && \
697 returnFlag2="tar created";
698 fi
699
700 # Determine function return code
701 if [[ "$returnFlag0" = "tar valid" ]]; then
f6fb18bd 702 return 0;
2fc10824
SBS
703 elif [[ "$returnFlag2" = "tar created" ]] && ! [[ "$returnFlag1" = "tar moved" ]]; then
704 return 1; # tar missing so created
705 elif [[ "$returnFlag2" = "tar created" ]] && [[ "$returnFlag1" = "tar moved" ]]; then
706 return 2; # tar not readable so moved; replaced
f6fb18bd
SBS
707 fi
708} # checks if arg1 is tar; creates one otherwise
66af6225
SBS
709appendArgTar(){
710 # Desc: Writes first argument to temporary file with arguments as options, then appends file to tar
e47e8048 711 # Usage: appendArgTar "$(echo "Data to be written.")" [name of file to be inserted] [tar path] [temp dir] ([cmd1] [cmd2] [cmd3] [cmd4]...)
f665ce95 712 # Version: 1.0.3
66af6225
SBS
713 # Input: arg1: data to be written
714 # arg2: file name of file to be inserted into tar
715 # arg3: tar archive path (must exist first)
716 # arg4: temporary working dir
717 # arg5+: command strings (ex: "gpsbabel -i nmea -f - -o kml -F - ")
718 # Output: file written to disk
719 # Example: decrypt multiple large files in parallel
720 # appendArgTar "$(cat /tmp/largefile1.gpg)" "largefile1" $HOME/archive.tar /tmp "gpg --decrypt" &
721 # appendArgTar "$(cat /tmp/largefile2.gpg)" "largefile2" $HOME/archive.tar /tmp "gpg --decrypt" &
722 # appendArgTar "$(cat /tmp/largefile3.gpg)" "largefile3" $HOME/archive.tar /tmp "gpg --decrypt" &
66af6225 723 # Depends: bash 5
f665ce95 724 # Ref/Attrib: Using 'eval' to construct command strings https://askubuntu.com/a/476533
66af6225 725
d9fe1eba 726 # Save function name
d7138c7f
SBS
727 local FN="${FUNCNAME[0]}";
728 #yell "DEBUG:STATUS:$FN:Finished appendArgTar()."
d9fe1eba 729
66af6225 730 # Set file name
d9fe1eba 731 if ! [ -z "$2" ]; then FILENAME="$2"; else yell "ERROR:$FN:Not enough arguments."; exit 1; fi
66af6225
SBS
732
733 # Check tar path is a file
d9fe1eba 734 if [ -f "$3" ]; then TAR_PATH="$3"; else yell "ERROR:$FN:Tar archive arg not a file."; exit 1; fi
66af6225
SBS
735
736 # Check temp dir arg
d9fe1eba 737 if ! [ -z "$4" ]; then TMP_DIR="$4"; else yell "ERROR:$FN:No temporary working dir set."; exit 1; fi
66af6225
SBS
738
739 # Set command strings
740 if ! [ -z "$5" ]; then CMD1="$5"; else CMD1="tee /dev/null "; fi # command string 1
741 if ! [ -z "$6" ]; then CMD2="$6"; else CMD2="tee /dev/null "; fi # command string 2
742 if ! [ -z "$7" ]; then CMD3="$7"; else CMD3="tee /dev/null "; fi # command string 3
743 if ! [ -z "$8" ]; then CMD4="$8"; else CMD4="tee /dev/null "; fi # command string 4
744
f665ce95
SBS
745 # Input command
746 CMD0="echo \"\$1\""
747
d7138c7f 748 # # Debug
f665ce95 749 # yell "DEBUG:STATUS:$FN:CMD0:$CMD0"
d7138c7f
SBS
750 # yell "DEBUG:STATUS:$FN:CMD1:$CMD1"
751 # yell "DEBUG:STATUS:$FN:CMD2:$CMD2"
752 # yell "DEBUG:STATUS:$FN:CMD3:$CMD3"
753 # yell "DEBUG:STATUS:$FN:CMD4:$CMD4"
754 # yell "DEBUG:STATUS:$FN:FILENAME:$FILENAME"
755 # yell "DEBUG:STATUS:$FN:TAR_PATH:$TAR_PATH"
756 # yell "DEBUG:STATUS:$FN:TMP_DIR:$TMP_DIR"
757
66af6225 758 # Write to temporary working dir
f665ce95 759 eval "$CMD0"" | ""$CMD1"" | ""$CMD2"" | ""$CMD3"" | ""$CMD4" > "$TMP_DIR"/"$FILENAME";
66af6225
SBS
760
761 # Append to tar
762 try tar --append --directory="$TMP_DIR" --file="$TAR_PATH" "$FILENAME";
d7138c7f 763 #yell "DEBUG:STATUS:$FN:Finished appendArgTar()."
66af6225 764} # Append Bash var to file appended to Tar archive
e47e8048
SBS
765appendFileTar(){
766 # Desc: Processes first file and then appends to tar
767 # Usage: appendFileTar [file path] [name of file to be inserted] [tar path] [temp dir] ([cmd1] [cmd2] [cmd3] [cmd4]...)
f665ce95 768 # Version: 1.0.2
e47e8048
SBS
769 # Input: arg1: path of file to be (processed and) written
770 # arg2: name to use for file inserted into tar
771 # arg3: tar archive path (must exist first)
772 # arg4: temporary working dir
773 # arg5+: command strings (ex: "gpsbabel -i nmea -f - -o kml -F - ")
774 # Output: file written to disk
775 # Example: decrypt multiple large files in parallel
776 # appendFileTar /tmp/largefile1.gpg "largefile1" $HOME/archive.tar /tmp "gpg --decrypt" &
777 # appendFileTar /tmp/largefile2.gpg "largefile2" $HOME/archive.tar /tmp "gpg --decrypt" &
778 # appendFileTar /tmp/largefile3.gpg "largefile3" $HOME/archive.tar /tmp "gpg --decrypt" &
779 # Depends: bash 5
780
781 # Save function name
782 local FN="${FUNCNAME[0]}";
783 #yell "DEBUG:STATUS:$FN:Finished appendFileTar()."
784
785 # Set file name
786 if ! [ -z "$2" ]; then FILENAME="$2"; else yell "ERROR:$FN:Not enough arguments."; exit 1; fi
787 # Check tar path is a file
788 if [ -f "$3" ]; then TAR_PATH="$3"; else yell "ERROR:$FN:Tar archive arg not a file."; exit 1; fi
789 # Check temp dir arg
790 if ! [ -z "$4" ]; then TMP_DIR="$4"; else yell "ERROR:$FN:No temporary working dir set."; exit 1; fi
791 # Set command strings
792 if ! [ -z "$5" ]; then CMD1="$5"; else CMD1="tee /dev/null "; fi # command string 1
793 if ! [ -z "$6" ]; then CMD2="$6"; else CMD2="tee /dev/null "; fi # command string 2
794 if ! [ -z "$7" ]; then CMD3="$7"; else CMD3="tee /dev/null "; fi # command string 3
795 if ! [ -z "$8" ]; then CMD4="$8"; else CMD4="tee /dev/null "; fi # command string 4
f665ce95
SBS
796
797 # Input command string
798 CMD0="cat \"\$1\""
799
e47e8048 800 # # Debug
f665ce95 801 # yell "DEBUG:STATUS:$FN:CMD0:$CMD0"
e47e8048
SBS
802 # yell "DEBUG:STATUS:$FN:CMD1:$CMD1"
803 # yell "DEBUG:STATUS:$FN:CMD2:$CMD2"
804 # yell "DEBUG:STATUS:$FN:CMD3:$CMD3"
805 # yell "DEBUG:STATUS:$FN:CMD4:$CMD4"
806 # yell "DEBUG:STATUS:$FN:FILENAME:$FILENAME"
807 # yell "DEBUG:STATUS:$FN:TAR_PATH:$TAR_PATH"
808 # yell "DEBUG:STATUS:$FN:TMP_DIR:$TMP_DIR"
809
810 # Write to temporary working dir
f665ce95 811 eval "$CMD0 | $CMD1 | $CMD2 | $CMD3 | $CMD4" > "$TMP_DIR"/"$FILENAME";
e47e8048
SBS
812
813 # Append to tar
814 try tar --append --directory="$TMP_DIR" --file="$TAR_PATH" "$FILENAME";
815 #yell "DEBUG:STATUS:$FN:Finished appendFileTar()."
816} # Append file to Tar archive
320ac29c
SBS
817checkAgePubkey() {
818 # Desc: Checks if string is an age-compatible pubkey
819 # Usage: checkAgePubkey [str pubkey]
820 # Version: 0.1.2
821 # Input: arg1: string
822 # Output: return code 0: string is age-compatible pubkey
823 # return code 1: string is NOT an age-compatible pubkey
824 # age stderr (ex: there is stderr if invalid string provided)
825 # Depends: age (v0.1.0-beta2; https://github.com/FiloSottile/age/releases/tag/v1.0.0-beta2 )
826
827 argPubkey="$1";
828
829 if echo "test" | age -a -r "$argPubkey" 1>/dev/null; then
830 return 0;
831 else
832 return 1;
833 fi;
834} # Check age pubkey
d6ba4173
SBS
835validateInput() {
836 # Desc: Validates Input
837 # Usage: validateInput [str input] [str input type]
838 # Version: 0.2.1
839 # Input: arg1: string to validate
840 # arg2: string specifying input type (ex:"ssh_pubkey")
841 # Output: return code 0: if input string matched specified string type
842 # Depends: bash 5, yell
843
844 # Save function name
845 local FN="${FUNCNAME[0]}";
846
847 # Process arguments
848 argInput="$1";
849 argType="$2";
850 if [[ $# -gt 2 ]]; then yell "ERROR:$0:$FN:Too many arguments."; exit 1; fi;
851
852 # Check for blank
853 if [[ -z "$argInput" ]]; then return 1; fi
854
855 # Define input types
856 ## ssh_pubkey
857 ### Check for alnum/dash base64 (ex: "ssh-rsa AAAAB3NzaC1yc2EAAA")
858 if [[ "$argType" = "ssh_pubkey" ]]; then
859 if [[ "$argInput" =~ ^[[:alnum:]-]*[\ ]*[[:alnum:]+/=]*$ ]]; then
860 return 0; fi; fi;
861
862 ## age_pubkey
863 ### Check for age1[:bech32:]
864 if [[ "$argType" = "age_pubkey" ]]; then
865 if [[ "$argInput" =~ ^age1[qpzry9x8gf2tvdw0s3jn54khce6mua7l]*$ ]]; then
866 return 0; fi; fi
867
868 # Return error if no condition matched.
869 return 1;
870} # Validates strings
3395ae22
SBS
871magicWriteVersion() {
872 # Desc: Appends time-stamped VERSION to PATHOUT_TAR
873 # Usage: magicWriteVersion
ff22a93f 874 # Version: 0.1.0
3395ae22 875 # Input: CONTENT_VERSION, FILEOUT_VERSION, PATHOUT_TAR, DIR_TMP
ff22a93f
SBS
876 # Input: SCRIPT_VERSION, SCRIPT_URL, AGE_VERSION, AGE_URL, SCRIPT_HOSTNAME
877 # Output: appends tar PATHOUT_TAR
3395ae22 878 # Depends: dateTimeShort, appendArgTar
ff22a93f 879 local CONTENT_VERSION pubKeyIndex
3395ae22 880
ff22a93f 881 # Set VERSION file name
3395ae22 882 FILEOUT_VERSION="$(dateTimeShort)..VERSION";
ff22a93f
SBS
883
884 # Gather VERSION data in CONTENT_VERSION
885 CONTENT_VERSION="SCRIPT_VERSION=$SCRIPT_VERSION";
aa2d7921
SBS
886 #CONTENT_VERSION="$CONTENT_VERSION""\\n";
887 CONTENT_VERSION="$CONTENT_VERSION""\\n""SCRIPT_NAME=$SCRIPT_NAME";
888 CONTENT_VERSION="$CONTENT_VERSION""\\n""SCRIPT_URL=$SCRIPT_URL";
889 CONTENT_VERSION="$CONTENT_VERSION""\\n""AGE_VERSION=$AGE_VERSION";
890 CONTENT_VERSION="$CONTENT_VERSION""\\n""AGE_URL=$AGE_URL";
891 CONTENT_VERSION="$CONTENT_VERSION""\\n""DATE=$(date --iso-8601=seconds)";
892 CONTENT_VERSION="$CONTENT_VERSION""\\n""HOSTNAME=$SCRIPT_HOSTNAME";
ff22a93f
SBS
893 ## Add list of recipient pubkeys
894 for pubkey in "${recPubKeysValid[@]}"; do
895 ((pubKeyIndex++))
aa2d7921 896 CONTENT_VERSION="$CONTENT_VERSION""\\n""PUBKEY_$pubKeyIndex=$pubkey";
ff22a93f
SBS
897 done
898 ## Process newline escapes
4c0c5850
SBS
899 CONTENT_VERSION="$(echo -e "$CONTENT_VERSION")"
900
ff22a93f 901 # Write CONTENT_VERSION as file FILEOUT_VERSION and write-append to PATHOUT_TAR
3395ae22 902 appendArgTar "$CONTENT_VERSION" "$FILEOUT_VERSION" "$PATHOUT_TAR" "$DIR_TMP";
ff22a93f 903
3395ae22 904} # bkgpslog: write version data to PATHOUT_TAR via appendArgTar()
811f50f2 905magicGatherWriteBuffer() {
66af6225 906 # Desc: bkgpslog-specific meta function for writing data to DIR_TMP then appending each file to PATHOUT_TAR
e47e8048
SBS
907 # Inputs: PATHOUT_TAR FILEOUT_{NMEA,GPX,KML} CMD_CONV_{NMEA,GPX,KML} CMD_{COMPRESS,ENCRYPT} DIR_TMP,
908 # Inputs: BUFFER_TTL bufferTTL_STR SCRIPT_HOSTNAME CMD_COMPRESS_SUFFIX CMD_ENCRYPT_SUFFIX
f6fb18bd 909 # Depends: yell, try, vbm, appendArgTar, tar
1b0e6227 910 local FN="${FUNCNAME[0]}";
872c737e 911 wait; # Wait to avoid collision with older magicWriteBuffer() instances (see https://www.tldp.org/LDP/abs/html/x9644.html )
811f50f2
SBS
912 # Create buffer file with unique name
913 PATHOUT_BUFFER="$DIR_TMP/buffer$SECONDS";
914 # Fill buffer
915 timeout "$BUFFER_TTL"s gpspipe -r -o "$PATHOUT_BUFFER" ;
11f61f1a 916 timeBufferStart="$(dateTimeShort "$(date --date="$BUFFER_TTL seconds ago")")"; # Note start time
e47e8048
SBS
917 vbm "DEBUG:STATUS:$FN:Started magicWriteBuffer().";
918 # Determine file paths (time is start of buffer period)
919 FILEOUT_BASENAME="$timeBufferStart""--""$bufferTTL_STR""..""$SCRIPT_HOSTNAME""_location" && vbm "STATUS:Set FILEOUT_BASENAME to:$FILEOUT_BASENAME";
920 ## Files saved to DIR_TMP
921 FILEOUT_NMEA="$FILEOUT_BASENAME".nmea"$CMD_COMPRESS_SUFFIX""$CMD_ENCRYPT_SUFFIX" && vbm "STATUS:Set FILEOUT_NMEA to:$FILEOUT_NMEA";
922 FILEOUT_GPX="$FILEOUT_BASENAME".gpx"$CMD_COMPRESS_SUFFIX""$CMD_ENCRYPT_SUFFIX" && vbm "STATUS:Set FILEOUT_GPX to:$FILEOUT_GPX";
923 FILEOUT_KML="$FILEOUT_BASENAME".kml"$CMD_COMPRESS_SUFFIX""$CMD_ENCRYPT_SUFFIX" && vbm "STATUS:Set FILEOUT_KML to:$FILEOUT_KML";
924 PATHOUT_NMEA="$DIR_TMP"/"$FILEOUT_NMEA" && vbm "STATUS:Set PATHOUT_NMEA to:$PATHOUT_NMEA";
925 PATHOUT_GPX="$DIR_TMP"/"$FILEOUT_GPX" && vbm "STATUS:Set PATHOUT_GPX to:$PATHOUT_GPX";
926 PATHOUT_KML="$DIR_TMP"/"$FILEOUT_KML" && vbm "STATUS:Set PATHOUT_KML to:$PATHOUT_KML";
927 ## Files saved to disk (DIR_OUT)
928 ### one file per day (Ex: "20200731..hostname_location.[.gpx.gz].tar")
929 PATHOUT_TAR="$DIR_OUT"/"$(dateShort "$(date --date="$BUFFER_TTL seconds ago")")".."$SCRIPT_HOSTNAME""_location""$CMD_COMPRESS_SUFFIX""$CMD_ENCRYPT_SUFFIX".tar && \
930 vbm "STATUS:Set PATHOUT_TAR to:$PATHOUT_TAR";
931 # DEBUG: check vars
932 vbm "STATUS:DIR_TMP :$DIR_TMP";
933 vbm "STATUS:PATHOUT_TAR :$PATHOUT_TAR";
934 vbm "STATUS:PATHOUT_NMEA:$PATHOUT_NMEA";
935 vbm "STATUS:PATHOUT_GPX:$PATHOUT_GPX";
936 vbm "STATUS:PATHOUT_KML:$PATHOUT_KML";
937
938
3395ae22
SBS
939 # Validate PATHOUT_TAR as tar.
940 checkMakeTar "$PATHOUT_TAR";
941 ## Add VERSION file if checkMakeTar had to create a tar (exited 1) or replace one (exited 2)
942 if [[ $? -eq 1 ]] || [[ $? -eq 2 ]]; then magicWriteVersion; fi
f6fb18bd
SBS
943
944 # Write bufferBash to PATHOUT_TAR
e47e8048
SBS
945 appendFileTar "$PATHOUT_BUFFER" "$FILEOUT_NMEA" "$PATHOUT_TAR" "$DIR_TMP" "$CMD_CONV_NMEA" "$CMD_COMPRESS" "$CMD_ENCRYPT"; # Write NMEA data
946 appendFileTar "$PATHOUT_BUFFER" "$FILEOUT_GPX" "$PATHOUT_TAR" "$DIR_TMP" "$CMD_CONV_GPX" "$CMD_COMPRESS" "$CMD_ENCRYPT"; # Write GPX file
947 appendFileTar "$PATHOUT_BUFFER" "$FILEOUT_KML" "$PATHOUT_TAR" "$DIR_TMP" "$CMD_CONV_KML" "$CMD_COMPRESS" "$CMD_ENCRYPT"; # Write KML file
948
e0452100 949 # Remove secured chunks from DIR_TMP
e47e8048
SBS
950 rm "$PATHOUT_BUFFER" "$PATHOUT_NMEA" "$PATHOUT_GPX" "$PATHOUT_KML";
951 vbm "DEBUG:STATUS:$FN:Finished magicWriteBuffer().";
952} # write buffer to disk
320ac29c
SBS
953magicParseRecipientDir() {
954 # Desc: Updates recPubKeysValid with pubkeys in dir specified by '-R' option ("recipient directory")
955 # Inputs: vars: OPTION_RECDIR, argRecDir,
956 # arry: recPubKeysValid
957 # Outputs: arry: recPubKeysValid (modified with pubkeys in argRecDir if pubkeys valid)
958 # Depends: processArguments,
959 local recFileLine updateRecipients recipientDir
960 declare -a candRecPubKeysValid
cfc25c90 961
320ac29c
SBS
962 if [[ "$OPTION_RECDIR" = "true" ]]; then
963 ### Check that argRecDir is a directory.
964 if [[ -d "$argRecDir" ]]; then
965 recipientDir="$argRecDir";
966 #### Initialize variable indicating outcome of pubkey review
967 unset updateRecipients
968 #### Add existing recipients
969 candRecPubKeysValid=(${recPubKeysValid[@]});
970 #### Parse files in recipientDir
971 for file in "$recipientDir"/*; do
972 ##### Read first line of each file
973 recFileLine="$(cat "$file" | head -n1)";
974 ##### check if first line is a valid pubkey
975 if checkAgePubkey "$recFileLine" && \
976 ( validateInput "$recFileLine" "ssh_pubkey" || validateInput "$recFileLine" "age_pubkey"); then
977 ###### T: add candidate pubkey to candRecPubKeysValid
978 candRecPubKeysValid+=("$recFileLine");
979 else
980 ###### F: throw warning;
981 yell "ERROR:Invalid recipient file detected. Not modifying recipient list."
982 updateRecipients="false";
983 fi;
984 done
985 #### Write updated recPubKeysValid array to recPubKeysValid if no failure detected
986 if ! updateRecipients="false"; then
987 recPubKeysValid=(${candRecPubKeysValid[@]});
988 fi;
cfc25c90 989 else
320ac29c
SBS
990 yell "ERROR:$0:Recipient directory $argRecDir does not exist. Exiting."; exit 1;
991 fi;
992 fi;
993} # Update recPubKeysValid with argRecDir
994magicParseRecipientArgs() {
995 # Desc: Parses recipient arguments specified by '-r' option
996 # Input: vars: OPTION_ENCRYPT from processArguments()
997 # arry: argRecPubKeys from processArguments()
998 # Output: vars: CMD_ENCRYPT, CMD_ENCRYPT_SUFFIX
999 # arry: recPubKeysValid
1000 # Depends: checkapp(), checkAgePubkey(), validateInput(), processArguments()
1001 local recipients
cfc25c90 1002
6a2cd888
SBS
1003 # Check if encryption option active.
1004 if [[ "$OPTION_ENCRYPT" = "true" ]] && [[ "$OPTION_RECIPIENTS" = "true" ]]; then
17d49005 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;
6a2cd888 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;
6a2cd888 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."
6a2cd888
SBS
1032 fi;
1033 # Catch case if '-e' is set but '-r' or '-R' is not
1034 if [[ "$OPTION_ENCRYPT" = "true" ]] && [[ ! "$OPTION_RECIPIENTS" = "true" ]]; then
1035 yell "ERROR:\'-e\' set but no \'-r\' or \'-R\' set."; exit 1; fi;
1036 # Catch case if '-r' or '-R' set but '-e' is not
1037 if [[ ! "$OPTION_ENCRYPT" = "true" ]] && [[ "$OPTION_RECIPIENTS" = "true" ]]; then
1038 yell "ERROR:\'-r\' or \'-R\' set but \'-e\' is not set."; exit 1; fi;
320ac29c
SBS
1039} # Populate recPubKeysValid with argRecPubKeys; form encryption cmd string and filename suffix
1040magicParseCompressionArg() {
1041 # Desc: Parses compression arguments specified by '-c' option
1042 # Input: vars: OPTION_COMPRESS
1043 # Output: CMD_COMPRESS, CMD_COMPRESS_SUFFIX
1044 # Depends: checkapp(), vbm(), gzip,
408a342b
SBS
1045 if [[ "$OPTION_COMPRESS" = "true" ]]; then # Check if compression option active
1046 if checkapp gzip; then # Check if gzip available
be9ef23e
SBS
1047 CMD_COMPRESS="gzip " && vbm "CMD_COMPRESS:$CMD_COMPRESS";
1048 CMD_COMPRESS_SUFFIX=".gz" && vbm "CMD_COMPRESS_SUFFIX:$CMD_COMPRESS_SUFFIX";
408a342b
SBS
1049 else
1050 yell "ERROR:Compression enabled but \"gzip\" not found. Exiting."; exit 1;
286d9825 1051 fi
408a342b 1052 else
be9ef23e
SBS
1053 CMD_COMPRESS="tee /dev/null " && vbm "CMD_COMPRESS:$CMD_COMPRESS";
1054 CMD_COMPRESS_SUFFIX="" && vbm "CMD_COMPRESS_SUFFIX:$CMD_COMPRESS_SUFFIX";
1b0e6227 1055 vbm "DEBUG:Compression not enabled.";
320ac29c
SBS
1056 fi
1057} # Form compression cmd string and filename suffix
1058magicInitWorkingDir() {
1059 # Desc: Determine temporary working directory from defaults or user input
1060 # Input: vars: OPTION_TEMPDIR, argTmpDirPriority, DIR_TMP_DEFAULT
1061 # Input: vars: SCRIPT_TIME_START
1062 # Output: vars: DIR_TMP
1063 # Depends: processArguments(), vbm(), yell()
1064 # Parse '-t' option (user-specified temporary working dir)
1065 ## Set DIR_TMP_PARENT to user-specified value if specified
1066 local DIR_TMP_PARENT
1067
1068 if [[ "$OPTION_TMPDIR" = "true" ]]; then
1069 if [[ -d "$argTempDirPriority" ]]; then
1070 DIR_TMP_PARENT="$argTempDirPriority";
1071 else
1072 yell "WARNING:Specified temporary working directory not valid:$OPTION_TMPDIR";
1073 exit 1; # Exit since user requires a specific temp dir and it is not available.
1074 fi;
1075 else
1076 ## Set DIR_TMP_PARENT to default or fallback otherwise
1077 if [[ -d "$DIR_TMP_DEFAULT" ]]; then
1078 DIR_TMP_PARENT="$DIR_TMP_DEFAULT";
1079 elif [[ -d /tmp ]]; then
1080 yell "WARNING:$DIR_TMP_DEFAULT not available. Falling back to /tmp .";
1081 DIR_TMP_PARENT="/tmp";
1082 else
1083 yell "ERROR:No valid working directory available. Exiting.";
1084 exit 1;
1085 fi
1086 fi;
1087 ## Set DIR_TMP using DIR_TMP_PARENT and nonce (SCRIPT_TIME_START)
1088 DIR_TMP="$DIR_TMP_PARENT"/"$SCRIPT_TIME_START""..bkgpslog" && vbm "DEBUG:Set DIR_TMP to:$DIR_TMP"; # Note: removed at end of main().
1089} # Sets working dir
032f4b05 1090
320ac29c
SBS
1091main() {
1092 # Process arguments
1093 processArguments "$@";
1094 ## Act upon arguments
1095 ### Determine working directory
1096 magicInitWorkingDir;
1097 ### Set output encryption and compression option strings
1098 #### React to "-r" ("encryption recipients") option
1099 magicParseRecipientArgs;
1100 #### React to "-c" ("compression") option
1101 magicParseCompressionArg;
1102 #### React to "-R" ("recipient directory") option
1103 magicParseRecipientDir;
1104
1105 # Check that critical apps and dirs are available, display missing ones.
1106 if ! checkapp gpspipe tar && ! checkdir "$DIR_OUT" "DIR_TMP"; then
6c30388f
SBS
1107 yell "ERROR:Critical components missing.";
1108 displayMissing; yell "Exiting."; exit 1; fi
1109
1110 # Set script lifespan
3395ae22 1111 setScriptTTL "$SCRIPT_TTL"; # seconds until next new SCRIPT_TTL (ex: "day" or "hour")
6c30388f
SBS
1112
1113 # File name substring: encoded bufferTTL
1114 bufferTTL_STR="$(timeDuration $BUFFER_TTL)";
1115
1116 # Init temp working dir
3a7b92f8 1117 try mkdir "$DIR_TMP" && vbm "DEBUG:Working dir creatd at:$DIR_TMP";
6c30388f
SBS
1118
1119 # Initialize 'tar' archive
1120 ## Define output tar path (note: each day gets *one* tar file (Ex: "20200731..hostname_location.[.gpx.gz].tar"))
9efeccb5
SBS
1121 PATHOUT_TAR="$DIR_OUT"/"$(dateShort)".."$SCRIPT_HOSTNAME""_location""$CMD_COMPRESS_SUFFIX""$CMD_ENCRYPT_SUFFIX".tar && \
1122 vbm "STATUS:Set PATHOUT_TAR to:$PATHOUT_TAR";
f6fb18bd
SBS
1123 ## Check that PATHOUT_TAR is a tar. Rename old and create empty one otherwise.
1124 checkMakeTar "$PATHOUT_TAR" && vbm "DEBUG:Confirmed or Created to be a tar:$PATHOUT_TAR";
6c30388f 1125 ## Append VERSION file to PATHOUT_TAR
3395ae22 1126 magicWriteVersion;
e47e8048
SBS
1127
1128 # Define GPS conversion commands
1129 CMD_CONV_NMEA="tee /dev/null " && vbm "STATUS:Set CMD_CONV_NMEA to:$CMD_CONV_NMEA"; # tee as passthrough
1130 CMD_CONV_GPX="gpsbabel -i nmea -f - -o gpx -F - " && vbm "STATUS:Set CMD_CONV_GPX to:$CMD_CONV_GPX"; # convert NMEA to GPX
1131 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 1132
e47e8048 1133 # MAIN LOOP:Record gps data until script lifespan ends
6c30388f 1134 while [[ "$SECONDS" -lt "$scriptTTL" ]]; do
811f50f2 1135 magicGatherWriteBuffer &
238775e6 1136 sleep "$BUFFER_TTL";
811f50f2 1137 done
cfc25c90 1138
e47e8048
SBS
1139 # Cleanup
1140 ## Remove DIR_TMP
cfc25c90 1141 try rm -r "$DIR_TMP";
0aabe6a3 1142
1b0e6227 1143 vbm "STATUS:Main function finished.";
8fbca23d
SBS
1144} # Main function.
1145#===END Declare local script functions===
1146#==END Define script parameters==
032f4b05
SBS
1147
1148
8fbca23d
SBS
1149#==BEGIN Perform work and exit==
1150main "$@" # Run main function.
1151exit 0;
1152#==END Perform work and exit==