chore(bklog):Remove extraneous echoerr references
[EVA-2020-02.git] / exec / bklog
1 #!/bin/bash
2 # Desc: Compresses, encrypts, and writes stdin every 5 seconds
3
4 #==BEGIN Define script parameters==
5 #===BEGIN Initialize variables===
6
7 # Logging Behavior parameters
8 bufferTTL="300"; # Time-to-live (seconds) for each buffer round
9 scriptTTL_TE="day"; # Time element at the end of which script terminates
10 dirTmpDefault="/dev/shm"; # Default parent of working directory
11
12 # Script Metadata
13 scriptName="bklog"; # Define basename of script file.
14 scriptVersion="0.1.4"; # Define version of script.
15 scriptURL="https://gitlab.com/baltakatei/ninfacyzga-01"; # Define wesite hosting this script.
16 scriptTimeStart="$(date +%Y%m%dT%H%M%S.%N)"; # YYYYmmddTHHMMSS.NNNNNNNNN
17 scriptHostname=$(hostname); # Save hostname of system running this script.
18 PATH="$HOME/.local/bin:$PATH"; # Add "$(systemd-path user-binaries)" path in case user apps saved there
19 ageVersion="1.0.0-beta2"; # Define version of age (encryption program)
20 ageURL="https://github.com/FiloSottile/age/releases/tag/v1.0.0-beta2"; # Define website hosting age.
21
22 # Arrays
23 declare -a buffer # array for storing while read buffer
24 declare -a argRecPubKeys # array for processArguments function
25 declare -a recPubKeysValid # array for storing both '-r' and '-R' recipient pubkeys
26 declare -a recPubKeysValidStatic # for storing '-r' recipient pubkeys
27 declare -a argProcStrings argProcFileExts # for storing buffer processing strings (ex: "gpsbabel -i nmea -f - -o gpx -F - ")
28 declare -Ag appRollCall # Associative array for storing app status
29 declare -Ag fileRollCall # Associative array for storing file status
30 declare -Ag dirRollCall # Associative array for storing dir status
31 declare -a procStrings procFileExts # Arrays for storing processing commands and resulting output file extensions
32
33 # Variables
34 optionVerbose=""; optionEncrypt=""; dirOut=""; optionEncrypt=""; dir_tmp="";
35 cmd_compress="";cmd_compress_suffix=""; cmd_encrypt=""; cmd_encrypt_suffix="";
36
37 #===END Initialize variables===
38
39 #===BEGIN Declare local script functions===
40 yell() { echo "$0: $*" >&2; } #o Yell, Die, Try Three-Fingered Claw technique
41 die() { yell "$*"; exit 111; } #o Ref/Attrib: https://stackoverflow.com/a/25515370
42 try() { "$@" || die "cannot $*"; } #o
43 processArguments() {
44 while [ ! $# -eq 0 ]; do # While number of arguments ($#) is not (!) equal to (-eq) zero (0).
45 case "$1" in
46 -v | --verbose) optionVerbose="true"; vbm "DEBUG:Verbose mode enabled.";; # Enable verbose mode.
47 -h | --help) showUsage; exit 1;; # Display usage.
48 --version) showVersion; exit 1;; # Show version
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
51 -r | --recipient) optionRecipients="true"; argRecPubKeys+=("$2"); vbm "STATUS:pubkey added:""$2"; shift;; # Add recipients
52 -c | --compress) optionCompress="true"; vbm "DEBUG:Compressed output mode enabled.";; # Enable compression
53 -z | --time-zone) try setTimeZoneEV "$2"; shift;; # Set timestamp timezone
54 -t | --temp-dir) optionTmpDir="true" && argTempDirPriority="$2"; shift;; # Set time zone
55 -R | --recipient-dir) optionRecipients="true"; optionRecDir="true" && argRecDir="$2"; shift;; # Add recipient watch dir
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")
58 -p | --process-string) optionProcString="true" && argProcStrings+=("$2") && argProcFileExts+=("$3") && vbm "STATUS:file extension \"$2\" for output of processing string added:\"$3\""; shift; shift;;
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;;
61 -W | --no-store-raw) optionNoStoreRaw="true"; vbm "DEBUG:Option selected to not store raw stdin data."; shift;;
62 *) yell "ERROR: Unrecognized argument: $1"; yell "STATUS:All arguments:$*"; exit 1;; # Handle unrecognized options.
63 esac
64 shift
65 done
66 } # Argument Processing
67 vbm() {
68 # Description: Prints verbose message ("vbm") to stderr if optionVerbose is set to "true".
69 # Usage: vbm "DEBUG:verbose message here"
70 # Version 0.1.2
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.
78 echo "[$functionTime] ""$*" 1>&2; # Display argument text.
79 fi
80
81 # End function
82 return 0; # Function finished.
83 } # Displays message if optionVerbose true
84 checkapp() {
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
110 checkfile() {
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
137 checkdir() {
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
164 displayMissing() {
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 :";
177 #for key in "${!appRollCall[@]}"; do echo "DEBUG:$key => ${appRollCall[$key]}"; done
178 for key in "${!appRollCall[@]}"; do
179 value="${appRollCall[$key]}";
180 if [ "$value" = "false" ]; then
181 #echo "DEBUG:Missing apps: $key => $value";
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:";
194 #for key in "${!fileRollCall[@]}"; do echo "DEBUG:$key => ${fileRollCall[$key]}"; done
195 for key in "${!fileRollCall[@]}"; do
196 value="${fileRollCall[$key]}";
197 if [ "$value" = "false" ]; then
198 #echo "DEBUG:Missing files: $key => $value";
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:";
211 #for key in "${!dirRollCall[@]}"; do echo "DEBUG:$key => ${dirRollCall[$key]}"; done
212 for key in "${!dirRollCall[@]}"; do
213 value="${dirRollCall[$key]}";
214 if [ "$value" = "false" ]; then
215 #echo "DEBUG:Missing dirs: $key => $value";
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
228 showUsage() {
229 cat <<'EOF'
230 USAGE:
231 cmd | bklog [ options ]
232
233 OPTIONS:
234 -h, --help
235 Display help information.
236 --version
237 Display script version.
238 -v, --verbose
239 Display debugging info.
240 -e, --encrypt
241 Encrypt output.
242 -r, --recipient [ string pubkey ]
243 Specify recipient. May be age or ssh pubkey.
244 May be specified multiple times for multiple pubkeys.
245 See https://github.com/FiloSottile/age
246 -o, --output [ path dir ]
247 Specify output directory to save logs. This option is required
248 to save log data.
249 -p, --process-string [ filter command ] [ output file extension]
250 Specify how to create and name a processed version of the stdin.
251 For example, if stdin is 'nmea' location data:
252
253 -p "gpsbabel -i nmea -f - -o gpx -F - " ".gpx"
254
255 This option would cause the stdin to 'bklog' to be piped into
256 the 'gpsbabel' command, interpreted as 'nmea' data, converted
257 into 'gpx' format, and then appended to the output tar file
258 as a file with a '.gpx' extension.
259 This option may be specified multiple times in order to output
260 results of multiple different processing methods.
261 -l, --label [ string ]
262 Specify a label to be included in all output file names.
263 Ex: 'location' if stdin is location data.
264 -w, --store-raw [ file extension ]
265 Specify file extension of file within output tar that contains
266 raw stdin data. The default behavior is to always save raw stdin
267 data in a '.stdin' file. Example usage when 'bklog' receives
268 'nmea' data from 'gpspipe -r':
269
270 -w ".nmea"
271
272 Stdin data is saved in a '.nmea' file within the output tar.
273 -W, --no-store-raw
274 Do not store raw stdin in output tar.
275 -c, --compress
276 Compress output with gzip (before encryption if enabled).
277 -z, --time-zone
278 Specify time zone. (ex: "America/New_York")
279 -t, --temp-dir [path dir]
280 Specify parent directory for temporary working directory.
281 Default: "/dev/shm"
282 -R, --recipient-dir [path dir]
283 Specify directory containing files whose first lines are
284 to be interpreted as pubkey strings (see '-r' option).
285 -b, --buffer-ttl [integer]
286 Specify custom buffer period in seconds (default: 300 seconds)
287 -B, --script-ttl [time element string]
288 Specify custom script time-to-live in seconds (default: "day")
289 Valid values: "day", "hour"
290
291 EXAMPLE: (bash script lines)
292 $ gpspipe -r | /bin/bash bklog -v -e -c -z "UTC" -t "/dev/shm" \
293 -r age1mrmfnwhtlprn4jquex0ukmwcm7y2nxlphuzgsgv8ew2k9mewy3rs8u7su5 \
294 -r age1ala848kqrvxc88rzaauc6vc5v0fqrvef9dxyk79m0vjea3hagclswu0lgq \
295 -R ~/.config/bklog/recipients -w ".nmea" -b 300 -B "day" \
296 -o ~/Sync/Logs -l "location" \
297 -p "gpsbabel -i nmea -f - -o gpx -F - " ".gpx" \
298 -p "gpsbabel -i nmea -f - -o kml -F - " ".kml"
299 EOF
300 } # Display information on how to use this script.
301 showVersion() {
302 yell "$scriptVersion"
303 } # Display script version.
304 setTimeZoneEV(){
305 # Desc: Set time zone environment variable TZ
306 # Usage: setTimeZoneEV arg1
307 # Version 0.1.2
308 # Input: arg1: 'date'-compatible timezone string (ex: "America/New_York")
309 # TZDIR env var (optional; default: "/usr/share/zoneinfo")
310 # Output: exports TZ
311 # exit code 0 on success
312 # exit code 1 on incorrect number of arguments
313 # exit code 2 if unable to validate arg1
314 # Depends: yell(), printenv 8.30, bash 5.0.3
315 # Tested on: Debian 10
316 local tzDir returnState argTimeZone
317
318 argTimeZone="$1"
319 if ! [[ $# -eq 1 ]]; then
320 yell "ERROR:Invalid argument count.";
321 return 1;
322 fi
323
324 # Read TZDIR env var if available
325 if printenv TZDIR 1>/dev/null 2>&1; then
326 tzDir="$(printenv TZDIR)";
327 else
328 tzDir="/usr/share/zoneinfo";
329 fi
330
331 # Validate TZ string
332 if ! [[ -f "$tzDir"/"$argTimeZone" ]]; then
333 yell "ERROR:Invalid time zone argument.";
334 return 2;
335 else
336 # Export ARG1 as TZ environment variable
337 TZ="$argTimeZone" && export TZ && returnState="true";
338 fi
339
340 # Determine function return code
341 if [ "$returnState" = "true" ]; then
342 return 0;
343 fi
344 } # Exports TZ environment variable
345 dateShort(){
346 # Desc: Date without separators (YYYYmmdd)
347 # Usage: dateShort ([str date])
348 # Version: 1.1.2
349 # Input: arg1: 'date'-parsable timestamp string (optional)
350 # Output: stdout: date (ISO-8601, no separators)
351 # Depends: bash 5.0.3, date 8.30, yell()
352 local argTime timeCurrent timeInput dateCurrentShort
353
354 argTime="$1";
355 # Get Current Time
356 timeCurrent="$(date --iso-8601=seconds)" ; # Produce `date`-parsable current timestamp with resolution of 1 second.
357 # Decide to parse current or supplied date
358 ## Check if time argument empty
359 if [[ -z "$argTime" ]]; then
360 ## T: Time argument empty, use current time
361 timeInput="$timeCurrent";
362 else
363 ## F: Time argument exists, validate time
364 if date --date="$argTime" 1>/dev/null 2>&1; then
365 ### T: Time argument is valid; use it
366 timeInput="$argTime";
367 else
368 ### F: Time argument not valid; exit
369 yell "ERROR:Invalid time argument supplied. Exiting."; exit 1;
370 fi;
371 fi;
372 # Construct and deliver separator-les date string
373 dateCurrentShort="$(date -d "$timeInput" +%Y%m%d)"; # Produce separator-less current date with resolution 1 day.
374 echo "$dateCurrentShort";
375 } # Get YYYYmmdd
376 appendFileTar(){
377 # Desc: Appends [processed] file to tar
378 # Usage: appendFileTar [file path] [name of file to be inserted] [tar path] [temp dir] ([process cmd])
379 # Version: 2.0.1
380 # Input: arg1: path of file to be (processed and) written
381 # arg2: name to use for file inserted into tar
382 # arg3: tar archive path (must exist first)
383 # arg4: temporary working dir
384 # arg5: (optional) command string to process file (ex: "gpsbabel -i nmea -f - -o kml -F - ")
385 # Output: file written to disk
386 # Example: decrypt multiple large files in parallel
387 # appendFileTar /tmp/largefile1.gpg "largefile1" $HOME/archive.tar /tmp "gpg --decrypt" &
388 # appendFileTar /tmp/largefile2.gpg "largefile2" $HOME/archive.tar /tmp "gpg --decrypt" &
389 # appendFileTar /tmp/largefile3.gpg "largefile3" $HOME/archive.tar /tmp "gpg --decrypt" &
390 # Depends: bash 5.0.3, tar 1.30, cat 8.30, yell()
391 local fn fileName tarPath tmpDir
392
393 # Save function name
394 fn="${FUNCNAME[0]}";
395 #yell "DEBUG:STATUS:$fn:Started appendFileTar()."
396
397 # Set file name
398 if ! [ -z "$2" ]; then fileName="$2"; else yell "ERROR:$fn:Not enough arguments."; exit 1; fi
399 # Check tar path is a file
400 if [ -f "$3" ]; then tarPath="$3"; else yell "ERROR:$fn:Tar archive arg not a file:$3"; exit 1; fi
401 # Check temp dir arg
402 if ! [ -z "$4" ]; then tmpDir="$4"; else yell "ERROR:$fn:No temporary working dir set."; exit 1; fi
403 # Set command strings
404 if ! [ -z "$5" ]; then cmd1="$5"; else cmd1="cat "; fi # command string
405
406 # Input command string
407 cmd0="cat \"\$1\"";
408
409 # Write to temporary working dir
410 eval "$cmd0 | $cmd1" > "$tmpDir"/"$fileName";
411
412 # Append to tar
413 try tar --append --directory="$tmpDir" --file="$tarPath" "$fileName";
414 #yell "DEBUG:STATUS:$fn:Finished appendFileTar()."
415 } # Append [processed] file to Tar archive
416 timeDuration(){
417 # Desc: Given seconds, output ISO-8601 duration string
418 # Ref/Attrib: ISO-8601:2004(E), §4.4.4.2 Representations of time intervals by duration and context information
419 # Note: "1 month" ("P1M") is assumed to be "30 days" (see ISO-8601:2004(E), §2.2.1.2)
420 # Usage: timeDuration [1:seconds] ([2:precision])
421 # Version: 1.0.4
422 # Input: arg1: seconds as base 10 integer >= 0 (ex: 3601)
423 # arg2: precision level (optional; default=2)
424 # Output: stdout: ISO-8601 duration string (ex: "P1H1S", "P2Y10M15DT10H30M20S")
425 # exit code 0: success
426 # exit code 1: error_input
427 # exit code 2: error_unknown
428 # Example: 'timeDuration 111111 3' yields 'P1DT6H51M'
429 # Depends: date 8, bash 5, yell,
430 local argSeconds argPrecision precision returnState remainder
431 local fullYears fullMonths fullDays fullHours fullMinutes fullSeconds
432 local hasYears hasMonths hasDays hasHours hasMinutes hasSeconds
433 local witherPrecision output
434 local displayYears displayMonths displayDays displayHours displayMinutes displaySeconds
435
436 argSeconds="$1"; # read arg1 (seconds)
437 argPrecision="$2"; # read arg2 (precision)
438 precision=2; # set default precision
439
440 # Check that between one and two arguments is supplied
441 if ! { [[ $# -ge 1 ]] && [[ $# -le 2 ]]; }; then
442 yell "ERROR:Invalid number of arguments:$# . Exiting.";
443 returnState="error_input"; fi
444
445 # Check that argSeconds provided
446 if [[ $# -ge 1 ]]; then
447 ## Check that argSeconds is a positive integer
448 if [[ "$argSeconds" =~ ^[[:digit:]]+$ ]]; then
449 :
450 else
451 yell "ERROR:argSeconds not a digit.";
452 returnState="error_input";
453 fi
454 else
455 yell "ERROR:No argument provided. Exiting.";
456 exit 1;
457 fi
458
459 # Consider whether argPrecision was provided
460 if [[ $# -eq 2 ]]; then
461 # Check that argPrecision is a positive integer
462 if [[ "$argPrecision" =~ ^[[:digit:]]+$ ]] && [[ "$argPrecision" -gt 0 ]]; then
463 precision="$argPrecision";
464 else
465 yell "ERROR:argPrecision not a positive integer. (is $argPrecision ). Leaving early.";
466 returnState="error_input";
467 fi;
468 else
469 :
470 fi;
471
472 remainder="$argSeconds" ; # seconds
473 ## Calculate full years Y, update remainder
474 fullYears=$(( remainder / (365*24*60*60) ));
475 remainder=$(( remainder - (fullYears*365*24*60*60) ));
476 ## Calculate full months M, update remainder
477 fullMonths=$(( remainder / (30*24*60*60) ));
478 remainder=$(( remainder - (fullMonths*30*24*60*60) ));
479 ## Calculate full days D, update remainder
480 fullDays=$(( remainder / (24*60*60) ));
481 remainder=$(( remainder - (fullDays*24*60*60) ));
482 ## Calculate full hours H, update remainder
483 fullHours=$(( remainder / (60*60) ));
484 remainder=$(( remainder - (fullHours*60*60) ));
485 ## Calculate full minutes M, update remainder
486 fullMinutes=$(( remainder / (60) ));
487 remainder=$(( remainder - (fullMinutes*60) ));
488 ## Calculate full seconds S, update remainder
489 fullSeconds=$(( remainder / (1) ));
490 remainder=$(( remainder - (remainder*1) ));
491 ## Check which fields filled
492 if [[ $fullYears -gt 0 ]]; then hasYears="true"; else hasYears="false"; fi
493 if [[ $fullMonths -gt 0 ]]; then hasMonths="true"; else hasMonths="false"; fi
494 if [[ $fullDays -gt 0 ]]; then hasDays="true"; else hasDays="false"; fi
495 if [[ $fullHours -gt 0 ]]; then hasHours="true"; else hasHours="false"; fi
496 if [[ $fullMinutes -gt 0 ]]; then hasMinutes="true"; else hasMinutes="false"; fi
497 if [[ $fullSeconds -gt 0 ]]; then hasSeconds="true"; else hasSeconds="false"; fi
498
499 ## Determine which fields to display (see ISO-8601:2004 §4.4.3.2)
500 witherPrecision="false"
501
502 ### Years
503 if $hasYears && [[ $precision -gt 0 ]]; then
504 displayYears="true";
505 witherPrecision="true";
506 else
507 displayYears="false";
508 fi;
509 if $witherPrecision; then ((precision--)); fi;
510
511 ### Months
512 if $hasMonths && [[ $precision -gt 0 ]]; then
513 displayMonths="true";
514 witherPrecision="true";
515 else
516 displayMonths="false";
517 fi;
518 if $witherPrecision && [[ $precision -gt 0 ]]; then
519 displayMonths="true";
520 fi;
521 if $witherPrecision; then ((precision--)); fi;
522
523 ### Days
524 if $hasDays && [[ $precision -gt 0 ]]; then
525 displayDays="true";
526 witherPrecision="true";
527 else
528 displayDays="false";
529 fi;
530 if $witherPrecision && [[ $precision -gt 0 ]]; then
531 displayDays="true";
532 fi;
533 if $witherPrecision; then ((precision--)); fi;
534
535 ### Hours
536 if $hasHours && [[ $precision -gt 0 ]]; then
537 displayHours="true";
538 witherPrecision="true";
539 else
540 displayHours="false";
541 fi;
542 if $witherPrecision && [[ $precision -gt 0 ]]; then
543 displayHours="true";
544 fi;
545 if $witherPrecision; then ((precision--)); fi;
546
547 ### Minutes
548 if $hasMinutes && [[ $precision -gt 0 ]]; then
549 displayMinutes="true";
550 witherPrecision="true";
551 else
552 displayMinutes="false";
553 fi;
554 if $witherPrecision && [[ $precision -gt 0 ]]; then
555 displayMinutes="true";
556 fi;
557 if $witherPrecision; then ((precision--)); fi;
558
559 ### Seconds
560
561 if $hasSeconds && [[ $precision -gt 0 ]]; then
562 displaySeconds="true";
563 witherPrecision="true";
564 else
565 displaySeconds="false";
566 fi;
567 if $witherPrecision && [[ $precision -gt 0 ]]; then
568 displaySeconds="true";
569 fi;
570 if $witherPrecision; then ((precision--)); fi;
571
572 ## Determine whether or not the "T" separator is needed to separate date and time elements
573 if ( $displayHours || $displayMinutes || $displaySeconds); then
574 displayDateTime="true"; else displayDateTime="false"; fi
575
576 ## Construct duration output string
577 output="P"
578 if $displayYears; then
579 output=$output$fullYears"Y"; fi
580 if $displayMonths; then
581 output=$output$fullMonths"M"; fi
582 if $displayDays; then
583 output=$output$fullDays"D"; fi
584 if $displayDateTime; then
585 output=$output"T"; fi
586 if $displayHours; then
587 output=$output$fullHours"H"; fi
588 if $displayMinutes; then
589 output=$output$fullMinutes"M"; fi
590 if $displaySeconds; then
591 output=$output$fullSeconds"S"; fi
592
593 ## Output duration string to stdout
594 echo "$output" && returnState="true";
595
596 #===Determine function return code===
597 if [ "$returnState" = "true" ]; then
598 return 0;
599 elif [ "$returnState" = "error_input" ]; then
600 yell "ERROR:input";
601 return 1;
602 else
603 yell "ERROR:Unknown";
604 return 2;
605 fi
606
607 } # Get duration (ex: PT10M4S )
608 magicInitWorkingDir() {
609 # Desc: Determine temporary working directory from defaults or user input
610 # Usage: magicInitWorkingDir
611 # Input: vars: optionTmpDir, argTempDirPriority, dirTmpDefault
612 # Input: vars: scriptTimeStart
613 # Output: vars: dir_tmp
614 # Depends: bash 5.0.3, processArguments(), vbm(), yell()
615 # Parse '-t' option (user-specified temporary working dir)
616 ## Set dir_tmp_parent to user-specified value if specified
617 local dir_tmp_parent
618
619 if [[ "$optionTmpDir" = "true" ]]; then
620 if [[ -d "$argTempDirPriority" ]]; then
621 dir_tmp_parent="$argTempDirPriority";
622 else
623 yell "WARNING:Specified temporary working directory not valid:$argTempDirPriority";
624 exit 1; # Exit since user requires a specific temp dir and it is not available.
625 fi;
626 else
627 ## Set dir_tmp_parent to default or fallback otherwise
628 if [[ -d "$dirTmpDefault" ]]; then
629 dir_tmp_parent="$dirTmpDefault";
630 elif [[ -d /tmp ]]; then
631 yell "WARNING:$dirTmpDefault not available. Falling back to /tmp .";
632 dir_tmp_parent="/tmp";
633 else
634 yell "ERROR:No valid working directory available. Exiting.";
635 exit 1;
636 fi;
637 fi;
638 ## Set dir_tmp using dir_tmp_parent and nonce (scriptTimeStart)
639 dir_tmp="$dir_tmp_parent"/"$scriptTimeStart""..bkgpslog" && vbm "DEBUG:Set dir_tmp to:$dir_tmp"; # Note: removed at end of main().
640 } # Sets working dir
641 magicInitCheckTar() {
642 # Desc: Initializes or checks output tar
643 # input: vars: dirOut, bufferTTL, cmd_encrypt_suffix, cmd_compress_suffix
644 # input: vars: scriptHostname
645 # output: vars: pathout_tar
646 # depends: Bash 5.0.3, vbm(), dateShort(), checkMakeTar(), magicWriteVersion()
647
648 # Form pathout_tar
649 pathout_tar="$dirOut"/"$(dateShort "$(date --date="$bufferTTL seconds ago" --iso-8601=seconds)")".."$scriptHostname""$label""$cmd_compress_suffix""$cmd_encrypt_suffix".tar && \
650 vbm "STATUS:Set pathout_tar to:$pathout_tar";
651 # Validate pathout_tar as tar.
652 checkMakeTar "$pathout_tar";
653 ## Add VERSION file if checkMakeTar had to create a tar (exited 1) or replace one (exited 2)
654 vbm "exit status before magicWriteVersion:$?"
655 if [[ $? -eq 1 ]] || [[ $? -eq 2 ]]; then magicWriteVersion; fi
656 } # Initialize tar, set pathout_tar
657 magicParseCompressionArg() {
658 # Desc: Parses compression arguments specified by '-c' option
659 # Input: vars: optionCompress
660 # Output: cmd_compress, cmd_compress_suffix
661 # Depends: processArguments(), vbm(), checkapp(), gzip 1.9
662 if [[ "$optionCompress" = "true" ]]; then # Check if compression option active
663 if checkapp gzip; then # Check if gzip available
664 cmd_compress="gzip " && vbm "cmd_compress:$cmd_compress";
665 cmd_compress_suffix=".gz" && vbm "cmd_compress_suffix:$cmd_compress_suffix";
666 else
667 yell "ERROR:Compression enabled but \"gzip\" not found. Exiting."; exit 1;
668 fi
669 else
670 cmd_compress="tee /dev/null " && vbm "cmd_compress:$cmd_compress";
671 cmd_compress_suffix="" && vbm "cmd_compress_suffix:$cmd_compress_suffix";
672 vbm "DEBUG:Compression not enabled.";
673 fi
674 } # Form compression cmd string and filename suffix
675 magicParseCustomTTL() {
676 # Desc: Set user-specified TTLs for buffer and script
677 # Usage: magicParseCustomTTL
678 # Input: vars: argCustomBufferTTL (integer), argCustomScriptTTL_TE (string)
679 # Input: vars: optionCustomBufferTTL, optionCustomScriptTTL_TE
680 # Input: vars: bufferTTL (integer), scriptTTL_TE (string)
681 # Output: bufferTTL (integer), scriptTTL_TE (string)
682 # Depends: Bash 5.0.3, yell(), vbm(), validateInput(), showUsage()
683
684 # React to '-b, --buffer-ttl' option
685 if [[ "$optionCustomBufferTTL" = "true" ]]; then
686 ## T: Check if argCustomBufferTTL is an integer
687 if validateInput "$argCustomBufferTTL" "integer"; then
688 ### T: argCustomBufferTTL is an integer
689 bufferTTL="$argCustomBufferTTL" && vbm "Custom bufferTTL from -b:$bufferTTL";
690 else
691 ### F: argcustomBufferTTL is not an integer
692 yell "ERROR:Invalid integer argument for custom buffer time-to-live."; showUsage; exit 1;
693 fi;
694 ## F: do not change bufferTTL
695 fi;
696
697 # React to '-B, --script-ttl' option
698 if [[ "$optionCustomScriptTTL_TE" = "true" ]]; then
699 ## T: Check if argCustomScriptTTL is a time element (ex: "day", "hour")
700 if validateInput "$argCustomScriptTTL_TE" "time_element"; then
701 ### T: argCustomScriptTTL is a time element
702 scriptTTL_TE="$argCustomScriptTTL_TE" && vbm "Custom scriptTTL_TE from -B:$scriptTTL_TE";
703 else
704 ### F: argcustomScriptTTL is not a time element
705 yell "ERROR:Invalid time element argument for custom script time-to-live."; showUsage; exit 1;
706 fi;
707 ## F: do not change scriptTTL_TE
708 fi;
709 } # Sets custom script or buffer TTL if specified
710 magicParseLabel() {
711 # Desc: Parses -l option to set label
712 # In : optionLabel, argLabel
713 # Out: vars: label
714 # Depends: Bash 5.0.3, vbm(), yell()
715
716 vbm "STATUS:Started magicParseLabel() function.";
717 # Do nothing if optionLabel not set to true.
718 if [[ ! "$optionLabel" = "true" ]]; then
719 vbm "STATUS:optionlabel not set to 'true'. Returning early.";
720 return;
721 fi;
722 # Set label if optionLabel is true
723 if [[ "$optionLabel" = "true" ]]; then
724 label="_""$argLabel";
725 vbm "STATUS:Set label:$label";
726 fi;
727 vbm "STATUS:Finished magicParseLabel() function.";
728 } # Set label used in output file name
729 magicParseProcessStrings() {
730 # Desc: Processes user-supplied process strings into process commands for appendFileTar().
731 # Usage: magicParseProcessStrings
732 # In : vars: optionProcString optionNoStoreRaw optionStoreRaw argRawFileExt
733 # arry: argProcStrings, argProcFileExts
734 # Out: arry: procStrings, procFileExts
735 # Depends Bash 5.0.3, yell(), vbm()
736 local rawFileExt
737
738 vbm "STATUS:Starting magicParseProcessStrings() function.";
739 # Validate input
740 ## Validate argRawFileExt
741 if [[ "$argRawFileExt" =~ ^[.][[:alnum:]]*$ ]]; then
742 rawFileExt="$argRawFileExt";
743 fi;
744
745 # Add default stdin output file entries for procStrings, procFileExts
746 ## Check if user specified that no raw stdin be saved.
747 if [[ ! "$optionNoStoreRaw" = "true" ]]; then
748 ### T: --no-store-raw not set. Store raw. Append procStrings with cat.
749 #### Append procStrings array
750 procStrings+=("cat ");
751 #### Check if --store-raw set.
752 if [[ "$optionStoreRaw" = "true" ]]; then
753 ##### T: --store-raw set. Append procFileExts with user-specified file ext
754 procFileExts+=("$rawFileExt");
755 else
756 ##### F: --store-raw not set. Append procFileExts with default ".stdin" file ext
757 ###### Append procFileExts array
758 procFileExts+=(".stdin");
759 fi;
760 else
761 ### F: --no-store-raw set. Do not store raw.
762 #### Do not append procStrings or procFileExts arrays.
763 :
764 fi;
765
766 # Do nothing more if optionProcString not set to true.
767 if [[ ! "$optionProcString" = "true" ]]; then
768 vbm "STATUS:optionProcString not set to 'true'. Returning early.";
769 return; fi;
770 # Validate input array indices
771 ## Make sure that argProcStrings and argProcFileExts have same index counts
772 if ! [[ "${#argProcStrings[@]}" -eq "${#argProcFileExts[@]}" ]]; then
773 yell "ERROR:Mismatch in number of elements in arrays argProcStrings and argProcFileExts:${#argProcStrings[@]} DNE ${#argProcFileExts[@]}";
774 yell "argProcStrings:${argProcStrings[*]}"; yell "argProcFileExts:${argProcFileExts[*]}"; exit 1; fi;
775 ## Make sure that no array elements are blank
776 for element in "${argProcStrings[@]}"; do
777 if [[ -z "$element" ]]; then yell "ERROR:Empty process string specified. Exiting."; exit 1; fi; done
778 for element in "${argProcFileExts[@]}"; do
779 if [[ -z "$element" ]]; then yell "ERROR:Empty output file extension specified. Exiting."; exit 1; fi; done
780 ## Make sure that no process string starts with '-' (ex: if only one arg supplied after '-p' option)
781 for element in "${argProcStrings[@]}"; do
782 if [[ ! "$element" =~ ^[-][[:print:]]*$ ]] && [[ "$element" =~ ^[[:print:]]*$ ]]; then
783 yell "ERROR:Illegal character '-' at start of process string element. Option syntax error?";
784 exit 1; fi; done;
785 vbm "STATUS:Quick check shows argProcStrings and argProcFileExts appear to have valid contents.";
786 procStrings=("${argProcStrings[@]}"); # Export process command strings
787 procFileExts=("${argProcFileExts[@]}"); # Export process command strings
788 vbm "STATUS:Finished magicParseProcessStrings() function.";
789 } # Validate and save process strings and file extensions to arrays procStrings, procFileExts
790 magicParseRecipientArgs() {
791 # Desc: Parses recipient arguments specified by '-r' option
792 # Input: vars: optionEncrypt, optionRecipients
793 # arry: argRecPubKeys from processArguments()
794 # Output: vars: cmd_encrypt, cmd_encrypt_suffix
795 # arry: recPubKeysValid, recPubKeysValidStatic
796 # Depends: processArguments(), yell(), vbm(), checkapp(), checkAgePubkey(), validateInput()
797 local recipients
798
799 # Check if encryption option active.
800 if [[ "$optionEncrypt" = "true" ]] && [[ "$optionRecipients" = "true" ]]; then
801 if checkapp age; then # Check that age is available.
802 for pubkey in "${argRecPubKeys[@]}"; do # Validate recipient pubkey strings by forming test message
803 vbm "DEBUG:Testing pubkey string:$pubkey";
804 if checkAgePubkey "$pubkey" && \
805 ( validateInput "$pubkey" "ssh_pubkey" || validateInput "$pubkey" "age_pubkey"); then
806 #### Form age recipient string
807 recipients="$recipients""-r '$pubkey' ";
808 vbm "STATUS:Added pubkey for forming age recipient string:""$pubkey";
809 vbm "DEBUG:recipients:""$recipients";
810 #### Add validated pubkey to recPubKeysValid array
811 recPubKeysValid+=("$pubkey") && vbm "DEBUG:recPubkeysValid:pubkey added:$pubkey";
812 else
813 yell "ERROR:Exit code ""$?"". Invalid recipient pubkey string. Exiting."; exit 1;
814 fi;
815 done
816 vbm "DEBUG:Finished processing argRecPubKeys array";
817 vbm "STATUS:Array of validated pubkeys:${recPubKeysValid[*]}";
818 recPubKeysValidStatic=("${recPubKeysValid[@]}"); # Save static image of pubkeys validated by this function
819
820 ## Form age command string
821 cmd_encrypt="age ""$recipients " && vbm "cmd_encrypt:$cmd_encrypt";
822 cmd_encrypt_suffix=".age" && vbm "cmd_encrypt_suffix:$cmd_encrypt_suffix";
823 else
824 yell "ERROR:Encryption enabled but \"age\" not found. Exiting."; exit 1;
825 fi;
826 else
827 cmd_encrypt="tee /dev/null " && vbm "cmd_encrypt:$cmd_encrypt";
828 cmd_encrypt_suffix="" && vbm "cmd_encrypt_suffix:$cmd_encrypt_suffix";
829 vbm "DEBUG:Encryption not enabled."
830 fi;
831 # Catch case if '-e' is set but '-r' or '-R' is not
832 if [[ "$optionEncrypt" = "true" ]] && [[ ! "$optionRecipients" = "true" ]]; then
833 yell "ERROR:\\'-e\\' set but no \\'-r\\' or \\'-R\\' set."; exit 1; fi;
834 # Catch case if '-r' or '-R' set but '-e' is not
835 if [[ ! "$optionEncrypt" = "true" ]] && [[ "$optionRecipients" = "true" ]]; then
836 yell "ERROR:\\'-r\\' or \\'-R\\' set but \\'-e\\' is not set."; exit 1; fi;
837 } # Populate recPubKeysValid with argRecPubKeys; form encryption cmd string and filename suffix
838 magicParseRecipientDir() {
839 # Desc: Updates recPubKeysValid with pubkeys in dir specified by '-R' option ("recipient directory")
840 # Inputs: vars: optionEncrypt, optionRecDir, argRecDir,
841 # arry: recPubKeysValid
842 # Outputs: arry: recPubKeysValid
843 # Depends: processArguments(), yell(), vbm(), validateInput(), checkAgePubkey()
844 local recipientDir recFileLine updateRecipients
845 declare -a candRecPubKeysValid
846
847 # Check that '-e' and '-R' set
848 if [[ "$optionEncrypt" = "true" ]] && [[ "$optionRecDir" = "true" ]]; then
849 ### Check that argRecDir is a directory.
850 if [[ -d "$argRecDir" ]]; then
851 recipientDir="$argRecDir" && vbm "STATUS:Recipient watch directory detected:\"$recipientDir\"";
852 #### Initialize variable indicating outcome of pubkey review
853 unset updateRecipients
854 #### Add existing recipients
855 candRecPubKeysValid=("${recPubKeysValidStatic[@]}");
856 #### Parse files in recipientDir
857 for file in "$recipientDir"/*; do
858 ##### Read first line of each file
859 recFileLine="$(head -n1 "$file")" && vbm "STATUS:Checking if pubkey:\"$recFileLine\"";
860 ##### check if first line is a valid pubkey
861 if checkAgePubkey "$recFileLine" && \
862 ( validateInput "$recFileLine" "ssh_pubkey" || validateInput "$recFileLine" "age_pubkey"); then
863 ###### T: add candidate pubkey to candRecPubKeysValid
864 candRecPubKeysValid+=("$recFileLine") && vbm "STATUS:RecDir pubkey is valid pubkey:\"$recFileLine\"";
865 else
866 ###### F: throw warning;
867 yell "ERROR:Invalid recipient file detected. Not modifying recipient list."
868 updateRecipients="false";
869 fi;
870 done
871 #### Write updated recPubKeysValid array to recPubKeysValid if no failure detected
872 if ! [[ "$updateRecipients" = "false" ]]; then
873 recPubKeysValid=("${candRecPubKeysValid[@]}") && vbm "STATUS:Wrote candRecPubkeysValid to recPubKeysValid:\"${recPubKeysValid[*]}\"";
874 fi;
875 else
876 yell "ERROR:$0:Recipient directory $argRecDir does not exist. Exiting."; exit 1;
877 fi;
878 fi;
879 # Handle case if '-R' set but '-e' not set
880 if [[ ! "$optionEncrypt" = "true" ]] && [[ "$optionRecDir" = "true" ]]; then
881 yell "ERROR: \\'-R\\' is set but \\'-e\\' is not set."; fi;
882 } # Update recPubKeysValid with argRecDir
883 magicSetScriptTTL() {
884 #Desc: Sets script_TTL seconds from provided time_element string argument
885 #Usage: magicSetScriptTTL [str time_element]
886 #Input: arg1: string (Ex: scriptTTL_TE; "day" or "hour")
887 #Output: var: scriptTTL (integer seconds)
888 #Depends: timeUntilNextHour, timeUntilNextDay
889 local argTimeElement
890
891 argTimeElement="$1";
892 if [[ "$argTimeElement" = "day" ]]; then
893 # Set script lifespan to end at start of next day
894 if ! scriptTTL="$(timeUntilNextDay)"; then # sets scriptTTL, then checks exit code
895 if [[ "$scriptTTL" -eq 0 ]]; then
896 ((scriptTTL++)); # Add 1 because 0 would cause 'timeout' to never timeout.
897 else
898 yell "ERROR: timeUntilNextDay exit code $?"; exit 1;
899 fi;
900 fi;
901 elif [[ "$argTimeElement" = "hour" ]]; then
902 # Set script lifespan to end at start of next hour
903 if ! scriptTTL="$(timeUntilNextHour)"; then # sets scriptTTL, then checks exit code
904 if [[ "$scriptTTL" -eq 0 ]]; then
905 ((scriptTTL++)); # Add 1 because 0 would cause 'timeout' to never timeout.
906 else
907 yell "ERROR: timeUntilNextHour exit code $?"; exit 1;
908 fi;
909 fi;
910 else
911 yell "ERROR:Invalid argument for setScriptTTL function:$argTimeElement"; exit 1;
912 fi;
913 } # Set scriptTTL in seconds until next (day|hour).
914 magicWriteVersion() {
915 # Desc: Appends time-stamped VERSION to pathout_tar
916 # Usage: magicWriteVersion
917 # Input: vars: pathout_tar, dir_tmp
918 # Input: vars: scriptVersion, scriptURL, ageVersion, ageURL, scriptHostname
919 # Input: array: recPubKeysValid
920 # Output: appends tar (pathout_tar)
921 # Depends: bash 5.0.3, dateTimeShort(), appendArgTar()
922 local fileoutVersion contentVersion pubKeyIndex pubKeyIndex
923
924 # Set VERSION file name
925 fileoutVersion="$(dateTimeShort)..VERSION";
926
927 # Gather VERSION data in contentVersion
928 contentVersion="scriptVersion=$scriptVersion";
929 #contentVersion="$contentVersion""\\n";
930 contentVersion="$contentVersion""\\n""scriptName=$scriptName";
931 contentVersion="$contentVersion""\\n""scriptURL=$scriptURL";
932 contentVersion="$contentVersion""\\n""ageVersion=$ageVersion";
933 contentVersion="$contentVersion""\\n""ageURL=$ageURL";
934 contentVersion="$contentVersion""\\n""date=$(date --iso-8601=seconds)";
935 contentVersion="$contentVersion""\\n""hostname=$scriptHostname";
936 ## Add list of recipient pubkeys
937 for pubkey in "${recPubKeysValid[@]}"; do
938 ((pubKeyIndex++))
939 contentVersion="$contentVersion""\\n""PUBKEY_$pubKeyIndex=$pubkey";
940 done
941 ## Process newline escapes
942 contentVersion="$(echo -e "$contentVersion")"
943
944 # Write contentVersion as file fileoutVersion and write-append to pathout_tar
945 appendArgTar "$contentVersion" "$fileoutVersion" "$pathout_tar" "$dir_tmp";
946 } # write version data to pathout_tar via appendArgTar()
947 magicProcessWriteBuffer() {
948 # Desc: process and write buffer
949 # In : vars: bufferTTL bufferTTL_STR scriptHostname label dir_tmp SECONDS
950 # : arry: buffer
951 # Out: file:(pathout_tar)
952 # Depends: Bash 5.0.3, date 8.30, yell(), vbm(), dateTimeShort(),
953 ### Note: These arrays should all have the same number of elements:
954 ### pathouts, fileouts, procFileExts, procStrings
955
956 local fn timeBufferStartLong timeBufferStart fileoutBasename
957 local -a fileouts pathouts
958 local writeCmd1 writeCmd2 writeCmd3 writeCmd4
959
960 vbm "DEBUG:STATUS:$fn:Started magicProcessWriteBuffer().";
961 # Debug:Get function name
962 fn="${FUNCNAME[0]}";
963
964 # Determine file paths (time is start of buffer period)
965 ## Calculate start time
966 timeBufferStartLong="$(date --date="$bufferTTL seconds ago" --iso-8601=seconds)" && \
967 vbm "timeBufferStartLong:$timeBufferStartLong";
968 timeBufferStart="$(dateTimeShort "$timeBufferStartLong" )" && \
969 vbm "timeBufferStart:$timeBufferStart"; # Note start time YYYYmmddTHHMMSS+zzzz (no separators)
970 ## Set common basename
971 fileoutBasename="$timeBufferStart""--""$bufferTTL_STR""..""$scriptHostname""$label" && \
972 vbm "STATUS:Set fileoutBasename to:$fileoutBasename";
973 ## Determine output file name array
974 ### in: fileOutBasename cmd_compress_suffix cmd_encrypt_suffix procFileExts
975 for fileExt in "${procFileExts[@]}"; do
976 fileouts+=("$fileoutBasename""$fileExt""$cmd_compress_suffix""$cmd_encrypt_suffix") && \
977 vbm "STATUS:Added $fileExt to fileouts:${fileouts[*]}";
978 done;
979 for fileName in "${fileouts[@]}"; do
980 pathouts+=("$dir_tmp"/"$fileName") && \
981 vbm "STATUS:Added $fileName to pathouts:${pathouts[*]}";
982 done;
983 ## Update pathout_tar
984 magicInitCheckTar;
985
986 # Process and write buffers to dir_tmp
987 ## Prepare command strings
988 writeCmd1="printf \"%s\\\\n\" \"\${buffer[@]}\""; # printf "%s\\n" "${buffer[@]}"
989 #writeCmd2="" # NOTE: Specified by parsing array procStrings
990 writeCmd3="$cmd_compress";
991 writeCmd4="$cmd_encrypt";
992
993 ## Process buffer and write to dir_tmp
994 for index in "${!pathouts[@]}"; do
995 writeCmd2="${procStrings[$index]}"
996 eval "$writeCmd1 | $writeCmd2 | $writeCmd3 | $writeCmd4" >> "${pathouts[$index]}";
997 done;
998
999 # Append dir_tmp files to pathout_tar
1000 wait; # Wait to avoid collision with older magicProcessWriteBuffer() instances (see https://www.tldp.org/LDP/abs/html/x9644.html )
1001 for index in "${!pathouts[@]}"; do
1002 appendFileTar "${pathouts[$index]}" "${fileouts[$index]}" "$pathout_tar" "$dir_tmp";
1003 done;
1004
1005 # Remove secured chunks from dir_tmp
1006 for path in "${pathouts[@]}"; do
1007 rm "$path";
1008 done;
1009
1010 vbm "DEBUG:STATUS:$fn:Finished magicProcessWriteBuffer().";
1011 } # Process and Write buffer
1012
1013 main() {
1014 # Process arguments
1015 processArguments "$@";
1016 ## Determine working directory
1017 magicInitWorkingDir; # Sets dir_tmp from argTempDirPriority
1018 ## Set output encryption and compression option strings
1019 ### React to "-e" and "-r" ("encryption recipients") options
1020 magicParseRecipientArgs; # Updates recPubKeysValid, cmd_encrypt[_suffix] from argRecPubKeys
1021 ### React to "-R" ("recipient directory") option
1022 magicParseRecipientDir; # Updates recPubKeysValid
1023 ### React to "-c" ("compression") option
1024 magicParseCompressionArg; # Updates cmd_compress[_suffix]
1025 ## React to "-b" and "-B" (custom buffer and script TTL) options
1026 magicParseCustomTTL; # Sets custom scriptTTL_TE and/or bufferTTL if specified
1027 ## React to "-p" (user-supplied process command and file extension strings) options
1028 magicParseProcessStrings; # Sets arrays: procStrings, procFileExts
1029 ## React to "-l" (output file label) option
1030 magicParseLabel; # sets label (ex: "_location")
1031 ## React to "-w" (how to name raw stdin file) option
1032 magicParseStoreRaw; # sets raw_suffix
1033
1034 # Perform secondary setup operations
1035 ## Set script lifespan (scriptTTL from scriptTTL_TE)
1036 magicSetScriptTTL "$scriptTTL_TE";
1037 ## File name substring (ISO-8601 duration from bufferTTL)
1038 bufferTTL_STR="$(timeDuration "$bufferTTL")" && vbm "DEBUG:bufferTTL_STR:$bufferTTL_STR";
1039 ## Init temp working dir
1040 try mkdir "$dir_tmp" && vbm "DEBUG:Working dir created at dir_tmp:$dir_tmp";
1041 ## Initialize output tar (set pathout_tar)
1042 magicInitCheckTar;
1043
1044 # Check vital apps, files, dirs
1045 if ! checkapp tar && ! checkdir "$dirOut" "dir_tmp"; then
1046 yell "ERROR:Critical components missing.";
1047 displayMissing; yell "Exiting."; exit 1; fi
1048
1049 # MAIN LOOP: Run until script TTL seconds pass
1050 bufferRound=0;
1051 while [[ $SECONDS -lt "scriptTTL" ]]; do
1052 bufferTOD="$((SECONDS + bufferTTL))"; # Set buffer round time-of-death
1053 lineCount=0; # Debug counter
1054 # Consume stdin to fill buffer until buffer time-of-death (TOD) arrives
1055 while read -r -t "$bufferTTL" line && [[ $SECONDS -lt "$bufferTOD" ]]; do
1056 # Append line to buffer array
1057 buffer+=("$line");
1058 echo "DEBUG:Processing line:$lineCount";
1059 echo "DEBUG:Current line :$line";
1060 echo "DEBUG:buf elem count :${#buffer[@]}";
1061 ((lineCount++));
1062 done;
1063 # Create dir_tmp if missing
1064 if ! [[ -d "$dir_tmp" ]]; then yell "ERROR:dir_tmp existence failure:$dir_tmp"; try mkdir "$dir_tmp" && vbm "DEBUG:Working dir recreated dir_tmp:$dir_tmp"; fi
1065 # Update encryption recipient array
1066 magicParseRecipientDir; # Update recPubKeysValid with argRecDir
1067 # Export buffer to asynchronous processing.
1068 magicProcessWriteBuffer &
1069 unset buffer; # Clear buffer array for next bufferRound
1070 # Increment buffer round
1071 ((bufferRound++));
1072 done;
1073
1074 # Cleanup
1075 ## Remove dir_tmp
1076 try rm -r "$dir_tmp" && vbm "Removed dir_tmp:$dir_tmp";
1077
1078 vbm "STATUS:Main function finished.";
1079 } # Main function
1080
1081 #===END Declare local script functions===
1082 #==END Define script parameters==
1083
1084 #==BEGIN Perform work and exit==
1085 main "$@" # Run main function.
1086 exit 0;
1087 #==END Perform work and exit==
1088
1089 # Author: Steven Baltakatei Sandoval;
1090 # License: GPLv3+