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.2"; # 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
231 cmd | bklog [ options ]
235 Display help information.
237 Display script version.
239 Display debugging info.
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
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:
253 -p "gpsbabel -i nmea -f - -o gpx -F - " ".gpx"
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':
272 Stdin data is saved in a '.nmea' file within the output tar.
274 Do not store raw stdin in output tar.
276 Compress output with gzip (before encryption if enabled).
278 Specify time zone. (ex: "America/New_York")
279 -t, --temp-dir [path dir]
280 Specify parent directory for temporary working directory.
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"
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 - " ".gpx"
300 } # Display information on how to use this script.
302 yell
"$scriptVersion"
303 } # Display script version.
305 # Desc: Set time zone environment variable TZ
306 # Usage: setTimeZoneEV arg1
308 # Input: arg1: 'date'-compatible timezone string (ex: "America/New_York")
309 # TZDIR env var (optional; default: "/usr/share/zoneinfo")
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
319 if ! [[ $# -eq 1 ]]; then
320 yell
"ERROR:Invalid argument count.";
324 # Read TZDIR env var if available
325 if printenv TZDIR
1>/dev
/null
2>&1; then
326 tzDir
="$(printenv TZDIR)";
328 tzDir
="/usr/share/zoneinfo";
332 if ! [[ -f "$tzDir"/"$argTimeZone" ]]; then
333 yell
"ERROR:Invalid time zone argument.";
336 # Export ARG1 as TZ environment variable
337 TZ
="$argTimeZone" && export TZ
&& returnState
="true";
340 # Determine function return code
341 if [ "$returnState" = "true" ]; then
344 } # Exports TZ environment variable
346 # Desc: Date without separators (YYYYmmdd)
347 # Usage: dateShort ([str date])
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
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";
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";
368 ### F: Time argument not valid; exit
369 yell
"ERROR:Invalid time argument supplied. Exiting."; exit 1;
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";
377 # Desc: Appends [processed] file to tar
378 # Usage: appendFileTar [file path] [name of file to be inserted] [tar path] [temp dir] ([process cmd])
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
395 #yell "DEBUG:STATUS:$fn:Started appendFileTar()."
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
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
406 # Input command string
409 # Write to temporary working dir
410 eval "$cmd0 | $cmd1" > "$tmpDir"/"$fileName";
413 try
tar --append --directory="$tmpDir" --file="$tarPath" "$fileName";
414 #yell "DEBUG:STATUS:$fn:Finished appendFileTar()."
415 } # Append [processed] file to Tar archive
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])
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
436 argSeconds
="$1"; # read arg1 (seconds)
437 argPrecision
="$2"; # read arg2 (precision)
438 precision
=2; # set default precision
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
445 # Check that argSeconds provided
446 if [[ $# -ge 1 ]]; then
447 ## Check that argSeconds is a positive integer
448 if [[ "$argSeconds" =~ ^
[[:digit
:]]+$
]]; then
451 yell
"ERROR:argSeconds not a digit.";
452 returnState
="error_input";
455 yell
"ERROR:No argument provided. Exiting.";
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";
465 yell
"ERROR:argPrecision not a positive integer. (is $argPrecision ). Leaving early.";
466 returnState
="error_input";
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
499 ## Determine which fields to display (see ISO-8601:2004 §4.4.3.2)
500 witherPrecision
="false"
503 if $hasYears && [[ $precision -gt 0 ]]; then
505 witherPrecision
="true";
507 displayYears
="false";
509 if $witherPrecision; then ((precision--
)); fi;
512 if $hasMonths && [[ $precision -gt 0 ]]; then
513 displayMonths
="true";
514 witherPrecision
="true";
516 displayMonths
="false";
518 if $witherPrecision && [[ $precision -gt 0 ]]; then
519 displayMonths
="true";
521 if $witherPrecision; then ((precision--
)); fi;
524 if $hasDays && [[ $precision -gt 0 ]]; then
526 witherPrecision
="true";
530 if $witherPrecision && [[ $precision -gt 0 ]]; then
533 if $witherPrecision; then ((precision--
)); fi;
536 if $hasHours && [[ $precision -gt 0 ]]; then
538 witherPrecision
="true";
540 displayHours
="false";
542 if $witherPrecision && [[ $precision -gt 0 ]]; then
545 if $witherPrecision; then ((precision--
)); fi;
548 if $hasMinutes && [[ $precision -gt 0 ]]; then
549 displayMinutes
="true";
550 witherPrecision
="true";
552 displayMinutes
="false";
554 if $witherPrecision && [[ $precision -gt 0 ]]; then
555 displayMinutes
="true";
557 if $witherPrecision; then ((precision--
)); fi;
561 if $hasSeconds && [[ $precision -gt 0 ]]; then
562 displaySeconds
="true";
563 witherPrecision
="true";
565 displaySeconds
="false";
567 if $witherPrecision && [[ $precision -gt 0 ]]; then
568 displaySeconds
="true";
570 if $witherPrecision; then ((precision--
)); fi;
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
576 ## Construct duration output string
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
593 ## Output duration string to stdout
594 echo "$output" && returnState
="true";
596 #===Determine function return code===
597 if [ "$returnState" = "true" ]; then
599 elif [ "$returnState" = "error_input" ]; then
603 yell
"ERROR:Unknown";
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
619 if [[ "$optionTmpDir" = "true" ]]; then
620 if [[ -d "$argTempDirPriority" ]]; then
621 dir_tmp_parent
="$argTempDirPriority";
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.
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";
634 yell
"ERROR:No valid working directory available. Exiting.";
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().
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()
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";
667 yell
"ERROR:Compression enabled but \"gzip\" not found. Exiting."; exit 1;
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.";
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()
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";
691 ### F: argcustomBufferTTL is not an integer
692 yell
"ERROR:Invalid integer argument for custom buffer time-to-live."; showUsage
; exit 1;
694 ## F: do not change bufferTTL
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";
704 ### F: argcustomScriptTTL is not a time element
705 yell
"ERROR:Invalid time element argument for custom script time-to-live."; showUsage
; exit 1;
707 ## F: do not change scriptTTL_TE
709 } # Sets custom script or buffer TTL if specified
711 # Desc: Parses -l option to set label
712 # In : optionLabel, argLabel
714 # Depends: Bash 5.0.3, vbm(), yell()
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.";
722 # Set label if optionLabel is true
723 if [[ "$optionLabel" = "true" ]]; then
724 label
="_""$argLabel";
725 vbm
"STATUS:Set label:$label";
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()
738 vbm
"STATUS:Starting magicParseProcessStrings() function.";
740 ## Validate argRawFileExt
741 if [[ "$argRawFileExt" =~ ^
[.
][[:alnum
:]]*$
]]; then
742 rawFileExt
="$argRawFileExt";
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");
756 ##### F: --store-raw not set. Append procFileExts with default ".stdin" file ext
757 ###### Append procFileExts array
758 procFileExts
+=(".stdin");
761 ### F: --no-store-raw set. Do not store raw.
762 #### Do not append procStrings or procFileExts arrays.
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.";
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?";
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()
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";
813 yell
"ERROR:Exit code ""$?"". Invalid recipient pubkey string. Exiting."; exit 1;
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
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";
824 yell
"ERROR:Encryption enabled but \"age\" not found. Exiting."; exit 1;
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."
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
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\"";
866 ###### F: throw warning;
867 yell
"ERROR:Invalid recipient file detected. Not modifying recipient list."
868 updateRecipients
="false";
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[*]}\"";
876 yell
"ERROR:$0:Recipient directory $argRecDir does not exist. Exiting."; exit 1;
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
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.
898 yell
"ERROR: timeUntilNextDay exit code $?"; exit 1;
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.
907 yell
"ERROR: timeUntilNextHour exit code $?"; exit 1;
911 yell
"ERROR:Invalid argument for setScriptTTL function:$argTimeElement"; exit 1;
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
924 # Set VERSION file name
925 fileoutVersion
="$(dateTimeShort)..VERSION";
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
939 contentVersion
="$contentVersion""\\n""PUBKEY_$pubKeyIndex=$pubkey";
941 ## Process newline escapes
942 contentVersion
="$(echo -e "$contentVersion")"
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
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
956 local fn timeBufferStartLong timeBufferStart fileoutBasename
957 local -a fileouts pathouts
958 local writeCmd1 writeCmd2 writeCmd3 writeCmd4
960 vbm
"DEBUG:STATUS:$fn:Started magicProcessWriteBuffer().";
961 # Debug:Get function name
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[*]}";
979 for fileName
in "${fileouts[@]}"; do
980 pathouts
+=("$dir_tmp"/"$fileName") && \
981 vbm
"STATUS:Added $fileName to pathouts:${pathouts[*]}";
983 ## Update pathout_tar
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";
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]}";
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";
1005 # Remove secured chunks from dir_tmp
1006 for path
in "${pathouts[@]}"; do
1010 vbm
"DEBUG:STATUS:$fn:Finished magicProcessWriteBuffer().";
1011 } # Process and Write buffer
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
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)
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
1049 # MAIN LOOP: Run until script TTL seconds pass
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
1058 echo "DEBUG:Processing line:$lineCount";
1059 echo "DEBUG:Current line :$line";
1060 echo "DEBUG:buf elem count :${#buffer[@]}";
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
1076 try
rm -r "$dir_tmp" && vbm
"Removed dir_tmp:$dir_tmp";
1078 vbm
"STATUS:Main function finished.";
1081 #===END Declare local script functions===
1082 #==END Define script parameters==
1084 #==BEGIN Perform work and exit==
1085 main
"$@" # Run main function.
1087 #==END Perform work and exit==
1089 # Author: Steven Baltakatei Sandoval;