2 # Desc: Compresses, encrypts, and writes stdin every 5 seconds
4 #==BEGIN Define script parameters==
5 #===BEGIN Initialize variables===
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
13 scriptName
="bklog"; # Define basename of script file.
14 scriptVersion
="0.1.7"; # 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.
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
34 optionVerbose
=""; optionEncrypt
=""; dirOut
=""; optionEncrypt
=""; dir_tmp
="";
35 cmd_compress
="";cmd_compress_suffix
=""; cmd_encrypt
=""; cmd_encrypt_suffix
="";
37 #===END Initialize variables===
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
44 while [ ! $# -eq 0 ]; do # While number of arguments ($#) is not (!) equal to (-eq) zero (0).
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.
66 } # Argument Processing
68 # Description: Prints verbose message ("vbm") to stderr if optionVerbose is set to "true".
69 # Usage: vbm "DEBUG:verbose message here"
74 # Depends: bash 5.0.3, echo 8.30, date 8.30
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.
82 return 0; # Function finished.
83 } # Displays message if optionVerbose true
85 # Desc: If arg is a command, save result in assoc array 'appRollCall'
86 # Usage: checkapp arg1 arg2 arg3 ...
88 # Input: global assoc. array 'appRollCall'
89 # Output: adds/updates key(value) to global assoc array 'appRollCall'
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;
99 appRollCall
[$arg]="false"; returnState
="false";
103 #===Determine function return code===
104 if [ "$returnState" = "true" ]; then
109 } # Check that app exists
111 # Desc: If arg is a file path, save result in assoc array 'fileRollCall'
112 # Usage: checkfile arg1 arg2 arg3 ...
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
122 if [ -f "$arg" ]; then
123 fileRollCall
["$arg"]="true";
124 if ! [ "$returnState" = "false" ]; then returnState
="true"; fi;
126 fileRollCall
["$arg"]="false"; returnState
="false";
130 #===Determine function return code===
131 if [ "$returnState" = "true" ]; then
136 } # Check that file exists
138 # Desc: If arg is a dir path, save result in assoc array 'dirRollCall'
139 # Usage: checkdir arg1 arg2 arg3 ...
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
149 if [ -d "$arg" ]; then
150 dirRollCall
["$arg"]="true";
151 if ! [ "$returnState" = "false" ]; then returnState
="true"; fi
153 dirRollCall
["$arg"]="false"; returnState
="false";
157 #===Determine function return code===
158 if [ "$returnState" = "true" ]; then
163 } # Check that dir exists
165 # Desc: Displays missing apps, files, and dirs
166 # Usage: displayMissing
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
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 ";
186 if [ "$appMissing" = "true" ]; then # Only indicate if an app is missing.
187 echo "$missingApps" 1>&2;
190 #===END Display Missing Apps===
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 ";
203 if [ "$fileMissing" = "true" ]; then # Only indicate if an app is missing.
204 echo "$missingFiles" 1>&2;
207 #===END Display Missing Files===
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 ";
220 if [ "$dirMissing" = "true" ]; then # Only indicate if an dir is missing.
221 echo "$missingDirs" 1>&2;
224 #===END Display Missing Directories===
226 #==END Display errors==
227 } # Display missing apps, files, dirs
230 # Desc: Appends [processed] file to tar
231 # Usage: appendFileTar [file path] [name of file to be inserted] [tar path] [temp dir] ([process cmd])
233 # Input: arg1: path of file to be (processed and) written
234 # arg2: name to use for file inserted into tar
235 # arg3: tar archive path (must exist first)
236 # arg4: temporary working dir
237 # arg5: (optional) command string to process file (ex: "gpsbabel -i nmea -f - -o kml -F - ")
238 # Output: file written to disk
239 # Example: decrypt multiple large files in parallel
240 # appendFileTar /tmp/largefile1.gpg "largefile1" $HOME/archive.tar /tmp "gpg --decrypt" &
241 # appendFileTar /tmp/largefile2.gpg "largefile2" $HOME/archive.tar /tmp "gpg --decrypt" &
242 # appendFileTar /tmp/largefile3.gpg "largefile3" $HOME/archive.tar /tmp "gpg --decrypt" &
243 # Depends: bash 5.0.3, tar 1.30, cat 8.30, yell()
244 local fn fileName tarPath tmpDir
248 #yell "DEBUG:STATUS:$fn:Started appendFileTar()."
251 if ! [ -z "$2" ]; then fileName
="$2"; else yell
"ERROR:$fn:Not enough arguments."; exit 1; fi
252 # Check tar path is a file
253 if [ -f "$3" ]; then tarPath
="$3"; else yell
"ERROR:$fn:Tar archive arg not a file:$3"; exit 1; fi
255 if ! [ -z "$4" ]; then tmpDir
="$4"; else yell
"ERROR:$fn:No temporary working dir set."; exit 1; fi
256 # Set command strings
257 if ! [ -z "$5" ]; then cmd1
="$5"; else cmd1
="cat "; fi # command string
259 # Input command string
262 # Write to temporary working dir
263 eval "$cmd0 | $cmd1" > "$tmpDir"/"$fileName";
266 try
tar --append --directory="$tmpDir" --file="$tarPath" "$fileName";
267 #yell "DEBUG:STATUS:$fn:Finished appendFileTar()."
268 } # Append [processed] file to Tar archive
270 # Desc: Checks if string is an age-compatible pubkey
271 # Usage: checkAgePubkey [str pubkey]
273 # Input: arg1: string
274 # Output: return code 0: string is age-compatible pubkey
275 # return code 1: string is NOT an age-compatible pubkey
276 # age stderr (ex: there is stderr if invalid string provided)
277 # Depends: age (v0.1.0-beta2; https://github.com/FiloSottile/age/releases/tag/v1.0.0-beta2 )
281 if echo "test" | age
-a -r "$argPubkey" 1>/dev
/null
; then
288 # Desc: Date without separators (YYYYmmdd)
289 # Usage: dateShort ([str date])
291 # Input: arg1: 'date'-parsable timestamp string (optional)
292 # Output: stdout: date (ISO-8601, no separators)
293 # Depends: bash 5.0.3, date 8.30, yell()
294 local argTime timeCurrent timeInput dateCurrentShort
298 timeCurrent
="$(date --iso-8601=seconds)" ; # Produce `date`-parsable current timestamp with resolution of 1 second.
299 # Decide to parse current or supplied date
300 ## Check if time argument empty
301 if [[ -z "$argTime" ]]; then
302 ## T: Time argument empty, use current time
303 timeInput
="$timeCurrent";
305 ## F: Time argument exists, validate time
306 if date --date="$argTime" 1>/dev
/null
2>&1; then
307 ### T: Time argument is valid; use it
308 timeInput
="$argTime";
310 ### F: Time argument not valid; exit
311 yell
"ERROR:Invalid time argument supplied. Exiting."; exit 1;
314 # Construct and deliver separator-les date string
315 dateCurrentShort
="$(date -d "$timeInput" +%Y%m%d)"; # Produce separator-less current date with resolution 1 day.
316 echo "$dateCurrentShort";
319 # Desc: Set time zone environment variable TZ
320 # Usage: setTimeZoneEV arg1
322 # Input: arg1: 'date'-compatible timezone string (ex: "America/New_York")
323 # TZDIR env var (optional; default: "/usr/share/zoneinfo")
325 # exit code 0 on success
326 # exit code 1 on incorrect number of arguments
327 # exit code 2 if unable to validate arg1
328 # Depends: yell(), printenv 8.30, bash 5.0.3
329 # Tested on: Debian 10
330 local tzDir returnState argTimeZone
333 if ! [[ $# -eq 1 ]]; then
334 yell
"ERROR:Invalid argument count.";
338 # Read TZDIR env var if available
339 if printenv TZDIR
1>/dev
/null
2>&1; then
340 tzDir
="$(printenv TZDIR)";
342 tzDir
="/usr/share/zoneinfo";
346 if ! [[ -f "$tzDir"/"$argTimeZone" ]]; then
347 yell
"ERROR:Invalid time zone argument.";
350 # Export ARG1 as TZ environment variable
351 TZ
="$argTimeZone" && export TZ
&& returnState
="true";
354 # Determine function return code
355 if [ "$returnState" = "true" ]; then
358 } # Exports TZ environment variable
362 cmd | bklog [ options ]
366 Display help information.
368 Display script version.
370 Display debugging info.
373 -r, --recipient [ string pubkey ]
374 Specify recipient. May be age or ssh pubkey.
375 May be specified multiple times for multiple pubkeys.
376 See https://github.com/FiloSottile/age
377 -o, --output [ path dir ]
378 Specify output directory to save logs. This option is required
380 -p, --process-string [ filter command ] [ output file extension]
381 Specify how to create and name a processed version of the stdin.
382 For example, if stdin is 'nmea' location data:
384 -p "gpsbabel -i nmea -f - -o gpx -F - " ".gpx"
386 This option would cause the stdin to 'bklog' to be piped into
387 the 'gpsbabel' command, interpreted as 'nmea' data, converted
388 into 'gpx' format, and then appended to the output tar file
389 as a file with a '.gpx' extension.
390 This option may be specified multiple times in order to output
391 results of multiple different processing methods.
392 -l, --label [ string ]
393 Specify a label to be included in all output file names.
394 Ex: 'location' if stdin is location data.
395 -w, --store-raw [ file extension ]
396 Specify file extension of file within output tar that contains
397 raw stdin data. The default behavior is to always save raw stdin
398 data in a '.stdin' file. Example usage when 'bklog' receives
399 'nmea' data from 'gpspipe -r':
403 Stdin data is saved in a '.nmea' file within the output tar.
405 Do not store raw stdin in output tar.
407 Compress output with gzip (before encryption if enabled).
409 Specify time zone. (ex: "America/New_York")
410 -t, --temp-dir [path dir]
411 Specify parent directory for temporary working directory.
413 -R, --recipient-dir [path dir]
414 Specify directory containing files whose first lines are
415 to be interpreted as pubkey strings (see '-r' option).
416 -b, --buffer-ttl [integer]
417 Specify custom buffer period in seconds (default: 300 seconds)
418 -B, --script-ttl [time element string]
419 Specify custom script time-to-live in seconds (default: "day")
420 Valid values: "day", "hour"
422 EXAMPLE: (bash script lines)
423 $ gpspipe -r | /bin/bash bklog -v -e -c -z "UTC" -t "/dev/shm" \
424 -r age1mrmfnwhtlprn4jquex0ukmwcm7y2nxlphuzgsgv8ew2k9mewy3rs8u7su5 \
425 -r age1ala848kqrvxc88rzaauc6vc5v0fqrvef9dxyk79m0vjea3hagclswu0lgq \
426 -R ~/.config/bklog/recipients -w ".nmea" -b 300 -B "day" \
427 -o ~/Sync/Logs -l "location" \
428 -p "gpsbabel -i nmea -f - -o gpx -F - " ".gpx" \
429 -p "gpsbabel -i nmea -f - -o kml -F - " ".kml"
431 } # Display information on how to use this script.
433 yell
"$scriptVersion"
434 } # Display script version.
436 # Desc: Given seconds, output ISO-8601 duration string
437 # Ref/Attrib: ISO-8601:2004(E), §4.4.4.2 Representations of time intervals by duration and context information
438 # Note: "1 month" ("P1M") is assumed to be "30 days" (see ISO-8601:2004(E), §2.2.1.2)
439 # Usage: timeDuration [1:seconds] ([2:precision])
441 # Input: arg1: seconds as base 10 integer >= 0 (ex: 3601)
442 # arg2: precision level (optional; default=2)
443 # Output: stdout: ISO-8601 duration string (ex: "P1H1S", "P2Y10M15DT10H30M20S")
444 # exit code 0: success
445 # exit code 1: error_input
446 # exit code 2: error_unknown
447 # Example: 'timeDuration 111111 3' yields 'P1DT6H51M'
448 # Depends: date 8, bash 5, yell,
449 local argSeconds argPrecision precision returnState remainder
450 local fullYears fullMonths fullDays fullHours fullMinutes fullSeconds
451 local hasYears hasMonths hasDays hasHours hasMinutes hasSeconds
452 local witherPrecision output
453 local displayYears displayMonths displayDays displayHours displayMinutes displaySeconds
455 argSeconds
="$1"; # read arg1 (seconds)
456 argPrecision
="$2"; # read arg2 (precision)
457 precision
=2; # set default precision
459 # Check that between one and two arguments is supplied
460 if ! { [[ $# -ge 1 ]] && [[ $# -le 2 ]]; }; then
461 yell
"ERROR:Invalid number of arguments:$# . Exiting.";
462 returnState
="error_input"; fi
464 # Check that argSeconds provided
465 if [[ $# -ge 1 ]]; then
466 ## Check that argSeconds is a positive integer
467 if [[ "$argSeconds" =~ ^
[[:digit
:]]+$
]]; then
470 yell
"ERROR:argSeconds not a digit.";
471 returnState
="error_input";
474 yell
"ERROR:No argument provided. Exiting.";
478 # Consider whether argPrecision was provided
479 if [[ $# -eq 2 ]]; then
480 # Check that argPrecision is a positive integer
481 if [[ "$argPrecision" =~ ^
[[:digit
:]]+$
]] && [[ "$argPrecision" -gt 0 ]]; then
482 precision
="$argPrecision";
484 yell
"ERROR:argPrecision not a positive integer. (is $argPrecision ). Leaving early.";
485 returnState
="error_input";
491 remainder
="$argSeconds" ; # seconds
492 ## Calculate full years Y, update remainder
493 fullYears
=$
(( remainder
/ (365*24*60*60) ));
494 remainder
=$
(( remainder
- (fullYears
*365*24*60*60) ));
495 ## Calculate full months M, update remainder
496 fullMonths
=$
(( remainder
/ (30*24*60*60) ));
497 remainder
=$
(( remainder
- (fullMonths
*30*24*60*60) ));
498 ## Calculate full days D, update remainder
499 fullDays
=$
(( remainder
/ (24*60*60) ));
500 remainder
=$
(( remainder
- (fullDays
*24*60*60) ));
501 ## Calculate full hours H, update remainder
502 fullHours
=$
(( remainder
/ (60*60) ));
503 remainder
=$
(( remainder
- (fullHours
*60*60) ));
504 ## Calculate full minutes M, update remainder
505 fullMinutes
=$
(( remainder
/ (60) ));
506 remainder
=$
(( remainder
- (fullMinutes
*60) ));
507 ## Calculate full seconds S, update remainder
508 fullSeconds
=$
(( remainder
/ (1) ));
509 remainder
=$
(( remainder
- (remainder
*1) ));
510 ## Check which fields filled
511 if [[ $fullYears -gt 0 ]]; then hasYears
="true"; else hasYears
="false"; fi
512 if [[ $fullMonths -gt 0 ]]; then hasMonths
="true"; else hasMonths
="false"; fi
513 if [[ $fullDays -gt 0 ]]; then hasDays
="true"; else hasDays
="false"; fi
514 if [[ $fullHours -gt 0 ]]; then hasHours
="true"; else hasHours
="false"; fi
515 if [[ $fullMinutes -gt 0 ]]; then hasMinutes
="true"; else hasMinutes
="false"; fi
516 if [[ $fullSeconds -gt 0 ]]; then hasSeconds
="true"; else hasSeconds
="false"; fi
518 ## Determine which fields to display (see ISO-8601:2004 §4.4.3.2)
519 witherPrecision
="false"
522 if $hasYears && [[ $precision -gt 0 ]]; then
524 witherPrecision
="true";
526 displayYears
="false";
528 if $witherPrecision; then ((precision--
)); fi;
531 if $hasMonths && [[ $precision -gt 0 ]]; then
532 displayMonths
="true";
533 witherPrecision
="true";
535 displayMonths
="false";
537 if $witherPrecision && [[ $precision -gt 0 ]]; then
538 displayMonths
="true";
540 if $witherPrecision; then ((precision--
)); fi;
543 if $hasDays && [[ $precision -gt 0 ]]; then
545 witherPrecision
="true";
549 if $witherPrecision && [[ $precision -gt 0 ]]; then
552 if $witherPrecision; then ((precision--
)); fi;
555 if $hasHours && [[ $precision -gt 0 ]]; then
557 witherPrecision
="true";
559 displayHours
="false";
561 if $witherPrecision && [[ $precision -gt 0 ]]; then
564 if $witherPrecision; then ((precision--
)); fi;
567 if $hasMinutes && [[ $precision -gt 0 ]]; then
568 displayMinutes
="true";
569 witherPrecision
="true";
571 displayMinutes
="false";
573 if $witherPrecision && [[ $precision -gt 0 ]]; then
574 displayMinutes
="true";
576 if $witherPrecision; then ((precision--
)); fi;
580 if $hasSeconds && [[ $precision -gt 0 ]]; then
581 displaySeconds
="true";
582 witherPrecision
="true";
584 displaySeconds
="false";
586 if $witherPrecision && [[ $precision -gt 0 ]]; then
587 displaySeconds
="true";
589 if $witherPrecision; then ((precision--
)); fi;
591 ## Determine whether or not the "T" separator is needed to separate date and time elements
592 if ( $displayHours ||
$displayMinutes ||
$displaySeconds); then
593 displayDateTime
="true"; else displayDateTime
="false"; fi
595 ## Construct duration output string
597 if $displayYears; then
598 output
=$output$fullYears"Y"; fi
599 if $displayMonths; then
600 output
=$output$fullMonths"M"; fi
601 if $displayDays; then
602 output
=$output$fullDays"D"; fi
603 if $displayDateTime; then
604 output
=$output"T"; fi
605 if $displayHours; then
606 output
=$output$fullHours"H"; fi
607 if $displayMinutes; then
608 output
=$output$fullMinutes"M"; fi
609 if $displaySeconds; then
610 output
=$output$fullSeconds"S"; fi
612 ## Output duration string to stdout
613 echo "$output" && returnState
="true";
615 #===Determine function return code===
616 if [ "$returnState" = "true" ]; then
618 elif [ "$returnState" = "error_input" ]; then
622 yell
"ERROR:Unknown";
626 } # Get duration (ex: PT10M4S )
628 # Desc: Validates Input
629 # Usage: validateInput [str input] [str input type]
631 # Input: arg1: string to validate
632 # arg2: string specifying input type (ex:"ssh_pubkey")
633 # Output: return code 0: if input string matched specified string type
634 # Depends: bash 5, yell()
636 local fn argInput argType
644 if [[ $# -gt 2 ]]; then yell
"ERROR:$0:$fn:Too many arguments."; exit 1; fi;
647 if [[ -z "$argInput" ]]; then return 1; fi
651 ### Check for alnum/dash base64 (ex: "ssh-rsa AAAAB3NzaC1yc2EAAA")
652 if [[ "$argType" = "ssh_pubkey" ]]; then
653 if [[ "$argInput" =~ ^
[[:alnum
:]-]*[\
]*[[:alnum
:]+/=]*$
]]; then
657 ### Check for age1[:bech32:]
658 if [[ "$argType" = "age_pubkey" ]]; then
659 if [[ "$argInput" =~ ^age1
[qpzry9x8gf2tvdw0s3jn54khce6mua7l
]*$
]]; then
663 if [[ "$argType" = "integer" ]]; then
664 if [[ "$argInput" =~ ^
[[:digit
:]]*$
]]; then
667 ## time element (year, month, week, day, hour, minute, second)
668 if [[ "$argType" = "time_element" ]]; then
669 if [[ "$argInput" = "year" ]] || \
670 [[ "$argInput" = "month" ]] || \
671 [[ "$argInput" = "week" ]] || \
672 [[ "$argInput" = "day" ]] || \
673 [[ "$argInput" = "hour" ]] || \
674 [[ "$argInput" = "minute" ]] || \
675 [[ "$argInput" = "second" ]]; then
678 # Return error if no condition matched.
680 } # Validates strings
682 magicInitWorkingDir
() {
683 # Desc: Determine temporary working directory from defaults or user input
684 # Usage: magicInitWorkingDir
685 # Input: vars: optionTmpDir, argTempDirPriority, dirTmpDefault
686 # Input: vars: scriptTimeStart
687 # Output: vars: dir_tmp
688 # Depends: bash 5.0.3, processArguments(), vbm(), yell()
689 # Parse '-t' option (user-specified temporary working dir)
690 ## Set dir_tmp_parent to user-specified value if specified
693 if [[ "$optionTmpDir" = "true" ]]; then
694 if [[ -d "$argTempDirPriority" ]]; then
695 dir_tmp_parent
="$argTempDirPriority";
697 yell
"WARNING:Specified temporary working directory not valid:$argTempDirPriority";
698 exit 1; # Exit since user requires a specific temp dir and it is not available.
701 ## Set dir_tmp_parent to default or fallback otherwise
702 if [[ -d "$dirTmpDefault" ]]; then
703 dir_tmp_parent
="$dirTmpDefault";
704 elif [[ -d /tmp
]]; then
705 yell
"WARNING:$dirTmpDefault not available. Falling back to /tmp .";
706 dir_tmp_parent
="/tmp";
708 yell
"ERROR:No valid working directory available. Exiting.";
712 ## Set dir_tmp using dir_tmp_parent and nonce (scriptTimeStart)
713 dir_tmp
="$dir_tmp_parent"/"$scriptTimeStart""..bkgpslog" && vbm
"DEBUG:Set dir_tmp to:$dir_tmp"; # Note: removed at end of main().
715 magicInitCheckTar
() {
716 # Desc: Initializes or checks output tar
717 # input: vars: dirOut, bufferTTL, cmd_encrypt_suffix, cmd_compress_suffix
718 # input: vars: scriptHostname
719 # output: vars: pathout_tar
720 # depends: Bash 5.0.3, vbm(), dateShort(), checkMakeTar(), magicWriteVersion()
723 pathout_tar
="$dirOut"/"$(dateShort "$
(date --date="$bufferTTL seconds ago" --iso-8601=seconds
)")"..
"$scriptHostname""$label""$cmd_compress_suffix""$cmd_encrypt_suffix".
tar && \
724 vbm
"STATUS:Set pathout_tar to:$pathout_tar";
725 # Validate pathout_tar as tar.
726 checkMakeTar
"$pathout_tar";
727 ## Add VERSION file if checkMakeTar had to create a tar (exited 1) or replace one (exited 2)
728 vbm
"exit status before magicWriteVersion:$?"
729 if [[ $?
-eq 1 ]] ||
[[ $?
-eq 2 ]]; then magicWriteVersion
; fi
730 } # Initialize tar, set pathout_tar
731 magicParseCompressionArg
() {
732 # Desc: Parses compression arguments specified by '-c' option
733 # Input: vars: optionCompress
734 # Output: cmd_compress, cmd_compress_suffix
735 # Depends: processArguments(), vbm(), checkapp(), gzip 1.9
736 if [[ "$optionCompress" = "true" ]]; then # Check if compression option active
737 if checkapp
gzip; then # Check if gzip available
738 cmd_compress
="gzip " && vbm
"cmd_compress:$cmd_compress";
739 cmd_compress_suffix
=".gz" && vbm
"cmd_compress_suffix:$cmd_compress_suffix";
741 yell
"ERROR:Compression enabled but \"gzip\" not found. Exiting."; exit 1;
744 cmd_compress
="tee /dev/null " && vbm
"cmd_compress:$cmd_compress";
745 cmd_compress_suffix
="" && vbm
"cmd_compress_suffix:$cmd_compress_suffix";
746 vbm
"DEBUG:Compression not enabled.";
748 } # Form compression cmd string and filename suffix
749 magicParseCustomTTL
() {
750 # Desc: Set user-specified TTLs for buffer and script
751 # Usage: magicParseCustomTTL
752 # Input: vars: argCustomBufferTTL (integer), argCustomScriptTTL_TE (string)
753 # Input: vars: optionCustomBufferTTL, optionCustomScriptTTL_TE
754 # Input: vars: bufferTTL (integer), scriptTTL_TE (string)
755 # Output: bufferTTL (integer), scriptTTL_TE (string)
756 # Depends: Bash 5.0.3, yell(), vbm(), validateInput(), showUsage()
758 # React to '-b, --buffer-ttl' option
759 if [[ "$optionCustomBufferTTL" = "true" ]]; then
760 ## T: Check if argCustomBufferTTL is an integer
761 if validateInput
"$argCustomBufferTTL" "integer"; then
762 ### T: argCustomBufferTTL is an integer
763 bufferTTL
="$argCustomBufferTTL" && vbm
"Custom bufferTTL from -b:$bufferTTL";
765 ### F: argcustomBufferTTL is not an integer
766 yell
"ERROR:Invalid integer argument for custom buffer time-to-live."; showUsage
; exit 1;
768 ## F: do not change bufferTTL
771 # React to '-B, --script-ttl' option
772 if [[ "$optionCustomScriptTTL_TE" = "true" ]]; then
773 ## T: Check if argCustomScriptTTL is a time element (ex: "day", "hour")
774 if validateInput
"$argCustomScriptTTL_TE" "time_element"; then
775 ### T: argCustomScriptTTL is a time element
776 scriptTTL_TE
="$argCustomScriptTTL_TE" && vbm
"Custom scriptTTL_TE from -B:$scriptTTL_TE";
778 ### F: argcustomScriptTTL is not a time element
779 yell
"ERROR:Invalid time element argument for custom script time-to-live."; showUsage
; exit 1;
781 ## F: do not change scriptTTL_TE
783 } # Sets custom script or buffer TTL if specified
785 # Desc: Parses -l option to set label
786 # In : optionLabel, argLabel
788 # Depends: Bash 5.0.3, vbm(), yell()
790 vbm
"STATUS:Started magicParseLabel() function.";
791 # Do nothing if optionLabel not set to true.
792 if [[ ! "$optionLabel" = "true" ]]; then
793 vbm
"STATUS:optionlabel not set to 'true'. Returning early.";
796 # Set label if optionLabel is true
797 if [[ "$optionLabel" = "true" ]]; then
798 label
="_""$argLabel";
799 vbm
"STATUS:Set label:$label";
801 vbm
"STATUS:Finished magicParseLabel() function.";
802 } # Set label used in output file name
803 magicParseProcessStrings
() {
804 # Desc: Processes user-supplied process strings into process commands for appendFileTar().
805 # Usage: magicParseProcessStrings
806 # In : vars: optionProcString optionNoStoreRaw optionStoreRaw argRawFileExt
807 # arry: argProcStrings, argProcFileExts
808 # Out: arry: procStrings, procFileExts
809 # Depends Bash 5.0.3, yell(), vbm()
812 vbm
"STATUS:Starting magicParseProcessStrings() function.";
813 vbm
"var:optionProcString:$optionProcString";
814 vbm
"var:optionNoStoreRaw:$optionNoStoreRaw";
815 vbm
"var:optionStoreRaw:$optionStoreRaw";
816 vbm
"var:argRawFileExt:$argRawFileExt";
817 vbm
"ary:argProcStrings:${argProcStrings[*]}";
818 vbm
"ary:argProcFileExts:${argProcFileExts[*]}"
820 ## Validate argRawFileExt
821 if [[ "$argRawFileExt" =~ ^
[.
][[:alnum
:]]*$
]]; then
822 rawFileExt
="$argRawFileExt";
825 # Add default stdin output file entries for procStrings, procFileExts
826 ## Check if user specified that no raw stdin be saved.
827 if [[ ! "$optionNoStoreRaw" = "true" ]]; then
828 ### T: --no-store-raw not set. Store raw. Append procStrings with cat.
829 #### Append procStrings array
830 procStrings
+=("cat ");
831 #### Check if --store-raw set.
832 if [[ "$optionStoreRaw" = "true" ]]; then
833 ##### T: --store-raw set. Append procFileExts with user-specified file ext
834 procFileExts
+=("$rawFileExt");
836 ##### F: --store-raw not set. Append procFileExts with default ".stdin" file ext
837 ###### Append procFileExts array
838 procFileExts
+=(".stdin");
841 ### F: --no-store-raw set. Do not store raw.
842 #### Do not append procStrings or procFileExts arrays.
846 # Do nothing more if optionProcString not set to true.
847 if [[ ! "$optionProcString" = "true" ]]; then
848 vbm
"STATUS:optionProcString not set to 'true'. Returning early.";
850 # Validate input array indices
851 ## Make sure that argProcStrings and argProcFileExts have same index counts
852 if ! [[ "${#argProcStrings[@]}" -eq "${#argProcFileExts[@]}" ]]; then
853 yell
"ERROR:Mismatch in number of elements in arrays argProcStrings and argProcFileExts:${#argProcStrings[@]} DNE ${#argProcFileExts[@]}";
854 yell
"argProcStrings:${argProcStrings[*]}"; yell
"argProcFileExts:${argProcFileExts[*]}"; exit 1; fi;
855 ## Make sure that no array elements are blank
856 for element
in "${argProcStrings[@]}"; do
857 if [[ -z "$element" ]]; then yell
"ERROR:Empty process string specified. Exiting."; exit 1; fi; done
858 for element
in "${argProcFileExts[@]}"; do
859 if [[ -z "$element" ]]; then yell
"ERROR:Empty output file extension specified. Exiting."; exit 1; fi; done
860 ## Make sure that no process string starts with '-' (ex: if only one arg supplied after '-p' option)
861 for element
in "${argProcStrings[@]}"; do
862 if [[ ! "$element" =~ ^
[-][[:print
:]]*$
]] && [[ "$element" =~ ^
[[:print
:]]*$
]]; then
863 yell
"ERROR:Illegal character '-' at start of process string element. Option syntax error?";
865 vbm
"STATUS:Quick check shows argProcStrings and argProcFileExts appear to have valid contents.";
866 procStrings
=("${argProcStrings[@]}"); # Export process command strings
867 procFileExts
=("${argProcFileExts[@]}"); # Export process command strings
868 vbm
"STATUS:Finished magicParseProcessStrings() function.";
869 } # Validate and save process strings and file extensions to arrays procStrings, procFileExts
870 magicParseRecipientArgs
() {
871 # Desc: Parses recipient arguments specified by '-r' option
872 # Input: vars: optionEncrypt, optionRecipients
873 # arry: argRecPubKeys from processArguments()
874 # Output: vars: cmd_encrypt, cmd_encrypt_suffix
875 # arry: recPubKeysValid, recPubKeysValidStatic
876 # Depends: processArguments(), yell(), vbm(), checkapp(), checkAgePubkey(), validateInput()
879 # Check if encryption option active.
880 if [[ "$optionEncrypt" = "true" ]] && [[ "$optionRecipients" = "true" ]]; then
881 if checkapp age
; then # Check that age is available.
882 for pubkey
in "${argRecPubKeys[@]}"; do # Validate recipient pubkey strings by forming test message
883 vbm
"DEBUG:Testing pubkey string:$pubkey";
884 if checkAgePubkey
"$pubkey" && \
885 ( validateInput
"$pubkey" "ssh_pubkey" || validateInput
"$pubkey" "age_pubkey"); then
886 #### Form age recipient string
887 recipients
="$recipients""-r '$pubkey' ";
888 vbm
"STATUS:Added pubkey for forming age recipient string:""$pubkey";
889 vbm
"DEBUG:recipients:""$recipients";
890 #### Add validated pubkey to recPubKeysValid array
891 recPubKeysValid
+=("$pubkey") && vbm
"DEBUG:recPubkeysValid:pubkey added:$pubkey";
893 yell
"ERROR:Exit code ""$?"". Invalid recipient pubkey string. Exiting."; exit 1;
896 vbm
"DEBUG:Finished processing argRecPubKeys array";
897 vbm
"STATUS:Array of validated pubkeys:${recPubKeysValid[*]}";
898 recPubKeysValidStatic
=("${recPubKeysValid[@]}"); # Save static image of pubkeys validated by this function
900 ## Form age command string
901 cmd_encrypt
="age ""$recipients " && vbm
"cmd_encrypt:$cmd_encrypt";
902 cmd_encrypt_suffix
=".age" && vbm
"cmd_encrypt_suffix:$cmd_encrypt_suffix";
904 yell
"ERROR:Encryption enabled but \"age\" not found. Exiting."; exit 1;
907 cmd_encrypt
="tee /dev/null " && vbm
"cmd_encrypt:$cmd_encrypt";
908 cmd_encrypt_suffix
="" && vbm
"cmd_encrypt_suffix:$cmd_encrypt_suffix";
909 vbm
"DEBUG:Encryption not enabled."
911 # Catch case if '-e' is set but '-r' or '-R' is not
912 if [[ "$optionEncrypt" = "true" ]] && [[ ! "$optionRecipients" = "true" ]]; then
913 yell
"ERROR:\\'-e\\' set but no \\'-r\\' or \\'-R\\' set."; exit 1; fi;
914 # Catch case if '-r' or '-R' set but '-e' is not
915 if [[ ! "$optionEncrypt" = "true" ]] && [[ "$optionRecipients" = "true" ]]; then
916 yell
"ERROR:\\'-r\\' or \\'-R\\' set but \\'-e\\' is not set."; exit 1; fi;
917 } # Populate recPubKeysValid with argRecPubKeys; form encryption cmd string and filename suffix
918 magicParseRecipientDir
() {
919 # Desc: Updates recPubKeysValid with pubkeys in dir specified by '-R' option ("recipient directory")
920 # Inputs: vars: optionEncrypt, optionRecDir, argRecDir,
921 # arry: recPubKeysValid
922 # Outputs: arry: recPubKeysValid
923 # Depends: processArguments(), yell(), vbm(), validateInput(), checkAgePubkey()
924 local recipientDir recFileLine updateRecipients
925 declare -a candRecPubKeysValid
927 # Check that '-e' and '-R' set
928 if [[ "$optionEncrypt" = "true" ]] && [[ "$optionRecDir" = "true" ]]; then
929 ### Check that argRecDir is a directory.
930 if [[ -d "$argRecDir" ]]; then
931 recipientDir
="$argRecDir" && vbm
"STATUS:Recipient watch directory detected:\"$recipientDir\"";
932 #### Initialize variable indicating outcome of pubkey review
933 unset updateRecipients
934 #### Add existing recipients
935 candRecPubKeysValid
=("${recPubKeysValidStatic[@]}");
936 #### Parse files in recipientDir
937 for file in "$recipientDir"/*; do
938 ##### Read first line of each file
939 recFileLine
="$(head -n1 "$file")" && vbm
"STATUS:Checking if pubkey:\"$recFileLine\"";
940 ##### check if first line is a valid pubkey
941 if checkAgePubkey
"$recFileLine" && \
942 ( validateInput
"$recFileLine" "ssh_pubkey" || validateInput
"$recFileLine" "age_pubkey"); then
943 ###### T: add candidate pubkey to candRecPubKeysValid
944 candRecPubKeysValid
+=("$recFileLine") && vbm
"STATUS:RecDir pubkey is valid pubkey:\"$recFileLine\"";
946 ###### F: throw warning;
947 yell
"ERROR:Invalid recipient file detected. Not modifying recipient list."
948 updateRecipients
="false";
951 #### Write updated recPubKeysValid array to recPubKeysValid if no failure detected
952 if ! [[ "$updateRecipients" = "false" ]]; then
953 recPubKeysValid
=("${candRecPubKeysValid[@]}") && vbm
"STATUS:Wrote candRecPubkeysValid to recPubKeysValid:\"${recPubKeysValid[*]}\"";
956 yell
"ERROR:$0:Recipient directory $argRecDir does not exist. Exiting."; exit 1;
959 # Handle case if '-R' set but '-e' not set
960 if [[ ! "$optionEncrypt" = "true" ]] && [[ "$optionRecDir" = "true" ]]; then
961 yell
"ERROR: \\'-R\\' is set but \\'-e\\' is not set."; fi;
962 } # Update recPubKeysValid with argRecDir
963 magicSetScriptTTL
() {
964 #Desc: Sets script_TTL seconds from provided time_element string argument
965 #Usage: magicSetScriptTTL [str time_element]
966 #Input: arg1: string (Ex: scriptTTL_TE; "day" or "hour")
967 #Output: var: scriptTTL (integer seconds)
968 #Depends: timeUntilNextHour, timeUntilNextDay
972 if [[ "$argTimeElement" = "day" ]]; then
973 # Set script lifespan to end at start of next day
974 if ! scriptTTL
="$(timeUntilNextDay)"; then # sets scriptTTL, then checks exit code
975 if [[ "$scriptTTL" -eq 0 ]]; then
976 ((scriptTTL
++)); # Add 1 because 0 would cause 'timeout' to never timeout.
978 yell
"ERROR: timeUntilNextDay exit code $?"; exit 1;
981 elif [[ "$argTimeElement" = "hour" ]]; then
982 # Set script lifespan to end at start of next hour
983 if ! scriptTTL
="$(timeUntilNextHour)"; then # sets scriptTTL, then checks exit code
984 if [[ "$scriptTTL" -eq 0 ]]; then
985 ((scriptTTL
++)); # Add 1 because 0 would cause 'timeout' to never timeout.
987 yell
"ERROR: timeUntilNextHour exit code $?"; exit 1;
991 yell
"ERROR:Invalid argument for setScriptTTL function:$argTimeElement"; exit 1;
993 } # Set scriptTTL in seconds until next (day|hour).
994 magicWriteVersion
() {
995 # Desc: Appends time-stamped VERSION to pathout_tar
996 # Usage: magicWriteVersion
997 # Input: vars: pathout_tar, dir_tmp
998 # Input: vars: scriptVersion, scriptURL, ageVersion, ageURL, scriptHostname
999 # Input: array: recPubKeysValid
1000 # Output: appends tar (pathout_tar)
1001 # Depends: bash 5.0.3, dateTimeShort(), appendArgTar()
1002 local fileoutVersion contentVersion pubKeyIndex pubKeyIndex
1004 # Set VERSION file name
1005 fileoutVersion
="$(dateTimeShort)..VERSION";
1007 # Gather VERSION data in contentVersion
1008 contentVersion
="scriptVersion=$scriptVersion";
1009 #contentVersion="$contentVersion""\\n";
1010 contentVersion
="$contentVersion""\\n""scriptName=$scriptName";
1011 contentVersion
="$contentVersion""\\n""scriptURL=$scriptURL";
1012 contentVersion
="$contentVersion""\\n""ageVersion=$ageVersion";
1013 contentVersion
="$contentVersion""\\n""ageURL=$ageURL";
1014 contentVersion
="$contentVersion""\\n""date=$(date --iso-8601=seconds)";
1015 contentVersion
="$contentVersion""\\n""hostname=$scriptHostname";
1016 ## Add list of recipient pubkeys
1017 for pubkey
in "${recPubKeysValid[@]}"; do
1019 contentVersion
="$contentVersion""\\n""PUBKEY_$pubKeyIndex=$pubkey";
1021 ## Process newline escapes
1022 contentVersion
="$(echo -e "$contentVersion")"
1024 # Write contentVersion as file fileoutVersion and write-append to pathout_tar
1025 appendArgTar
"$contentVersion" "$fileoutVersion" "$pathout_tar" "$dir_tmp";
1026 } # write version data to pathout_tar via appendArgTar()
1027 magicProcessWriteBuffer
() {
1028 # Desc: process and write buffer
1029 # In : vars: bufferTTL bufferTTL_STR scriptHostname label dir_tmp SECONDS
1031 # Out: file:(pathout_tar)
1032 # Depends: Bash 5.0.3, date 8.30, yell(), vbm(), dateTimeShort(),
1033 ### Note: These arrays should all have the same number of elements:
1034 ### pathouts, fileouts, procFileExts, procStrings
1036 local fn timeBufferStartLong timeBufferStart fileoutBasename
1037 local -a fileouts pathouts
1038 local writeCmd1 writeCmd2 writeCmd3 writeCmd4
1040 vbm
"DEBUG:STATUS:$fn:Started magicProcessWriteBuffer().";
1041 # Debug:Get function name
1042 fn
="${FUNCNAME[0]}";
1044 # Determine file paths (time is start of buffer period)
1045 ## Calculate start time
1046 timeBufferStartLong
="$(date --date="$bufferTTL seconds ago
" --iso-8601=seconds)" && \
1047 vbm
"timeBufferStartLong:$timeBufferStartLong";
1048 timeBufferStart
="$(dateTimeShort "$timeBufferStartLong" )" && \
1049 vbm
"timeBufferStart:$timeBufferStart"; # Note start time YYYYmmddTHHMMSS+zzzz (no separators)
1050 ## Set common basename
1051 fileoutBasename
="$timeBufferStart""--""$bufferTTL_STR""..""$scriptHostname""$label" && \
1052 vbm
"STATUS:Set fileoutBasename to:$fileoutBasename";
1053 ## Determine output file name array
1054 ### in: fileOutBasename cmd_compress_suffix cmd_encrypt_suffix procFileExts
1055 for fileExt
in "${procFileExts[@]}"; do
1056 fileouts
+=("$fileoutBasename""$fileExt""$cmd_compress_suffix""$cmd_encrypt_suffix") && \
1057 vbm
"STATUS:Added $fileExt to fileouts:${fileouts[*]}";
1059 for fileName
in "${fileouts[@]}"; do
1060 pathouts
+=("$dir_tmp"/"$fileName") && \
1061 vbm
"STATUS:Added $fileName to pathouts:${pathouts[*]}";
1063 ## Update pathout_tar
1066 # Process and write buffers to dir_tmp
1067 ## Prepare command strings
1068 writeCmd1
="printf \"%s\\\\n\" \"\${buffer[@]}\""; # printf "%s\\n" "${buffer[@]}"
1069 #writeCmd2="" # NOTE: Specified by parsing array procStrings
1070 writeCmd3
="$cmd_compress";
1071 writeCmd4
="$cmd_encrypt";
1073 ## Process buffer and write to dir_tmp
1074 for index
in "${!pathouts[@]}"; do
1075 writeCmd2
="${procStrings[$index]}"
1076 eval "$writeCmd1 | $writeCmd2 | $writeCmd3 | $writeCmd4" >> "${pathouts[$index]}";
1079 # Append dir_tmp files to pathout_tar
1080 wait; # Wait to avoid collision with older magicProcessWriteBuffer() instances (see https://www.tldp.org/LDP/abs/html/x9644.html )
1081 for index
in "${!pathouts[@]}"; do
1082 appendFileTar
"${pathouts[$index]}" "${fileouts[$index]}" "$pathout_tar" "$dir_tmp";
1085 # Remove secured chunks from dir_tmp
1086 for path
in "${pathouts[@]}"; do
1090 vbm
"DEBUG:STATUS:$fn:Finished magicProcessWriteBuffer().";
1091 } # Process and Write buffer
1095 processArguments
"$@";
1096 ## Determine working directory
1097 magicInitWorkingDir
; # Sets dir_tmp from argTempDirPriority
1098 ## Set output encryption and compression option strings
1099 ### React to "-e" and "-r" ("encryption recipients") options
1100 magicParseRecipientArgs
; # Updates recPubKeysValid, cmd_encrypt[_suffix] from argRecPubKeys
1101 ### React to "-R" ("recipient directory") option
1102 magicParseRecipientDir
; # Updates recPubKeysValid
1103 ### React to "-c" ("compression") option
1104 magicParseCompressionArg
; # Updates cmd_compress[_suffix]
1105 ## React to "-b" and "-B" (custom buffer and script TTL) options
1106 magicParseCustomTTL
; # Sets custom scriptTTL_TE and/or bufferTTL if specified
1107 ## React to "-p" (user-supplied process command and file extension strings) options
1108 magicParseProcessStrings
; # Sets arrays: procStrings, procFileExts
1109 ## React to "-l" (output file label) option
1110 magicParseLabel
; # sets label (ex: "_location")
1111 ## React to "-w" (how to name raw stdin file) option
1112 magicParseStoreRaw
; # sets raw_suffix
1114 # Perform secondary setup operations
1115 ## Set script lifespan (scriptTTL from scriptTTL_TE)
1116 magicSetScriptTTL
"$scriptTTL_TE";
1117 ## File name substring (ISO-8601 duration from bufferTTL)
1118 bufferTTL_STR
="$(timeDuration "$bufferTTL")" && vbm
"DEBUG:bufferTTL_STR:$bufferTTL_STR";
1119 ## Init temp working dir
1120 try mkdir
"$dir_tmp" && vbm
"DEBUG:Working dir created at dir_tmp:$dir_tmp";
1121 ## Initialize output tar (set pathout_tar)
1124 # Check vital apps, files, dirs
1125 if ! checkapp
tar && ! checkdir
"$dirOut" "dir_tmp"; then
1126 yell
"ERROR:Critical components missing.";
1127 displayMissing
; yell
"Exiting."; exit 1; fi
1129 # MAIN LOOP: Run until script TTL seconds pass
1131 while [[ $SECONDS -lt "scriptTTL" ]]; do
1132 bufferTOD
="$((SECONDS + bufferTTL))"; # Set buffer round time-of-death
1133 lineCount
=0; # Debug counter
1134 # Consume stdin to fill buffer until buffer time-of-death (TOD) arrives
1135 while read -r -t "$bufferTTL" line
&& [[ $SECONDS -lt "$bufferTOD" ]]; do
1136 # Append line to buffer array
1138 echo "DEBUG:Processing line:$lineCount";
1139 echo "DEBUG:Current line :$line";
1140 echo "DEBUG:buf elem count :${#buffer[@]}";
1143 # Create dir_tmp if missing
1144 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
1145 # Update encryption recipient array
1146 magicParseRecipientDir
; # Update recPubKeysValid with argRecDir
1147 # Export buffer to asynchronous processing.
1148 magicProcessWriteBuffer
&
1149 unset buffer
; # Clear buffer array for next bufferRound
1150 # Increment buffer round
1156 try
rm -r "$dir_tmp" && vbm
"Removed dir_tmp:$dir_tmp";
1158 vbm
"STATUS:Main function finished.";
1161 #===END Declare local script functions===
1162 #==END Define script parameters==
1164 #==BEGIN Perform work and exit==
1165 main
"$@" # Run main function.
1167 #==END Perform work and exit==
1169 # Author: Steven Baltakatei Sandoval;