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