fix(bklog):Fix buffer drift via SECONDS
[EVA-2020-02.git] / exec / bklog
CommitLineData
6cbe7c0a
SBS
1#!/bin/bash
2# Desc: Compresses, encrypts, and writes stdin every 5 seconds
3
5938a598
SBS
4#==BEGIN Define script parameters==
5#===BEGIN Initialize variables===
adf766fc 6
5938a598 7# Logging Behavior parameters
c5da633d 8bufferTTL="300"; # Time-to-live (seconds) for each buffer round
adf766fc 9scriptTTL_TE="day"; # Time element at the end of which script terminates
c5da633d 10dirTmpDefault="/dev/shm"; # Default parent of working directory
5938a598
SBS
11
12# Script Metadata
5938a598 13scriptName="bklog"; # Define basename of script file.
f5024030 14scriptVersion="0.1.25"; # Define version of script.
c5da633d
SBS
15scriptURL="https://gitlab.com/baltakatei/ninfacyzga-01"; # Define wesite hosting this script.
16scriptTimeStart="$(date +%Y%m%dT%H%M%S.%N)"; # YYYYmmddTHHMMSS.NNNNNNNNN
17scriptHostname=$(hostname); # Save hostname of system running this script.
18PATH="$HOME/.local/bin:$PATH"; # Add "$(systemd-path user-binaries)" path in case user apps saved there
19ageVersion="1.0.0-beta2"; # Define version of age (encryption program)
20ageURL="https://github.com/FiloSottile/age/releases/tag/v1.0.0-beta2"; # Define website hosting age.
5938a598
SBS
21
22# Arrays
c5da633d
SBS
23declare -a buffer # array for storing while read buffer
24declare -a argRecPubKeys # array for processArguments function
25declare -a recPubKeysValid # array for storing both '-r' and '-R' recipient pubkeys
c5da633d
SBS
26declare -a argProcStrings argProcFileExts # for storing buffer processing strings (ex: "gpsbabel -i nmea -f - -o gpx -F - ")
27declare -Ag appRollCall # Associative array for storing app status
28declare -Ag fileRollCall # Associative array for storing file status
29declare -Ag dirRollCall # Associative array for storing dir status
30declare -a procStrings procFileExts # Arrays for storing processing commands and resulting output file extensions
5938a598
SBS
31
32# Variables
c5da633d
SBS
33optionVerbose=""; optionEncrypt=""; dirOut=""; optionEncrypt=""; dir_tmp="";
34cmd_compress="";cmd_compress_suffix=""; cmd_encrypt=""; cmd_encrypt_suffix="";
5938a598
SBS
35
36#===END Initialize variables===
37
38#===BEGIN Declare local script functions===
6cbe7c0a
SBS
39yell() { echo "$0: $*" >&2; } #o Yell, Die, Try Three-Fingered Claw technique
40die() { yell "$*"; exit 111; } #o Ref/Attrib: https://stackoverflow.com/a/25515370
41try() { "$@" || die "cannot $*"; } #o
c5da633d
SBS
42processArguments() {
43 while [ ! $# -eq 0 ]; do # While number of arguments ($#) is not (!) equal to (-eq) zero (0).
44 case "$1" in
fcefae4c 45 -v | --verbose) optionVerbose="true"; vbm "DEBUG :Verbose mode enabled.";; # Enable verbose mode.
c5da633d
SBS
46 -h | --help) showUsage; exit 1;; # Display usage.
47 --version) showVersion; exit 1;; # Show version
fcefae4c
SBS
48 -o | --output) if [ -d "$2" ]; then dirOut="$2"; vbm "DEBUG :dirOut:$dirOut"; shift; fi ;; # Define output directory.
49 -e | --encrypt) optionEncrypt="true"; vbm "DEBUG :Encrypted output mode enabled.";; # Enable encryption
236c8b97
SBS
50 -r | --recipient) optionRecArg="true"; argRecPubKeys+=("$2"); vbm "STATUS:pubkey added:""$2"; shift;; # Add recipients
51 -R | --recipient-dir) optionRecDir="true" && argRecDir="$2"; shift;; # Add recipient watch dir
fcefae4c 52 -c | --compress) optionCompress="true"; vbm "DEBUG :Compressed output mode enabled.";; # Enable compression
c5da633d
SBS
53 -z | --time-zone) try setTimeZoneEV "$2"; shift;; # Set timestamp timezone
54 -t | --temp-dir) optionTmpDir="true" && argTempDirPriority="$2"; shift;; # Set time zone
c5da633d
SBS
55 -b | --buffer-ttl) optionCustomBufferTTL="true" && argCustomBufferTTL="$2"; shift;; # Set custom buffer period (default: 300 seconds)
56 -B | --script-ttl) optionCustomScriptTTL_TE="true" && argCustomScriptTTL_TE="$2"; shift;; # Set custom script TTL (default: "day")
a2c47ccf 57 -p | --process-string) optionProcString="true" && argProcStrings+=("$2") && argProcFileExts+=("$3") && vbm "STATUS:file extension \"$3\" for output of processing string added:\"$2\""; shift; shift;;
fcefae4c
SBS
58 -l | --label) optionLabel="true" && argLabel="$2"; vbm "DEBUG :Custom label received:$argLabel"; shift;;
59 -w | --store-raw) optionStoreRaw="true" && argRawFileExt="$2"; vbm "DEBUG :Raw stdin file extension received:$argRawFileExt"; shift;;
60 -W | --no-store-raw) optionNoStoreRaw="true"; vbm "DEBUG :Option selected to not store raw stdin data."; shift;;
c5da633d
SBS
61 *) yell "ERROR: Unrecognized argument: $1"; yell "STATUS:All arguments:$*"; exit 1;; # Handle unrecognized options.
62 esac
63 shift
64 done
65} # Argument Processing
66vbm() {
67 # Description: Prints verbose message ("vbm") to stderr if optionVerbose is set to "true".
fcefae4c 68 # Usage: vbm "DEBUG :verbose message here"
5cc9c873 69 # Version 0.1.3
c5da633d
SBS
70 # Input: arg1: string
71 # vars: optionVerbose
72 # Output: stderr
73 # Depends: bash 5.0.3, echo 8.30, date 8.30
74
75 if [ "$optionVerbose" = "true" ]; then
76 functionTime=$(date --iso-8601=ns); # Save current time in nano seconds.
5cc9c873 77 echo "[$functionTime]:$0:""$*" 1>&2; # Display argument text.
c5da633d
SBS
78 fi
79
80 # End function
81 return 0; # Function finished.
82} # Displays message if optionVerbose true
83checkapp() {
84 # Desc: If arg is a command, save result in assoc array 'appRollCall'
85 # Usage: checkapp arg1 arg2 arg3 ...
86 # Version: 0.1.1
87 # Input: global assoc. array 'appRollCall'
88 # Output: adds/updates key(value) to global assoc array 'appRollCall'
89 # Depends: bash 5.0.3
90 local returnState
91
92 #===Process Args===
93 for arg in "$@"; do
94 if command -v "$arg" 1>/dev/null 2>&1; then # Check if arg is a valid command
95 appRollCall[$arg]="true";
96 if ! [ "$returnState" = "false" ]; then returnState="true"; fi;
97 else
98 appRollCall[$arg]="false"; returnState="false";
99 fi;
100 done;
101
102 #===Determine function return code===
103 if [ "$returnState" = "true" ]; then
104 return 0;
105 else
106 return 1;
107 fi;
108} # Check that app exists
109checkfile() {
110 # Desc: If arg is a file path, save result in assoc array 'fileRollCall'
111 # Usage: checkfile arg1 arg2 arg3 ...
112 # Version: 0.1.1
113 # Input: global assoc. array 'fileRollCall'
114 # Output: adds/updates key(value) to global assoc array 'fileRollCall';
115 # Output: returns 0 if app found, 1 otherwise
116 # Depends: bash 5.0.3
117 local returnState
118
119 #===Process Args===
120 for arg in "$@"; do
121 if [ -f "$arg" ]; then
122 fileRollCall["$arg"]="true";
123 if ! [ "$returnState" = "false" ]; then returnState="true"; fi;
124 else
125 fileRollCall["$arg"]="false"; returnState="false";
126 fi;
127 done;
128
129 #===Determine function return code===
130 if [ "$returnState" = "true" ]; then
131 return 0;
132 else
133 return 1;
134 fi;
135} # Check that file exists
136checkdir() {
137 # Desc: If arg is a dir path, save result in assoc array 'dirRollCall'
138 # Usage: checkdir arg1 arg2 arg3 ...
139 # Version 0.1.1
140 # Input: global assoc. array 'dirRollCall'
141 # Output: adds/updates key(value) to global assoc array 'dirRollCall';
142 # Output: returns 0 if app found, 1 otherwise
143 # Depends: Bash 5.0.3
144 local returnState
145
146 #===Process Args===
147 for arg in "$@"; do
148 if [ -d "$arg" ]; then
149 dirRollCall["$arg"]="true";
150 if ! [ "$returnState" = "false" ]; then returnState="true"; fi
151 else
152 dirRollCall["$arg"]="false"; returnState="false";
153 fi
154 done
155
156 #===Determine function return code===
157 if [ "$returnState" = "true" ]; then
158 return 0;
159 else
160 return 1;
161 fi
162} # Check that dir exists
163displayMissing() {
164 # Desc: Displays missing apps, files, and dirs
165 # Usage: displayMissing
166 # Version 0.1.1
167 # Input: associative arrays: appRollCall, fileRollCall, dirRollCall
168 # Output: stderr: messages indicating missing apps, file, or dirs
169 # Depends: bash 5, checkAppFileDir()
170 local missingApps value appMissing missingFiles fileMissing
171 local missingDirs dirMissing
172
173 #==BEGIN Display errors==
174 #===BEGIN Display Missing Apps===
175 missingApps="Missing apps :";
fcefae4c 176 #for key in "${!appRollCall[@]}"; do echo "DEBUG :$key => ${appRollCall[$key]}"; done
c5da633d
SBS
177 for key in "${!appRollCall[@]}"; do
178 value="${appRollCall[$key]}";
179 if [ "$value" = "false" ]; then
fcefae4c 180 #echo "DEBUG :Missing apps: $key => $value";
c5da633d
SBS
181 missingApps="$missingApps""$key ";
182 appMissing="true";
183 fi;
184 done;
185 if [ "$appMissing" = "true" ]; then # Only indicate if an app is missing.
186 echo "$missingApps" 1>&2;
187 fi;
188 unset value;
189 #===END Display Missing Apps===
190
191 #===BEGIN Display Missing Files===
192 missingFiles="Missing files:";
fcefae4c 193 #for key in "${!fileRollCall[@]}"; do echo "DEBUG :$key => ${fileRollCall[$key]}"; done
c5da633d
SBS
194 for key in "${!fileRollCall[@]}"; do
195 value="${fileRollCall[$key]}";
196 if [ "$value" = "false" ]; then
fcefae4c 197 #echo "DEBUG :Missing files: $key => $value";
c5da633d
SBS
198 missingFiles="$missingFiles""$key ";
199 fileMissing="true";
200 fi;
201 done;
202 if [ "$fileMissing" = "true" ]; then # Only indicate if an app is missing.
203 echo "$missingFiles" 1>&2;
204 fi;
205 unset value;
206 #===END Display Missing Files===
207
208 #===BEGIN Display Missing Directories===
209 missingDirs="Missing dirs:";
fcefae4c 210 #for key in "${!dirRollCall[@]}"; do echo "DEBUG :$key => ${dirRollCall[$key]}"; done
c5da633d
SBS
211 for key in "${!dirRollCall[@]}"; do
212 value="${dirRollCall[$key]}";
213 if [ "$value" = "false" ]; then
fcefae4c 214 #echo "DEBUG :Missing dirs: $key => $value";
c5da633d
SBS
215 missingDirs="$missingDirs""$key ";
216 dirMissing="true";
217 fi;
218 done;
219 if [ "$dirMissing" = "true" ]; then # Only indicate if an dir is missing.
220 echo "$missingDirs" 1>&2;
221 fi;
222 unset value;
223 #===END Display Missing Directories===
224
225 #==END Display errors==
226} # Display missing apps, files, dirs
79bb6c16 227
7cebebc2
SBS
228appendArgTar(){
229 # Desc: Writes first argument to temporary file with arguments as options, then appends file to tar
230 # Usage: appendArgTar "$(echo "Data to be written.")" [name of file to be inserted] [tar path] [temp dir] ([cmd1] [cmd2] [cmd3] [cmd4]...)
231 # Version: 1.0.6
232 # Input: arg1: data to be written
233 # arg2: file name of file to be inserted into tar
234 # arg3: tar archive path (must exist first)
235 # arg4: temporary working dir
236 # arg5+: command strings (ex: "gpsbabel -i nmea -f - -o kml -F - ")
237 # Output: file written to disk
238 # Example: decrypt multiple large files in parallel
239 # appendArgTar "$(cat /tmp/largefile1.gpg)" "largefile1" $HOME/archive.tar /tmp "gpg --decrypt" &
240 # appendArgTar "$(cat /tmp/largefile2.gpg)" "largefile2" $HOME/archive.tar /tmp "gpg --decrypt" &
241 # appendArgTar "$(cat /tmp/largefile3.gpg)" "largefile3" $HOME/archive.tar /tmp "gpg --decrypt" &
242 # Depends: bash 5, tar 1, yell()
243 # Ref/Attrib: Using 'eval' to construct command strings https://askubuntu.com/a/476533
244
245 local fn fileName tarPath tmpDir cmd0 cmd1 cmd2 cmd3 cmd4
246
247 # Save function name
248 fn="${FUNCNAME[0]}";
249 #yell "DEBUG:STATUS:$fn:Finished appendArgTar()."
250
251 # Set file name
252 if ! [ -z "$2" ]; then fileName="$2"; else yell "ERROR:$fn:Not enough arguments."; exit 1; fi
253
254 # Check tar path is a file
255 if [ -f "$3" ]; then tarPath="$3"; else yell "ERROR:$fn:Tar archive arg not a file."; exit 1; fi
256
257 # Check temp dir arg
258 if ! [ -z "$4" ]; then tmpDir="$4"; else yell "ERROR:$fn:No temporary working dir set."; exit 1; fi
259
260 # Set command strings
261 if ! [ -z "$5" ]; then cmd1="$5"; else cmd1="cat "; fi # command string 1
262 if ! [ -z "$6" ]; then cmd2="$6"; else cmd2="cat "; fi # command string 2
263 if ! [ -z "$7" ]; then cmd3="$7"; else cmd3="cat "; fi # command string 3
264 if ! [ -z "$8" ]; then cmd4="$8"; else cmd4="cat "; fi # command string 4
265
266 # Input command
267 cmd0="echo \"\$1\""
268
269 # # Debug
270 # yell "DEBUG:STATUS:$fn:cmd0:$cmd0"
271 # yell "DEBUG:STATUS:$fn:cmd1:$cmd1"
272 # yell "DEBUG:STATUS:$fn:cmd2:$cmd2"
273 # yell "DEBUG:STATUS:$fn:cmd3:$cmd3"
274 # yell "DEBUG:STATUS:$fn:cmd4:$cmd4"
275 # yell "DEBUG:STATUS:$fn:fileName:$fileName"
276 # yell "DEBUG:STATUS:$fn:tarPath:$tarPath"
277 # yell "DEBUG:STATUS:$fn:tmpDir:$tmpDir"
278
279 # Write to temporary working dir
280 eval "$cmd0 | $cmd1 | $cmd2 | $cmd3 | $cmd4" > "$tmpDir"/"$fileName";
281
282 # Append to tar
283 try tar --append --directory="$tmpDir" --file="$tarPath" "$fileName";
284 #yell "DEBUG:STATUS:$fn:Finished appendArgTar()."
285} # Append Bash var to file appended to Tar archive
79bb6c16
SBS
286appendFileTar(){
287 # Desc: Appends [processed] file to tar
288 # Usage: appendFileTar [file path] [name of file to be inserted] [tar path] [temp dir] ([process cmd])
289 # Version: 2.0.1
290 # Input: arg1: path of file to be (processed and) written
291 # arg2: name to use for file inserted into tar
292 # arg3: tar archive path (must exist first)
293 # arg4: temporary working dir
294 # arg5: (optional) command string to process file (ex: "gpsbabel -i nmea -f - -o kml -F - ")
295 # Output: file written to disk
296 # Example: decrypt multiple large files in parallel
297 # appendFileTar /tmp/largefile1.gpg "largefile1" $HOME/archive.tar /tmp "gpg --decrypt" &
298 # appendFileTar /tmp/largefile2.gpg "largefile2" $HOME/archive.tar /tmp "gpg --decrypt" &
299 # appendFileTar /tmp/largefile3.gpg "largefile3" $HOME/archive.tar /tmp "gpg --decrypt" &
300 # Depends: bash 5.0.3, tar 1.30, cat 8.30, yell()
301 local fn fileName tarPath tmpDir
302
303 # Save function name
304 fn="${FUNCNAME[0]}";
fcefae4c 305 #yell "DEBUG :STATUS:$fn:Started appendFileTar()."
79bb6c16
SBS
306
307 # Set file name
308 if ! [ -z "$2" ]; then fileName="$2"; else yell "ERROR:$fn:Not enough arguments."; exit 1; fi
309 # Check tar path is a file
310 if [ -f "$3" ]; then tarPath="$3"; else yell "ERROR:$fn:Tar archive arg not a file:$3"; exit 1; fi
311 # Check temp dir arg
312 if ! [ -z "$4" ]; then tmpDir="$4"; else yell "ERROR:$fn:No temporary working dir set."; exit 1; fi
313 # Set command strings
314 if ! [ -z "$5" ]; then cmd1="$5"; else cmd1="cat "; fi # command string
315
316 # Input command string
317 cmd0="cat \"\$1\"";
318
319 # Write to temporary working dir
320 eval "$cmd0 | $cmd1" > "$tmpDir"/"$fileName";
321
322 # Append to tar
323 try tar --append --directory="$tmpDir" --file="$tarPath" "$fileName";
fcefae4c 324 #yell "DEBUG :STATUS:$fn:Finished appendFileTar()."
79bb6c16
SBS
325} # Append [processed] file to Tar archive
326checkAgePubkey() {
327 # Desc: Checks if string is an age-compatible pubkey
328 # Usage: checkAgePubkey [str pubkey]
329 # Version: 0.1.2
330 # Input: arg1: string
331 # Output: return code 0: string is age-compatible pubkey
332 # return code 1: string is NOT an age-compatible pubkey
333 # age stderr (ex: there is stderr if invalid string provided)
334 # Depends: age (v0.1.0-beta2; https://github.com/FiloSottile/age/releases/tag/v1.0.0-beta2 )
335
336 argPubkey="$1";
337
338 if echo "test" | age -a -r "$argPubkey" 1>/dev/null; then
339 return 0;
340 else
341 return 1;
342 fi;
343} # Check age pubkey
34711384
SBS
344checkMakeTar() {
345 # Desc: Checks that a valid tar archive exists, creates one otherwise
346 # Usage: checkMakeTar [ path ]
347 # Version: 1.0.2
348 # Input: arg1: path of tar archive
349 # Output: exit code 0 : tar readable
350 # exit code 1 : tar missing; created
351 # exit code 2 : tar not readable; moved; replaced
352 # Depends: bash 5, date 8, tar 1, try()
353 local pathTar returnFlag0 returnFlag1 returnFlag2
354 pathTar="$1";
355
356 # Check if file is a valid tar archive
357 if tar --list --file="$pathTar" 1>/dev/null 2>&1; then
358 ## T1: return success
359 returnFlag0="tar valid";
d5f5aa4e
SBS
360 elif { sleep 2; tar --list --file="$pathTar" 1>/dev/null 2>&1; }; then
361 ## F1: Check tar archive again after 2-second sleep
362 returnFlag0="tar valid";
34711384 363 else
d5f5aa4e 364 ## F2-1: Check if file exists
34711384
SBS
365 if [[ -f "$pathTar" ]]; then
366 ### T: Rename file
d5f5aa4e 367 try mv "$pathTar" "$pathTar""--broken--""$(date +%Y%m%dT%H%M%S%z)" && \
34711384
SBS
368 returnFlag1="tar moved";
369 else
370 ### F: -
371 :
372 fi;
d5f5aa4e 373 ## F2-1: Create tar archive, return 0
34711384
SBS
374 try tar --create --file="$pathTar" --files-from=/dev/null && \
375 returnFlag2="tar created";
376 fi;
377
378 # Determine function return code
379 if [[ "$returnFlag0" = "tar valid" ]]; then
380 return 0;
381 elif [[ "$returnFlag2" = "tar created" ]] && ! [[ "$returnFlag1" = "tar moved" ]]; then
382 return 1; # tar missing so created
383 elif [[ "$returnFlag2" = "tar created" ]] && [[ "$returnFlag1" = "tar moved" ]]; then
384 return 2; # tar not readable so moved; replaced
385 fi;
386} # checks if arg1 is tar; creates one otherwise
79bb6c16
SBS
387dateShort(){
388 # Desc: Date without separators (YYYYmmdd)
389 # Usage: dateShort ([str date])
390 # Version: 1.1.2
391 # Input: arg1: 'date'-parsable timestamp string (optional)
392 # Output: stdout: date (ISO-8601, no separators)
393 # Depends: bash 5.0.3, date 8.30, yell()
394 local argTime timeCurrent timeInput dateCurrentShort
395
396 argTime="$1";
397 # Get Current Time
398 timeCurrent="$(date --iso-8601=seconds)" ; # Produce `date`-parsable current timestamp with resolution of 1 second.
399 # Decide to parse current or supplied date
400 ## Check if time argument empty
401 if [[ -z "$argTime" ]]; then
402 ## T: Time argument empty, use current time
403 timeInput="$timeCurrent";
404 else
405 ## F: Time argument exists, validate time
406 if date --date="$argTime" 1>/dev/null 2>&1; then
407 ### T: Time argument is valid; use it
408 timeInput="$argTime";
409 else
410 ### F: Time argument not valid; exit
411 yell "ERROR:Invalid time argument supplied. Exiting."; exit 1;
412 fi;
413 fi;
414 # Construct and deliver separator-les date string
415 dateCurrentShort="$(date -d "$timeInput" +%Y%m%d)"; # Produce separator-less current date with resolution 1 day.
416 echo "$dateCurrentShort";
417} # Get YYYYmmdd
8681fe1b
SBS
418dateTimeShort(){
419 # Desc: Timestamp without separators (YYYYmmddTHHMMSS+zzzz)
420 # Usage: dateTimeShort ([str date])
421 # Version 1.1.1
422 # Input: arg1: 'date'-parsable timestamp string (optional)
423 # Output: stdout: timestamp (ISO-8601, no separators)
424 # Depends: yell
425 local argTime timeCurrent timeInput timeCurrentShort
426
427 argTime="$1";
428 # Get Current Time
429 timeCurrent="$(date --iso-8601=seconds)" ; # Produce `date`-parsable current timestamp with resolution of 1 second.
430 # Decide to parse current or supplied date
431 ## Check if time argument empty
432 if [[ -z "$argTime" ]]; then
433 ## T: Time argument empty, use current time
434 timeInput="$timeCurrent";
435 else
436 ## F: Time argument exists, validate time
437 if date --date="$argTime" 1>/dev/null 2>&1; then
438 ### T: Time argument is valid; use it
439 timeInput="$argTime";
440 else
441 ### F: Time argument not valid; exit
442 yell "ERROR:Invalid time argument supplied. Exiting."; exit 1;
443 fi
444 fi
445 # Construct and deliver separator-les date string
446 timeCurrentShort="$(date -d "$timeInput" +%Y%m%dT%H%M%S%z)";
447 echo "$timeCurrentShort";
448} # Get YYYYmmddTHHMMSS±zzzz
79bb6c16
SBS
449setTimeZoneEV(){
450 # Desc: Set time zone environment variable TZ
451 # Usage: setTimeZoneEV arg1
452 # Version 0.1.2
453 # Input: arg1: 'date'-compatible timezone string (ex: "America/New_York")
454 # TZDIR env var (optional; default: "/usr/share/zoneinfo")
455 # Output: exports TZ
456 # exit code 0 on success
457 # exit code 1 on incorrect number of arguments
458 # exit code 2 if unable to validate arg1
459 # Depends: yell(), printenv 8.30, bash 5.0.3
460 # Tested on: Debian 10
461 local tzDir returnState argTimeZone
462
463 argTimeZone="$1"
464 if ! [[ $# -eq 1 ]]; then
465 yell "ERROR:Invalid argument count.";
466 return 1;
467 fi
468
469 # Read TZDIR env var if available
470 if printenv TZDIR 1>/dev/null 2>&1; then
471 tzDir="$(printenv TZDIR)";
472 else
473 tzDir="/usr/share/zoneinfo";
474 fi
475
476 # Validate TZ string
477 if ! [[ -f "$tzDir"/"$argTimeZone" ]]; then
478 yell "ERROR:Invalid time zone argument.";
479 return 2;
480 else
481 # Export ARG1 as TZ environment variable
482 TZ="$argTimeZone" && export TZ && returnState="true";
483 fi
484
485 # Determine function return code
486 if [ "$returnState" = "true" ]; then
487 return 0;
488 fi
489} # Exports TZ environment variable
c5da633d
SBS
490showVersion() {
491 yell "$scriptVersion"
492} # Display script version.
4e4707fb
SBS
493timeDuration(){
494 # Desc: Given seconds, output ISO-8601 duration string
495 # Ref/Attrib: ISO-8601:2004(E), §4.4.4.2 Representations of time intervals by duration and context information
496 # Note: "1 month" ("P1M") is assumed to be "30 days" (see ISO-8601:2004(E), §2.2.1.2)
497 # Usage: timeDuration [1:seconds] ([2:precision])
498 # Version: 1.0.4
499 # Input: arg1: seconds as base 10 integer >= 0 (ex: 3601)
500 # arg2: precision level (optional; default=2)
501 # Output: stdout: ISO-8601 duration string (ex: "P1H1S", "P2Y10M15DT10H30M20S")
502 # exit code 0: success
503 # exit code 1: error_input
504 # exit code 2: error_unknown
505 # Example: 'timeDuration 111111 3' yields 'P1DT6H51M'
506 # Depends: date 8, bash 5, yell,
507 local argSeconds argPrecision precision returnState remainder
508 local fullYears fullMonths fullDays fullHours fullMinutes fullSeconds
509 local hasYears hasMonths hasDays hasHours hasMinutes hasSeconds
510 local witherPrecision output
511 local displayYears displayMonths displayDays displayHours displayMinutes displaySeconds
512
513 argSeconds="$1"; # read arg1 (seconds)
514 argPrecision="$2"; # read arg2 (precision)
515 precision=2; # set default precision
516
517 # Check that between one and two arguments is supplied
518 if ! { [[ $# -ge 1 ]] && [[ $# -le 2 ]]; }; then
519 yell "ERROR:Invalid number of arguments:$# . Exiting.";
520 returnState="error_input"; fi
521
522 # Check that argSeconds provided
523 if [[ $# -ge 1 ]]; then
524 ## Check that argSeconds is a positive integer
525 if [[ "$argSeconds" =~ ^[[:digit:]]+$ ]]; then
526 :
527 else
528 yell "ERROR:argSeconds not a digit.";
529 returnState="error_input";
530 fi
531 else
532 yell "ERROR:No argument provided. Exiting.";
533 exit 1;
534 fi
535
536 # Consider whether argPrecision was provided
537 if [[ $# -eq 2 ]]; then
538 # Check that argPrecision is a positive integer
539 if [[ "$argPrecision" =~ ^[[:digit:]]+$ ]] && [[ "$argPrecision" -gt 0 ]]; then
540 precision="$argPrecision";
541 else
542 yell "ERROR:argPrecision not a positive integer. (is $argPrecision ). Leaving early.";
543 returnState="error_input";
544 fi;
545 else
546 :
547 fi;
548
549 remainder="$argSeconds" ; # seconds
550 ## Calculate full years Y, update remainder
551 fullYears=$(( remainder / (365*24*60*60) ));
552 remainder=$(( remainder - (fullYears*365*24*60*60) ));
553 ## Calculate full months M, update remainder
554 fullMonths=$(( remainder / (30*24*60*60) ));
555 remainder=$(( remainder - (fullMonths*30*24*60*60) ));
556 ## Calculate full days D, update remainder
557 fullDays=$(( remainder / (24*60*60) ));
558 remainder=$(( remainder - (fullDays*24*60*60) ));
559 ## Calculate full hours H, update remainder
560 fullHours=$(( remainder / (60*60) ));
561 remainder=$(( remainder - (fullHours*60*60) ));
562 ## Calculate full minutes M, update remainder
563 fullMinutes=$(( remainder / (60) ));
564 remainder=$(( remainder - (fullMinutes*60) ));
565 ## Calculate full seconds S, update remainder
566 fullSeconds=$(( remainder / (1) ));
567 remainder=$(( remainder - (remainder*1) ));
568 ## Check which fields filled
569 if [[ $fullYears -gt 0 ]]; then hasYears="true"; else hasYears="false"; fi
570 if [[ $fullMonths -gt 0 ]]; then hasMonths="true"; else hasMonths="false"; fi
571 if [[ $fullDays -gt 0 ]]; then hasDays="true"; else hasDays="false"; fi
572 if [[ $fullHours -gt 0 ]]; then hasHours="true"; else hasHours="false"; fi
573 if [[ $fullMinutes -gt 0 ]]; then hasMinutes="true"; else hasMinutes="false"; fi
574 if [[ $fullSeconds -gt 0 ]]; then hasSeconds="true"; else hasSeconds="false"; fi
575
576 ## Determine which fields to display (see ISO-8601:2004 §4.4.3.2)
577 witherPrecision="false"
578
579 ### Years
580 if $hasYears && [[ $precision -gt 0 ]]; then
581 displayYears="true";
582 witherPrecision="true";
583 else
584 displayYears="false";
585 fi;
586 if $witherPrecision; then ((precision--)); fi;
587
588 ### Months
589 if $hasMonths && [[ $precision -gt 0 ]]; then
590 displayMonths="true";
591 witherPrecision="true";
592 else
593 displayMonths="false";
594 fi;
595 if $witherPrecision && [[ $precision -gt 0 ]]; then
596 displayMonths="true";
597 fi;
598 if $witherPrecision; then ((precision--)); fi;
599
600 ### Days
601 if $hasDays && [[ $precision -gt 0 ]]; then
602 displayDays="true";
603 witherPrecision="true";
604 else
605 displayDays="false";
606 fi;
607 if $witherPrecision && [[ $precision -gt 0 ]]; then
608 displayDays="true";
609 fi;
610 if $witherPrecision; then ((precision--)); fi;
611
612 ### Hours
613 if $hasHours && [[ $precision -gt 0 ]]; then
614 displayHours="true";
615 witherPrecision="true";
616 else
617 displayHours="false";
618 fi;
619 if $witherPrecision && [[ $precision -gt 0 ]]; then
620 displayHours="true";
621 fi;
622 if $witherPrecision; then ((precision--)); fi;
623
624 ### Minutes
625 if $hasMinutes && [[ $precision -gt 0 ]]; then
626 displayMinutes="true";
627 witherPrecision="true";
628 else
629 displayMinutes="false";
630 fi;
631 if $witherPrecision && [[ $precision -gt 0 ]]; then
632 displayMinutes="true";
633 fi;
634 if $witherPrecision; then ((precision--)); fi;
635
636 ### Seconds
637
638 if $hasSeconds && [[ $precision -gt 0 ]]; then
639 displaySeconds="true";
640 witherPrecision="true";
641 else
642 displaySeconds="false";
643 fi;
644 if $witherPrecision && [[ $precision -gt 0 ]]; then
645 displaySeconds="true";
646 fi;
647 if $witherPrecision; then ((precision--)); fi;
648
649 ## Determine whether or not the "T" separator is needed to separate date and time elements
650 if ( $displayHours || $displayMinutes || $displaySeconds); then
651 displayDateTime="true"; else displayDateTime="false"; fi
652
653 ## Construct duration output string
654 output="P"
655 if $displayYears; then
656 output=$output$fullYears"Y"; fi
657 if $displayMonths; then
658 output=$output$fullMonths"M"; fi
659 if $displayDays; then
660 output=$output$fullDays"D"; fi
661 if $displayDateTime; then
662 output=$output"T"; fi
663 if $displayHours; then
664 output=$output$fullHours"H"; fi
665 if $displayMinutes; then
666 output=$output$fullMinutes"M"; fi
667 if $displaySeconds; then
668 output=$output$fullSeconds"S"; fi
669
670 ## Output duration string to stdout
671 echo "$output" && returnState="true";
672
673 #===Determine function return code===
674 if [ "$returnState" = "true" ]; then
675 return 0;
676 elif [ "$returnState" = "error_input" ]; then
677 yell "ERROR:input";
678 return 1;
679 else
680 yell "ERROR:Unknown";
681 return 2;
682 fi
683
684} # Get duration (ex: PT10M4S )
34711384
SBS
685timeUntilNextDay(){
686 # Desc: Report seconds until next day.
687 # Version: 1.0.2
688 # Output: stdout: integer seconds until next day
689 # Output: exit code 0 if stdout > 0; 1 if stdout = 0; 2 if stdout < 0
690 # Usage: timeUntilNextDay
691 # Usage: if ! myTTL="$(timeUntilNextDay)"; then yell "ERROR in if statement"; exit 1; fi
692 # Depends: date 8, echo 8, yell, try
693
694 local returnState timeCurrent timeNextDay secondsUntilNextDay returnState
695 timeCurrent="$(date --iso-8601=seconds)" ; # Produce `date`-parsable current timestamp with resolution of 1 second.
696 timeNextDay="$(date -d "$timeCurrent next day" --iso-8601=date)"; # Produce timestamp of beginning of tomorrow with resolution of 1 second.
697 secondsUntilNextDay="$(( $(date +%s -d "$timeNextDay") - $(date +%s -d "$timeCurrent") ))" ; # Calculate seconds until closest future midnight (res. 1 second).
698 if [[ "$secondsUntilNextDay" -gt 0 ]]; then
699 returnState="true";
700 elif [[ "$secondsUntilNextDay" -eq 0 ]]; then
701 returnState="warning_zero";
702 yell "WARNING:Reported time until next day exactly zero.";
703 elif [[ "$secondsUntilNextDay" -lt 0 ]]; then
704 returnState="warning_negative";
705 yell "WARNING:Reported time until next day is negative.";
706 fi
707
708 try echo "$secondsUntilNextDay"; # Report
709
710 # Determine function return code
711 if [[ "$returnState" = "true" ]]; then
712 return 0;
713 elif [[ "$returnState" = "warning_zero" ]]; then
714 return 1;
715 elif [[ "$returnState" = "warning_negative" ]]; then
716 return 2;
717 fi
718} # Report seconds until next day
719timeUntilNextHour(){
720 # Desc: Report seconds until next hour
721 # Version 1.0.1
722 # Output: stdout: integer seconds until next hour
723 # Output: exit code 0 if stdout > 0; 1 if stdout = 0; 2 if stdout < 0
724 # Usage: timeUntilNextHour
725 # Usage: if ! myTTL="$(timeUntilNextHour)"; then yell "ERROR in if statement"; exit 1; fi
726
727 local returnState timeCurrent timeNextHour secondsUntilNextHour
728 timeCurrent="$(date --iso-8601=seconds)"; # Produce `date`-parsable current timestamp with resolution of 1 second.
729 timeNextHour="$(date -d "$timeCurrent next hour" --iso-8601=hours)"; # Produce `date`-parsable current time stamp with resolution of 1 second.
730 secondsUntilNextHour="$(( $(date +%s -d "$timeNextHour") - $(date +%s -d "$timeCurrent") ))"; # Calculate seconds until next hour (res. 1 second).
731 if [[ "$secondsUntilNextHour" -gt 0 ]]; then
732 returnState="true";
733 elif [[ "$secondsUntilNextHour" -eq 0 ]]; then
734 returnState="warning_zero";
735 yell "WARNING:Reported time until next hour exactly zero.";
736 elif [[ "$secondsUntilNextHour" -lt 0 ]]; then
737 returnState="warning_negative";
738 yell "WARNING:Reported time until next hour is negative.";
739 fi;
740
741 try echo "$secondsUntilNextHour"; # Report
742
743 # Determine function return code
744 if [[ "$returnState" = "true" ]]; then
745 return 0;
746 elif [[ "$returnState" = "warning_zero" ]]; then
747 return 1;
748 elif [[ "$returnState" = "warning_negative" ]]; then
749 return 2;
750 fi;
751} # Report seconds until next hour
be6ce8f4
SBS
752validateInput() {
753 # Desc: Validates Input
754 # Usage: validateInput [str input] [str input type]
755 # Version: 0.3.1
756 # Input: arg1: string to validate
757 # arg2: string specifying input type (ex:"ssh_pubkey")
758 # Output: return code 0: if input string matched specified string type
759 # Depends: bash 5, yell()
760
761 local fn argInput argType
762
763 # Save function name
764 fn="${FUNCNAME[0]}";
765
766 # Process arguments
767 argInput="$1";
768 argType="$2";
769 if [[ $# -gt 2 ]]; then yell "ERROR:$0:$fn:Too many arguments."; exit 1; fi;
770
771 # Check for blank
772 if [[ -z "$argInput" ]]; then return 1; fi
773
774 # Define input types
775 ## ssh_pubkey
776 ### Check for alnum/dash base64 (ex: "ssh-rsa AAAAB3NzaC1yc2EAAA")
777 if [[ "$argType" = "ssh_pubkey" ]]; then
778 if [[ "$argInput" =~ ^[[:alnum:]-]*[\ ]*[[:alnum:]+/=]*$ ]]; then
779 return 0; fi; fi;
780
781 ## age_pubkey
782 ### Check for age1[:bech32:]
783 if [[ "$argType" = "age_pubkey" ]]; then
784 if [[ "$argInput" =~ ^age1[qpzry9x8gf2tvdw0s3jn54khce6mua7l]*$ ]]; then
785 return 0; fi; fi
786
787 ## integer
788 if [[ "$argType" = "integer" ]]; then
789 if [[ "$argInput" =~ ^[[:digit:]]*$ ]]; then
790 return 0; fi; fi;
791
792 ## time element (year, month, week, day, hour, minute, second)
793 if [[ "$argType" = "time_element" ]]; then
794 if [[ "$argInput" = "year" ]] || \
795 [[ "$argInput" = "month" ]] || \
796 [[ "$argInput" = "week" ]] || \
797 [[ "$argInput" = "day" ]] || \
798 [[ "$argInput" = "hour" ]] || \
799 [[ "$argInput" = "minute" ]] || \
800 [[ "$argInput" = "second" ]]; then
801 return 0; fi; fi;
802
803 # Return error if no condition matched.
804 return 1;
805} # Validates strings
79bb6c16 806
4e4707fb
SBS
807magicInitWorkingDir() {
808 # Desc: Determine temporary working directory from defaults or user input
809 # Usage: magicInitWorkingDir
810 # Input: vars: optionTmpDir, argTempDirPriority, dirTmpDefault
811 # Input: vars: scriptTimeStart
812 # Output: vars: dir_tmp
813 # Depends: bash 5.0.3, processArguments(), vbm(), yell()
814 # Parse '-t' option (user-specified temporary working dir)
815 ## Set dir_tmp_parent to user-specified value if specified
80fbd111 816 local fn dir_tmp_parent
6fe054b4 817
80fbd111
SBS
818 # Save function name
819 fn="${FUNCNAME[0]}";
820
821 vbm "STATUS:$fn:Starting magicInitWorkingDir() function.";
4e4707fb
SBS
822 if [[ "$optionTmpDir" = "true" ]]; then
823 if [[ -d "$argTempDirPriority" ]]; then
824 dir_tmp_parent="$argTempDirPriority";
825 else
80fbd111 826 yell "WARNING:$fn:Specified temporary working directory not valid:$argTempDirPriority";
4e4707fb
SBS
827 exit 1; # Exit since user requires a specific temp dir and it is not available.
828 fi;
829 else
830 ## Set dir_tmp_parent to default or fallback otherwise
831 if [[ -d "$dirTmpDefault" ]]; then
832 dir_tmp_parent="$dirTmpDefault";
833 elif [[ -d /tmp ]]; then
80fbd111 834 yell "WARNING:$fn:$dirTmpDefault not available. Falling back to /tmp .";
4e4707fb
SBS
835 dir_tmp_parent="/tmp";
836 else
80fbd111 837 yell "ERROR:$fn:No valid working directory available. Exiting.";
4e4707fb
SBS
838 exit 1;
839 fi;
840 fi;
841 ## Set dir_tmp using dir_tmp_parent and nonce (scriptTimeStart)
fcefae4c 842 dir_tmp="$dir_tmp_parent"/"$scriptTimeStart""..bkgpslog" && vbm "DEBUG :$fn:Set dir_tmp to:$dir_tmp"; # Note: removed at end of main().
80fbd111 843 vbm "STATUS:$fn:Finished magicInitWorkingDir() function.";
4e4707fb
SBS
844} # Sets working dir
845magicInitCheckTar() {
846 # Desc: Initializes or checks output tar
847 # input: vars: dirOut, bufferTTL, cmd_encrypt_suffix, cmd_compress_suffix
848 # input: vars: scriptHostname
849 # output: vars: pathout_tar
850 # depends: Bash 5.0.3, vbm(), dateShort(), checkMakeTar(), magicWriteVersion()
195dd3c3 851 local fn checkMakeTarES
4e4707fb 852
80fbd111
SBS
853 # Save function name
854 fn="${FUNCNAME[0]}";
855
856 vbm "STATUS:$fn:Starting magicInitCheckTar() function.";
4e4707fb
SBS
857 # Form pathout_tar
858 pathout_tar="$dirOut"/"$(dateShort "$(date --date="$bufferTTL seconds ago" --iso-8601=seconds)")".."$scriptHostname""$label""$cmd_compress_suffix""$cmd_encrypt_suffix".tar && \
80fbd111 859 vbm "STATUS:$fn:Set pathout_tar to:$pathout_tar";
4e4707fb 860 # Validate pathout_tar as tar.
195dd3c3 861 checkMakeTar "$pathout_tar"; checkMakeTarES="$?";
4e4707fb 862 ## Add VERSION file if checkMakeTar had to create a tar (exited 1) or replace one (exited 2)
195dd3c3
SBS
863 vbm "STATUS:$fn:exit status before magicWriteVersion:$checkMakeTarES"
864 if [[ "$checkMakeTarES" -eq 1 ]] || [[ "$checkMakeTarES" -eq 2 ]]; then magicWriteVersion; fi
80fbd111 865 vbm "STATUS:$fn:Finished magicInitCheckTar() function.";
4e4707fb
SBS
866} # Initialize tar, set pathout_tar
867magicParseCompressionArg() {
868 # Desc: Parses compression arguments specified by '-c' option
869 # Input: vars: optionCompress
870 # Output: cmd_compress, cmd_compress_suffix
871 # Depends: processArguments(), vbm(), checkapp(), gzip 1.9
80fbd111 872 local fn
6fe054b4 873
80fbd111
SBS
874 # Save function name
875 fn="${FUNCNAME[0]}";
876
877 vbm "STATUS:$fn:Starting magicParseCompressionArg() function.";
4e4707fb
SBS
878 if [[ "$optionCompress" = "true" ]]; then # Check if compression option active
879 if checkapp gzip; then # Check if gzip available
80fbd111
SBS
880 cmd_compress="gzip " && vbm "STATUS:$fn:cmd_compress:$cmd_compress";
881 cmd_compress_suffix=".gz" && vbm "STATUS:$fn:cmd_compress_suffix:$cmd_compress_suffix";
4e4707fb 882 else
80fbd111 883 yell "ERROR:$fn:Compression enabled but \"gzip\" not found. Exiting."; exit 1;
6fe054b4 884 fi;
4e4707fb 885 else
80fbd111
SBS
886 cmd_compress="tee /dev/null " && vbm "STATUS:$fn:cmd_compress:$cmd_compress";
887 cmd_compress_suffix="" && vbm "STATUS:$fn:cmd_compress_suffix:$cmd_compress_suffix";
fcefae4c 888 vbm "DEBUG :$fn:Compression not enabled.";
6fe054b4 889 fi;
a2c47ccf 890 vbm "STATUS:$fn:Finished magicParseCompressionArg() function.";
4e4707fb
SBS
891} # Form compression cmd string and filename suffix
892magicParseCustomTTL() {
893 # Desc: Set user-specified TTLs for buffer and script
894 # Usage: magicParseCustomTTL
895 # Input: vars: argCustomBufferTTL (integer), argCustomScriptTTL_TE (string)
896 # Input: vars: optionCustomBufferTTL, optionCustomScriptTTL_TE
897 # Input: vars: bufferTTL (integer), scriptTTL_TE (string)
898 # Output: bufferTTL (integer), scriptTTL_TE (string)
899 # Depends: Bash 5.0.3, yell(), vbm(), validateInput(), showUsage()
80fbd111
SBS
900 local fn
901
902 # Save function name
903 fn="${FUNCNAME[0]}";
4e4707fb 904
80fbd111 905 vbm "STATUS:$fn:Starting magicParseCustomTTL() function.";
4e4707fb
SBS
906 # React to '-b, --buffer-ttl' option
907 if [[ "$optionCustomBufferTTL" = "true" ]]; then
908 ## T: Check if argCustomBufferTTL is an integer
909 if validateInput "$argCustomBufferTTL" "integer"; then
910 ### T: argCustomBufferTTL is an integer
80fbd111 911 bufferTTL="$argCustomBufferTTL" && vbm "STATUS:$fn:Custom bufferTTL from -b:$bufferTTL";
4e4707fb
SBS
912 else
913 ### F: argcustomBufferTTL is not an integer
80fbd111 914 yell "ERROR:$fn:Invalid integer argument for custom buffer time-to-live."; showUsage; exit 1;
4e4707fb
SBS
915 fi;
916 ## F: do not change bufferTTL
917 fi;
918
919 # React to '-B, --script-ttl' option
920 if [[ "$optionCustomScriptTTL_TE" = "true" ]]; then
921 ## T: Check if argCustomScriptTTL is a time element (ex: "day", "hour")
922 if validateInput "$argCustomScriptTTL_TE" "time_element"; then
923 ### T: argCustomScriptTTL is a time element
80fbd111 924 scriptTTL_TE="$argCustomScriptTTL_TE" && vbm "STATUS:$fn:Custom scriptTTL_TE from -B:$scriptTTL_TE";
4e4707fb
SBS
925 else
926 ### F: argcustomScriptTTL is not a time element
80fbd111 927 yell "ERROR:$fn:Invalid time element argument for custom script time-to-live."; showUsage; exit 1;
4e4707fb
SBS
928 fi;
929 ## F: do not change scriptTTL_TE
6fe054b4 930 fi;
a2c47ccf 931 vbm "STATUS:$fn:Finished magicParseCustomTTL() function.";
4e4707fb
SBS
932} # Sets custom script or buffer TTL if specified
933magicParseLabel() {
934 # Desc: Parses -l option to set label
935 # In : optionLabel, argLabel
936 # Out: vars: label
937 # Depends: Bash 5.0.3, vbm(), yell()
80fbd111 938 local fn
4e4707fb 939
80fbd111
SBS
940 # Save function name
941 fn="${FUNCNAME[0]}";
942
943 vbm "STATUS:$fn:Started magicParseLabel() function.";
4e4707fb
SBS
944 # Do nothing if optionLabel not set to true.
945 if [[ ! "$optionLabel" = "true" ]]; then
80fbd111 946 vbm "STATUS:$fn:optionlabel not set to 'true'. Returning early.";
4e4707fb
SBS
947 return;
948 fi;
949 # Set label if optionLabel is true
950 if [[ "$optionLabel" = "true" ]]; then
951 label="_""$argLabel";
80fbd111 952 vbm "STATUS:$fn:Set label:$label";
4e4707fb 953 fi;
80fbd111 954 vbm "STATUS:$fn:Finished magicParseLabel() function.";
4e4707fb
SBS
955} # Set label used in output file name
956magicParseProcessStrings() {
957 # Desc: Processes user-supplied process strings into process commands for appendFileTar().
958 # Usage: magicParseProcessStrings
959 # In : vars: optionProcString optionNoStoreRaw optionStoreRaw argRawFileExt
960 # arry: argProcStrings, argProcFileExts
961 # Out: arry: procStrings, procFileExts
962 # Depends Bash 5.0.3, yell(), vbm()
80fbd111
SBS
963 local fn rawFileExt
964
965 # Save function name
966 fn="${FUNCNAME[0]}";
967
968 vbm "STATUS:$fn:Starting magicParseProcessStrings() function.";
969 vbm "STATUS:$fn:var:optionProcString:$optionProcString";
970 vbm "STATUS:$fn:var:optionNoStoreRaw:$optionNoStoreRaw";
971 vbm "STATUS:$fn:var:optionStoreRaw:$optionStoreRaw";
972 vbm "STATUS:$fn:var:argRawFileExt:$argRawFileExt";
973 vbm "STATUS:$fn:ary:argProcStrings:${argProcStrings[*]}";
974 vbm "STATUS:$fn:ary:argProcFileExts:${argProcFileExts[*]}"
4e4707fb
SBS
975 # Validate input
976 ## Validate argRawFileExt
977 if [[ "$argRawFileExt" =~ ^[.][[:alnum:]]*$ ]]; then
492d1a57
SBS
978 rawFileExt="$argRawFileExt" && \
979 vbm "DEBUG :$fn:Set rawFileExt to \"$argRawFileExt\"";
980 else
981 vbm "DEBUG :$fn:Validation failure for $argRawFileExt . Not set to rawFileExt.";
4e4707fb
SBS
982 fi;
983
984 # Add default stdin output file entries for procStrings, procFileExts
985 ## Check if user specified that no raw stdin be saved.
986 if [[ ! "$optionNoStoreRaw" = "true" ]]; then
987 ### T: --no-store-raw not set. Store raw. Append procStrings with cat.
492d1a57 988 vbm "DEBUG :$fn:--no-store-raw not set. Storing raw.";
4e4707fb 989 #### Append procStrings array
492d1a57
SBS
990 procStrings+=("cat ") && \
991 vbm "DEBUG :$fn:Appended \"cat \" to procStrings";
992 vbm "DEBUG :$fn:procStrings array:${procStrings[*]}";
4e4707fb
SBS
993 #### Check if --store-raw set.
994 if [[ "$optionStoreRaw" = "true" ]]; then
995 ##### T: --store-raw set. Append procFileExts with user-specified file ext
492d1a57
SBS
996 vbm "DEBUG :$fn:--store-raw set.";
997 procFileExts+=("$rawFileExt") && \
998 vbm "DEBUG :$fn:Appended $rawFileExt to procFileExts";
999 vbm "STATUS:$fn:procFileExts array:${procFileExts[*]}";
4e4707fb
SBS
1000 else
1001 ##### F: --store-raw not set. Append procFileExts with default ".stdin" file ext
1002 ###### Append procFileExts array
492d1a57
SBS
1003 procFileExts+=(".stdin") && \
1004 vbm "DEBUG :$fn:Appended \".stdin\" to procFileExts";
1005 vbm "STATUS:$fn:procFileExts array:${procFileExts[*]}";
4e4707fb
SBS
1006 fi;
1007 else
1008 ### F: --no-store-raw set. Do not store raw.
1009 #### Do not append procStrings or procFileExts arrays.
492d1a57
SBS
1010 vbm "STATUS:$fn:--no-store-raw set. Not storing raw.";
1011 vbm "STATUS:$fn:procFileExts array:${procFileExts[*]}";
4e4707fb
SBS
1012 fi;
1013
1014 # Do nothing more if optionProcString not set to true.
1015 if [[ ! "$optionProcString" = "true" ]]; then
80fbd111 1016 vbm "STATUS:$fn:optionProcString not set to 'true'. Returning early.";
4e4707fb
SBS
1017 return; fi;
1018 # Validate input array indices
1019 ## Make sure that argProcStrings and argProcFileExts have same index counts
1020 if ! [[ "${#argProcStrings[@]}" -eq "${#argProcFileExts[@]}" ]]; then
80fbd111
SBS
1021 yell "ERROR:$fn:Mismatch in number of elements in arrays argProcStrings and argProcFileExts:${#argProcStrings[@]} DNE ${#argProcFileExts[@]}";
1022 yell "STATUS:$fn:argProcStrings:${argProcStrings[*]}"; yell "STATUS:$fn:argProcFileExts:${argProcFileExts[*]}"; exit 1; fi;
4e4707fb
SBS
1023 ## Make sure that no array elements are blank
1024 for element in "${argProcStrings[@]}"; do
80fbd111 1025 if [[ -z "$element" ]]; then yell "ERROR:$fn:Empty process string specified. Exiting."; exit 1; fi; done
4e4707fb 1026 for element in "${argProcFileExts[@]}"; do
80fbd111 1027 if [[ -z "$element" ]]; then yell "ERROR:$fn:Empty output file extension specified. Exiting."; exit 1; fi; done
4e4707fb
SBS
1028 ## Make sure that no process string starts with '-' (ex: if only one arg supplied after '-p' option)
1029 for element in "${argProcStrings[@]}"; do
61b243e0 1030 if [[ "$element" =~ ^[-][[:print:]]*$ ]] && [[ ! "$element" =~ ^[[:print:]]*$ ]]; then
80fbd111 1031 yell "ERROR:$fn:Illegal character '-' at start of process string element:\"$element\"";
4e4707fb 1032 exit 1; fi; done;
80fbd111 1033 vbm "STATUS:$fn:Quick check shows argProcStrings and argProcFileExts appear to have valid contents.";
5fb689c8 1034 vbm "STATUS:$fn:argProcStrings:${argProcStrings[*]}"
a2c47ccf 1035 vbm "STATUS:$fn:argProcFileExts:${argProcFileExts[*]}"
492d1a57
SBS
1036 procStrings+=("${argProcStrings[@]}"); # Export process command strings
1037 procFileExts+=("${argProcFileExts[@]}"); # Export process command strings
a2c47ccf
SBS
1038 vbm "STATUS:$fn:procStrings:${procStrings[*]}"
1039 vbm "STATUS:$fn:procFileExts:${procFileExts[*]}"
80fbd111 1040 vbm "STATUS:$fn:Finished magicParseProcessStrings() function.";
4e4707fb 1041} # Validate and save process strings and file extensions to arrays procStrings, procFileExts
236c8b97
SBS
1042magicParseRecipients() {
1043 # Desc: Parses recipient arguments specified by '-r' or '-R' options
1044 # Usage: magicParseRecipients
1045 # In : vars: optionEncrypt, optionRecArg, optionRecDir
1046 # arry: argRecPubKeys (-r), argRecDir (-R)
1047 # Out: vars: cmd_encrypt, cmd_encrypt_suffix
1048 # Depends: head 8.30, checkapp(), checkAgePubkey(), validateInput()
1049 local fn recipients recipientDir recFileLine updateRecipients
1050 local -a recPubKeysValid candRecPubKeysValid
c5da633d 1051
80fbd111
SBS
1052 # Save function name
1053 fn="${FUNCNAME[0]}";
236c8b97 1054 vbm "STATUS:$fn:Starting magicParseRecipients() function.";
80fbd111 1055
236c8b97
SBS
1056 # Catch illegal option combinations
1057 ## Catch case if '-e' is set but neither '-r' nor '-R' is set
1058 if [[ "$optionEncrypt" = "true" ]] && \
1059 ! { [[ "$optionRecArg" = "true" ]] || [[ "$optionRecDir" = "true" ]]; }; then
80fbd111 1060 yell "ERROR:$fn:\\'-e\\' set but no \\'-r\\' or \\'-R\\' set."; exit 1; fi;
236c8b97
SBS
1061 ## Catch case if '-r' or '-R' set but '-e' is not
1062 if [[ ! "$optionEncrypt" = "true" ]] && \
1063 { [[ "$optionRecArg" = "true" ]] || [[ "$optionRecDir" = "true" ]]; }; then
80fbd111 1064 yell "ERROR:$fn:\\'-r\\' or \\'-R\\' set but \\'-e\\' is not set."; exit 1; fi;
80fbd111 1065
236c8b97
SBS
1066 # Handle no encryption cases
1067 if [[ ! "$optionEncrypt" = "true" ]]; then
1068 cmd_encrypt="cat " && vbm "STATUS:$fn:cmd_encrypt:$cmd_encrypt";
1069 cmd_encrypt_suffix="" && vbm "STATUS:$fn:cmd_encrypt_suffix:$cmd_encrypt_suffix";
1070 vbm "DEBUG :$fn:Encryption not enabled.";
1071 return; fi;
1072
1073 # Handle encryption cases
1074 ## Check age availability
1075 if ! checkapp age; then yell "ERROR:$fn:age not available. Exiting."; exit 1; fi
1076 ## Parse '-r' options: validate and append pubkeys from argRecPubKeys to recPubKeysValid
1077 if [[ "$optionRecArg" = "true" ]]; then
1078 for pubkey in "${argRecPubKeys[@]}"; do # Validate recipient pubkey strings by forming test message
1079 vbm "DEBUG :$fn:Testing pubkey string:$pubkey";
1080 if checkAgePubkey "$pubkey" && \
1081 ( validateInput "$pubkey" "ssh_pubkey" || validateInput "$pubkey" "age_pubkey"); then
1082 #### Add validated pubkey to recPubKeysValid array
1083 recPubKeysValid+=("$pubkey") && \
1084 vbm "DEBUG :$fn:recPubkeysValid:pubkey added:$pubkey";
1085 else
1086 yell "ERROR:$fn:Exit code ""$?"". Invalid recipient pubkey string. Exiting."; exit 1;
1087 fi;
1088 done;
1089 vbm "STATUS:$fn:Finished processing argRecPubKeys array";
1090 vbm "DEBUG :$fn:Array of validated pubkeys:${recPubKeysValid[*]}";
1091 fi;
1092 ## Parse '-R' options: validate and append pubkeys in argRecDir to recPubKeysValid
1093 if [[ "$optionRecDir" = "true" ]]; then
1094 ### Check that argRecDir is a directory
c5da633d 1095 if [[ -d "$argRecDir" ]]; then
236c8b97
SBS
1096 recipientDir="$argRecDir" && \
1097 vbm "STATUS:$fn:Recipient watch directory detected:\"$recipientDir\"";
c5da633d
SBS
1098 #### Initialize variable indicating outcome of pubkey review
1099 unset updateRecipients
236c8b97
SBS
1100 #### Add existing recipients from '-r' option
1101 candRecPubKeysValid=("${recPubKeysValid[@]}");
c5da633d
SBS
1102 #### Parse files in recipientDir
1103 for file in "$recipientDir"/*; do
1104 ##### Read first line of each file
236c8b97
SBS
1105 recFileLine="$(head -n1 "$file")" && \
1106 vbm "STATUS:$fn:Checking if pubkey:\"$recFileLine\"";
c5da633d
SBS
1107 ##### check if first line is a valid pubkey
1108 if checkAgePubkey "$recFileLine" && \
1109 ( validateInput "$recFileLine" "ssh_pubkey" || validateInput "$recFileLine" "age_pubkey"); then
1110 ###### T: add candidate pubkey to candRecPubKeysValid
236c8b97
SBS
1111 candRecPubKeysValid+=("$recFileLine") && \
1112 vbm "STATUS:$fn:RecDir pubkey is valid pubkey:\"$recFileLine\"";
c5da633d
SBS
1113 else
1114 ###### F: throw warning;
236c8b97 1115 yell "ERROR:$fn:Invalid recipient file detected. Not modifying recipient list:$recFileLine";
c5da633d
SBS
1116 updateRecipients="false";
1117 fi;
1118 done
236c8b97 1119 #### Write candRecPubKeysValid array to recPubKeysValid if no invalid key detected
c5da633d 1120 if ! [[ "$updateRecipients" = "false" ]]; then
236c8b97
SBS
1121 recPubKeysValid=("${candRecPubKeysValid[@]}") && \
1122 vbm "STATUS:$fn:Wrote candRecPubkeysValid to recPubKeysValid:\"${recPubKeysValid[*]}\"";
c5da633d 1123 fi;
c5da633d
SBS
1124 fi;
1125 fi;
236c8b97
SBS
1126
1127 ## Form age recipient string from recPubKeysValid
1128 for pubkey in "${recPubKeysValid[@]}"; do
1129 recipients="$recipients""-r '$pubkey' ";
1130 vbm "STATUS:$fn:Added pubkey for forming age recipient string:""$pubkey";
1131 vbm "DEBUG :$fn:recipients:""$recipients";
1132 done;
1133
1134 ## Output cmd_encrypt, cmd_encrypt_suffix from recipients
1135 cmd_encrypt="age ""$recipients " && vbm "STATUS:$fn:cmd_encrypt:$cmd_encrypt";
1136 cmd_encrypt_suffix=".age" && vbm "STATUS:$fn:cmd_encrypt_suffix:$cmd_encrypt_suffix";
1137
1138 vbm "STATUS:$fn:Finished magicParseRecipients() function.";
1139} # Sets cmd_encrypt, cmd_encrypt_suffix from -r, -R args
c5da633d
SBS
1140magicSetScriptTTL() {
1141 #Desc: Sets script_TTL seconds from provided time_element string argument
1142 #Usage: magicSetScriptTTL [str time_element]
1143 #Input: arg1: string (Ex: scriptTTL_TE; "day" or "hour")
1144 #Output: var: scriptTTL (integer seconds)
1145 #Depends: timeUntilNextHour, timeUntilNextDay
80fbd111 1146 local fn argTimeElement
6fe054b4 1147
80fbd111
SBS
1148 # Save function name
1149 fn="${FUNCNAME[0]}";
1150
1151 vbm "STATUS:$fn:Starting magicSetScriptTTL() function.";
c5da633d
SBS
1152 argTimeElement="$1";
1153 if [[ "$argTimeElement" = "day" ]]; then
a2c47ccf
SBS
1154 # Set script lifespan to end at start of next day
1155 vbm "STATUS:$fn:Setting script lifespan to end at start of next day. argTimeElement:$argTimeElement";
c5da633d
SBS
1156 if ! scriptTTL="$(timeUntilNextDay)"; then # sets scriptTTL, then checks exit code
1157 if [[ "$scriptTTL" -eq 0 ]]; then
a2c47ccf
SBS
1158 ((scriptTTL++)); # Add 1 because 0 would cause 'timeout' to never timeout.
1159 vbm "STATUS:$fn:scriptTTL:$scriptTTL";
c5da633d 1160 else
80fbd111 1161 yell "ERROR:$fn:timeUntilNextDay exit code $?"; exit 1;
c5da633d
SBS
1162 fi;
1163 fi;
1164 elif [[ "$argTimeElement" = "hour" ]]; then
1165 # Set script lifespan to end at start of next hour
a2c47ccf 1166 vbm "STATUS:$fn:Setting script lifespan to end at start of next hour. argTimeElement:$argTimeElement";
c5da633d
SBS
1167 if ! scriptTTL="$(timeUntilNextHour)"; then # sets scriptTTL, then checks exit code
1168 if [[ "$scriptTTL" -eq 0 ]]; then
1169 ((scriptTTL++)); # Add 1 because 0 would cause 'timeout' to never timeout.
a2c47ccf 1170 vbm "STATUS:$fn:scriptTTL:$scriptTTL";
c5da633d 1171 else
80fbd111 1172 yell "ERROR:$fn:timeUntilNextHour exit code $?"; exit 1;
c5da633d
SBS
1173 fi;
1174 fi;
1175 else
80fbd111 1176 yell "ERROR:$fn:Invalid argument for setScriptTTL function:$argTimeElement"; exit 1;
c5da633d 1177 fi;
80fbd111 1178 vbm "STATUS:$fn:Finished magicSetScriptTTL() function.";
c5da633d 1179} # Set scriptTTL in seconds until next (day|hour).
c5da633d
SBS
1180magicWriteVersion() {
1181 # Desc: Appends time-stamped VERSION to pathout_tar
1182 # Usage: magicWriteVersion
1183 # Input: vars: pathout_tar, dir_tmp
1184 # Input: vars: scriptVersion, scriptURL, ageVersion, ageURL, scriptHostname
1185 # Input: array: recPubKeysValid
1186 # Output: appends tar (pathout_tar)
1187 # Depends: bash 5.0.3, dateTimeShort(), appendArgTar()
80fbd111 1188 local fn fileoutVersion contentVersion pubKeyIndex pubKeyIndex
6fe054b4 1189
80fbd111
SBS
1190 # Save function name
1191 fn="${FUNCNAME[0]}";
1192
1193 vbm "STATUS:$fn:Starting magicWriteVersion() function.";
c5da633d
SBS
1194 # Set VERSION file name
1195 fileoutVersion="$(dateTimeShort)..VERSION";
1196
1197 # Gather VERSION data in contentVersion
1198 contentVersion="scriptVersion=$scriptVersion";
1199 #contentVersion="$contentVersion""\\n";
1200 contentVersion="$contentVersion""\\n""scriptName=$scriptName";
1201 contentVersion="$contentVersion""\\n""scriptURL=$scriptURL";
1202 contentVersion="$contentVersion""\\n""ageVersion=$ageVersion";
1203 contentVersion="$contentVersion""\\n""ageURL=$ageURL";
1204 contentVersion="$contentVersion""\\n""date=$(date --iso-8601=seconds)";
1205 contentVersion="$contentVersion""\\n""hostname=$scriptHostname";
1206 ## Add list of recipient pubkeys
1207 for pubkey in "${recPubKeysValid[@]}"; do
1208 ((pubKeyIndex++))
1209 contentVersion="$contentVersion""\\n""PUBKEY_$pubKeyIndex=$pubkey";
1210 done
1211 ## Process newline escapes
1212 contentVersion="$(echo -e "$contentVersion")"
1213
1214 # Write contentVersion as file fileoutVersion and write-append to pathout_tar
80fbd111
SBS
1215 appendArgTar "$contentVersion" "$fileoutVersion" "$pathout_tar" "$dir_tmp" && \
1216 vbm "STATUS:$fn:Appended $fileoutVersion to $pathout_tar";
1217 vbm "STATUS:$fn:Finished magicWriteVersion() function.";
c5da633d 1218} # write version data to pathout_tar via appendArgTar()
5938a598 1219magicProcessWriteBuffer() {
c5da633d 1220 # Desc: process and write buffer
f5024030
SBS
1221 # In : vars: bufferTTL scriptHostname label dir_tmp SECONDS
1222 # : vars: timeBufferStartEpoch timeBufferEndEpoch
c5da633d
SBS
1223 # : arry: buffer
1224 # Out: file:(pathout_tar)
1225 # Depends: Bash 5.0.3, date 8.30, yell(), vbm(), dateTimeShort(),
1226 ### Note: These arrays should all have the same number of elements:
1227 ### pathouts, fileouts, procFileExts, procStrings
1228
f5024030 1229 local fn timeBufferStartLong timeBufferStart bufferDuration bufferDurationStr fileoutBasename
c5da633d
SBS
1230 local -a fileouts pathouts
1231 local writeCmd1 writeCmd2 writeCmd3 writeCmd4
1232
c5da633d
SBS
1233 # Debug:Get function name
1234 fn="${FUNCNAME[0]}";
80fbd111
SBS
1235
1236 vbm "STATUS:$fn:Started magicProcessWriteBuffer().";
b3dc68f6
SBS
1237 vbm "DEBUG :$fn:buffer array element count:${#buffer[@]}";
1238 vbm "DEBUG :$fn:buffer array first element:${buffer[0]}";
1239 vbm "DEBUG :$fn:buffer array last element :${buffer[-1]}";
1240
c5da633d
SBS
1241 # Determine file paths (time is start of buffer period)
1242 ## Calculate start time
f5024030
SBS
1243 timeBufferStartLong="$(date --date="@$timeBufferStartEpoch" --iso-8601=seconds)" && \
1244 vbm "DEBUG :$fn:timeBufferStartLong:$timeBufferStartLong"; # Note start time in 'date' parsable ISO-8601
c5da633d 1245 timeBufferStart="$(dateTimeShort "$timeBufferStartLong" )" && \
fcefae4c 1246 vbm "DEBUG :$fn:timeBufferStart:$timeBufferStart"; # Note start time YYYYmmddTHHMMSS+zzzz (no separators)
f5024030
SBS
1247 ## Calculate buffer duration string (ISO-8601 duration)
1248 bufferDuration="$((timeBufferEndEpoch - timeBufferStartEpoch))" && \
1249 vbm "DEBUG :$fn:bufferDuration:$bufferDuration"; # length of time (seconds) stdin was read
1250 bufferDurationStr="$(timeDuration "$bufferDuration")" && \
1251 vbm "DEBUG :$fn:bufferDurationStr:$bufferDurationStr"; # buffer duration (ISO-8601)
c5da633d 1252 ## Set common basename
f5024030 1253 fileoutBasename="$timeBufferStart""--""$bufferDurationStr""..""$scriptHostname""$label" && \
80fbd111 1254 vbm "STATUS:$fn:Set fileoutBasename to:$fileoutBasename";
c5da633d
SBS
1255 ## Determine output file name array
1256 ### in: fileOutBasename cmd_compress_suffix cmd_encrypt_suffix procFileExts
1257 for fileExt in "${procFileExts[@]}"; do
1258 fileouts+=("$fileoutBasename""$fileExt""$cmd_compress_suffix""$cmd_encrypt_suffix") && \
80fbd111 1259 vbm "STATUS:$fn:Added $fileExt to fileouts:${fileouts[*]}";
c5da633d
SBS
1260 done;
1261 for fileName in "${fileouts[@]}"; do
1262 pathouts+=("$dir_tmp"/"$fileName") && \
80fbd111 1263 vbm "STATUS:$fn:Added $fileName to pathouts:${pathouts[*]}";
c5da633d
SBS
1264 done;
1265 ## Update pathout_tar
1266 magicInitCheckTar;
1267
1268 # Process and write buffers to dir_tmp
1269 ## Prepare command strings
1270 writeCmd1="printf \"%s\\\\n\" \"\${buffer[@]}\""; # printf "%s\\n" "${buffer[@]}"
1271 #writeCmd2="" # NOTE: Specified by parsing array procStrings
1272 writeCmd3="$cmd_compress";
1273 writeCmd4="$cmd_encrypt";
1274
1275 ## Process buffer and write to dir_tmp
3a5ef93a
SBS
1276 vbm "DEBUG :$fn:fileouts element count:${#fileouts[@]}";
1277 vbm "DEBUG :$fn:pathouts element count:${#pathouts[@]}";
1278 vbm "DEBUG :$fn:procStrings element count:${#pathouts[@]}";
1279 vbm "DEBUG :$fn:fileouts contents:${fileouts[*]}";
1280 vbm "DEBUG :$fn:pathouts contents:${pathouts[*]}";
1281 vbm "DEBUG :$fn:procStrings contents:${pathouts[*]}";
c5da633d 1282 for index in "${!pathouts[@]}"; do
21363b80
SBS
1283 writeCmd2="${procStrings[$index]}";
1284 writeCmdAll="$writeCmd1 | $writeCmd2 | $writeCmd3 | $writeCmd4" && vbm "STATUS:$fn:Assembled command:\"$writeCmdAll\"";
195dd3c3 1285 eval "$writeCmd1 | $writeCmd2 | $writeCmd3 | $writeCmd4" > "${pathouts[$index]}" && vbm "STATUS:$fn:Wrote command output to ${pathouts[$index]}";
c5da633d
SBS
1286 done;
1287
1288 # Append dir_tmp files to pathout_tar
1289 wait; # Wait to avoid collision with older magicProcessWriteBuffer() instances (see https://www.tldp.org/LDP/abs/html/x9644.html )
1290 for index in "${!pathouts[@]}"; do
7177fe18 1291 tar --append --directory="$dir_tmp" --file="$pathout_tar" "${fileouts[$index]}" && \
21363b80 1292 vbm "STATUS:$fn:Appended ${pathouts[$index]} to $pathout_tar";
3a5ef93a 1293 #appendFileTar "${pathouts[$index]}" "${fileouts[$index]}" "$pathout_tar" "$dir_tmp" && \
c5da633d
SBS
1294 done;
1295
1296 # Remove secured chunks from dir_tmp
1297 for path in "${pathouts[@]}"; do
a4bea612 1298 rm "$path" && vbm "STATUS:$fn:Removed:$path";
c5da633d
SBS
1299 done;
1300
80fbd111 1301 vbm "STATUS:$fn:Finished magicProcessWriteBuffer().";
5938a598 1302} # Process and Write buffer
236c8b97
SBS
1303showUsage() {
1304 cat <<'EOF'
1305 USAGE:
1306 cmd | bklog [ options ]
1307
1308 OPTIONS:
1309 -h, --help
1310 Display help information.
1311 --version
1312 Display script version.
1313 -v, --verbose
1314 Display debugging info.
1315 -e, --encrypt
1316 Encrypt output.
1317 -r, --recipient [ string pubkey ]
1318 Specify recipient. May be age or ssh pubkey.
1319 May be specified multiple times for multiple pubkeys.
1320 See https://github.com/FiloSottile/age
1321 -o, --output [ path dir ]
1322 Specify output directory to save logs. This option is required
1323 to save log data.
1324 -p, --process-string [ filter command ] [ output file extension]
1325 Specify how to create and name a processed version of the stdin.
1326 For example, if stdin is 'nmea' location data:
1327
1328 -p "gpsbabel -i nmea -f - -o gpx -F - " ".gpx"
1329
1330 This option would cause the stdin to 'bklog' to be piped into
1331 the 'gpsbabel' command, interpreted as 'nmea' data, converted
1332 into 'gpx' format, and then appended to the output tar file
1333 as a file with a '.gpx' extension.
1334 This option may be specified multiple times in order to output
1335 results of multiple different processing methods.
1336 -l, --label [ string ]
1337 Specify a label to be included in all output file names.
1338 Ex: 'location' if stdin is location data.
1339 -w, --store-raw [ file extension ]
1340 Specify file extension of file within output tar that contains
1341 raw stdin data. The default behavior is to always save raw stdin
1342 data in a '.stdin' file. Example usage when 'bklog' receives
1343 'nmea' data from 'gpspipe -r':
1344
1345 -w ".nmea"
1346
1347 Stdin data is saved in a '.nmea' file within the output tar.
1348 -W, --no-store-raw
1349 Do not store raw stdin in output tar.
1350 -c, --compress
1351 Compress output with gzip (before encryption if enabled).
1352 -z, --time-zone
1353 Specify time zone. (ex: "America/New_York")
1354 -t, --temp-dir [path dir]
1355 Specify parent directory for temporary working directory.
1356 Default: "/dev/shm"
1357 -R, --recipient-dir [path dir]
1358 Specify directory containing files whose first lines are
1359 to be interpreted as pubkey strings (see '-r' option). Only
1360 one directory may be specified.
1361 -b, --buffer-ttl [integer]
1362 Specify custom buffer period in seconds (default: 300 seconds)
1363 -B, --script-ttl [time element string]
1364 Specify custom script time-to-live in seconds (default: "day")
1365 Valid values: "day", "hour"
1366
1367 EXAMPLE: (bash script lines)
1368 $ gpspipe -r | /bin/bash bklog -v -e -c -z "UTC" -t "/dev/shm" \
1369 -r age1mrmfnwhtlprn4jquex0ukmwcm7y2nxlphuzgsgv8ew2k9mewy3rs8u7su5 \
1370 -r age1ala848kqrvxc88rzaauc6vc5v0fqrvef9dxyk79m0vjea3hagclswu0lgq \
1371 -R ~/.config/bklog/recipients -w ".nmea" -b 300 -B "day" \
1372 -o ~/Sync/Logs -l "location" \
1373 -p "gpsbabel -i nmea -f - -o gpx -F - " ".gpx" \
1374 -p "gpsbabel -i nmea -f - -o kml -F - " ".kml"
1375EOF
1376} # Display information on how to use this script.
4e4707fb 1377
5938a598 1378main() {
80fbd111
SBS
1379 # Desc: Main function
1380 # Usage: main "$@"
1381 # Inputs: many
1382 # Outputs: file (pathout_tar)
1383 # Depends: many
1384 local fn
1385
1386 # Debug:Get function name
1387 fn="${FUNCNAME[0]}";
1388
1389 vbm "STATUS:$fn:Started function main().";
c5da633d
SBS
1390 # Process arguments
1391 processArguments "$@";
1392 ## Determine working directory
1393 magicInitWorkingDir; # Sets dir_tmp from argTempDirPriority
1394 ## Set output encryption and compression option strings
236c8b97
SBS
1395 ### React to "-e", "-r", and "-R" (encryption recipients) options
1396 magicParseRecipients; # Update cmd_encrypt, cmd_encrypt_suffix
c5da633d
SBS
1397 ### React to "-c" ("compression") option
1398 magicParseCompressionArg; # Updates cmd_compress[_suffix]
1399 ## React to "-b" and "-B" (custom buffer and script TTL) options
1400 magicParseCustomTTL; # Sets custom scriptTTL_TE and/or bufferTTL if specified
1401 ## React to "-p" (user-supplied process command and file extension strings) options
1402 magicParseProcessStrings; # Sets arrays: procStrings, procFileExts
1403 ## React to "-l" (output file label) option
1404 magicParseLabel; # sets label (ex: "_location")
c5da633d
SBS
1405
1406 # Perform secondary setup operations
1407 ## Set script lifespan (scriptTTL from scriptTTL_TE)
1408 magicSetScriptTTL "$scriptTTL_TE";
f5024030
SBS
1409 ## Adjust SECONDS so buffer rounds align with time elements
1410 ### Advance SECONDS the remainder seconds for dividend timeUntilNextDay, divisor bufferTTL
1411 if [[ "$(timeUntilNextDay)" -gt "$bufferTTL" ]]; then
1412 vbm "DEBUG :$fn:SECONDS currently :$SECONDS";
1413 SECONDS="$(( $(timeUntilNextDay) % bufferTTL ))" && \
1414 vbm "DEBUG :$fn:SECONDS advanced to:$SECONDS";
1415 fi;
c5da633d 1416 ## Init temp working dir
fcefae4c 1417 try mkdir "$dir_tmp" && vbm "DEBUG :$fn:Working dir created at dir_tmp:$dir_tmp";
c5da633d 1418 ## Initialize output tar (set pathout_tar)
195dd3c3
SBS
1419 magicInitCheckTar;
1420 ## Append VERSION file to tar
1421 magicWriteVersion;
c5da633d
SBS
1422
1423 # Check vital apps, files, dirs
1424 if ! checkapp tar && ! checkdir "$dirOut" "dir_tmp"; then
80fbd111 1425 yell "ERROR:$fn:Critical components missing.";
c5da633d
SBS
1426 displayMissing; yell "Exiting."; exit 1; fi
1427
1428 # MAIN LOOP: Run until script TTL seconds pass
5938a598 1429 bufferRound=0;
5938a598 1430 while [[ $SECONDS -lt "scriptTTL" ]]; do
80fbd111 1431 vbm "STATUS:$fn:Starting buffer round:$bufferRound";
f5024030
SBS
1432 bufferTOD="$(( (1+bufferRound)*bufferTTL ))"; # Set buffer round time-of-death
1433 # Note start time of data collection
1434 timeBufferStartEpoch="$(date +%s)";
5938a598
SBS
1435 # Consume stdin to fill buffer until buffer time-of-death (TOD) arrives
1436 while read -r -t "$bufferTTL" line && [[ $SECONDS -lt "$bufferTOD" ]]; do
1437 # Append line to buffer array
1438 buffer+=("$line");
5938a598 1439 done;
f5024030
SBS
1440 # Note end time of data collection
1441 timeBufferEndEpoch="$(date +%s)";
c5da633d 1442 # Create dir_tmp if missing
80fbd111
SBS
1443 if ! [[ -d "$dir_tmp" ]]; then
1444 yell "ERROR:$fn:dir_tmp existence failure:$dir_tmp";
fcefae4c 1445 try mkdir "$dir_tmp" && vbm "DEBUG :$fn:Working dir recreated dir_tmp:$dir_tmp"; fi
236c8b97
SBS
1446 # Update cmd_encrypt, cmd_encrypt_suffix
1447 magicParseRecipients;
5938a598
SBS
1448 # Export buffer to asynchronous processing.
1449 magicProcessWriteBuffer &
1450 unset buffer; # Clear buffer array for next bufferRound
1451 # Increment buffer round
1452 ((bufferRound++));
1453 done;
c5da633d
SBS
1454
1455 # Cleanup
1456 ## Remove dir_tmp
80fbd111 1457 try rm -r "$dir_tmp" && vbm "STATUS:$fn:Removed dir_tmp:$dir_tmp";
c5da633d 1458
80fbd111 1459 vbm "STATUS:$fn:Finished function main().";
c5da633d 1460} # Main function
6cbe7c0a 1461
5938a598
SBS
1462#===END Declare local script functions===
1463#==END Define script parameters==
1464
1465#==BEGIN Perform work and exit==
1466main "$@" # Run main function.
1467exit 0;
1468#==END Perform work and exit==
1469
1470# Author: Steven Baltakatei Sandoval;
6cbe7c0a 1471# License: GPLv3+