fix(bkgpslog):Remove nuisance error if -R not set
[EVA-2020-02.git] / exec / bkgpslog
index 6daa5414b82c5c3d1561569e61262469b8dc0f58..2b19e31e087024c59416c97824fc1962e4b19ca7 100755 (executable)
@@ -7,14 +7,14 @@
 #==BEGIN Define script parameters==
 ## Logging Behavior parameters
 BUFFER_TTL="300"; # time between file writes
 #==BEGIN Define script parameters==
 ## Logging Behavior parameters
 BUFFER_TTL="300"; # time between file writes
-SCRIPT_TTL="day"; # (day|hour)
+SCRIPT_TTL_TE="day"; # (day|hour)
 #### TZ="UTC"; export TZ; # Default time zone; overridden by '--time-zone=[str]' option
 DIR_TMP_DEFAULT="/dev/shm"; # Default parent of working directory
 
 SCRIPT_TIME_START=$(date +%Y%m%dT%H%M%S.%N);
 PATH="$HOME/.local/bin:$PATH";   # Add "$(systemd-path user-binaries)" path in case apps saved there
 SCRIPT_HOSTNAME=$(hostname);     # Save hostname of system running this script.
 #### TZ="UTC"; export TZ; # Default time zone; overridden by '--time-zone=[str]' option
 DIR_TMP_DEFAULT="/dev/shm"; # Default parent of working directory
 
 SCRIPT_TIME_START=$(date +%Y%m%dT%H%M%S.%N);
 PATH="$HOME/.local/bin:$PATH";   # Add "$(systemd-path user-binaries)" path in case apps saved there
 SCRIPT_HOSTNAME=$(hostname);     # Save hostname of system running this script.
-SCRIPT_VERSION="0.3.9";          # Define version of script.
+SCRIPT_VERSION="0.4.3";          # Define version of script.
 SCRIPT_NAME="bkgpslog";          # Define basename of script file.
 SCRIPT_URL="https://gitlab.com/baltakatei/ninfacyzga-01"; # Define wesite hosting this script.
 AGE_VERSION="1.0.0-beta2";       # Define version of age (encryption program)
 SCRIPT_NAME="bkgpslog";          # Define basename of script file.
 SCRIPT_URL="https://gitlab.com/baltakatei/ninfacyzga-01"; # Define wesite hosting this script.
 AGE_VERSION="1.0.0-beta2";       # Define version of age (encryption program)
@@ -152,21 +152,26 @@ showUsage() {
     echoerr "            Display debugging info."
     echoerr "    -e, --encrypt"
     echoerr "            Encrypt output."
     echoerr "            Display debugging info."
     echoerr "    -e, --encrypt"
     echoerr "            Encrypt output."
-    echoerr "    -r, --recipient [ pubkey string ]"
+    echoerr "    -r, --recipient [ string pubkey ]"
     echoerr "            Specify recipient. May be age or ssh pubkey."
     echoerr "            Specify recipient. May be age or ssh pubkey."
+    echoerr "            May be specified multiple times for multiple pubkeys."
     echoerr "            See https://github.com/FiloSottile/age"
     echoerr "            See https://github.com/FiloSottile/age"
-    echoerr "    -o, --output [ directory ]"
+    echoerr "    -o, --output [ path dir ]"
     echoerr "            Specify output directory to save logs."
     echoerr "    -c, --compress"
     echoerr "            Compress output with gzip (before encryption if enabled)."
     echoerr "    -z, --time-zone"
     echoerr "            Specify time zone. (ex: \"America/New_York\")"
     echoerr "            Specify output directory to save logs."
     echoerr "    -c, --compress"
     echoerr "            Compress output with gzip (before encryption if enabled)."
     echoerr "    -z, --time-zone"
     echoerr "            Specify time zone. (ex: \"America/New_York\")"
-    echoerr "    -t, --temp-dir"
+    echoerr "    -t, --temp-dir [path dir]"
     echoerr "            Specify parent directory for temporary working directory."
     echoerr "            Default: \"/dev/shm\""
     echoerr "            Specify parent directory for temporary working directory."
     echoerr "            Default: \"/dev/shm\""
-    echoerr "    -R, --recipient-dir"
+    echoerr "    -R, --recipient-dir [path dir]"
     echoerr "            Specify directory containing files whose first lines are"
     echoerr "            Specify directory containing files whose first lines are"
-    echoerr "            to be interpreted as pubkey strings (see \'-r\' option)."
+    echoerr "            to be interpreted as pubkey strings (see \\'-r\\' option)."
+    echoerr "    -b, --buffer-ttl [integer]"
+    echoerr "            Specify custom buffer period in seconds (default: 300 seconds)"
+    echoerr "    -B, --script-ttl [integer]"
+    echoerr "            Specify custom script time-to-live in seconds (default: \"day\")"
     echoerr
     echoerr "EXAMPLE: (bash script lines)"
     echoerr "/bin/bash bkgpslog -v -e -c \\"
     echoerr
     echoerr "EXAMPLE: (bash script lines)"
     echoerr "/bin/bash bkgpslog -v -e -c \\"
@@ -209,13 +214,14 @@ processArguments() {
            --version) showVersion; exit 1;; # Show version
            -v | --verbose) OPTION_VERBOSE="true"; vbm "DEBUG:Verbose mode enabled.";; # Enable verbose mode.
            -o | --output) if [ -d "$2" ]; then DIR_OUT="$2"; vbm "DEBUG:DIR_OUT:$DIR_OUT"; shift; fi ;; # Define output directory.
            --version) showVersion; exit 1;; # Show version
            -v | --verbose) OPTION_VERBOSE="true"; vbm "DEBUG:Verbose mode enabled.";; # Enable verbose mode.
            -o | --output) if [ -d "$2" ]; then DIR_OUT="$2"; vbm "DEBUG:DIR_OUT:$DIR_OUT"; shift; fi ;; # Define output directory.
-           -e | --encrypt) OPTION_ENCRYPT="true"; vbm "DEBUG:Encrypted output mode enabled.";;
-           -r | --recipient) # Add 'age' recipient via public key string
-               argRecPubKeys+=("$2"); vbm "STATUS:pubkey added:""$2"; shift;;
-           -c | --compress) OPTION_COMPRESS="true"; vbm "DEBUG:Compressed output mode enabled.";;
-           -z | --time-zone) try setTimeZoneEV "$2"; shift;;
-           -t | --temp-dir) OPTION_TMPDIR="true" && argTmpDirPriority="$2"; shift;;
-           -R | --recipient-dir) OPTION_RECDIR="true" && argRecDir="$2"; shift;;
+           -e | --encrypt) OPTION_ENCRYPT="true"; vbm "DEBUG:Encrypted output mode enabled.";; # Enable encryption
+           -r | --recipient) OPTION_RECIPIENTS="true"; argRecPubKeys+=("$2"); vbm "STATUS:pubkey added:""$2"; shift;; # Add recipients
+           -c | --compress) OPTION_COMPRESS="true"; vbm "DEBUG:Compressed output mode enabled.";; # Enable compression
+           -z | --time-zone) try setTimeZoneEV "$2"; shift;; # Set timestamp timezone
+           -t | --temp-dir) OPTION_TMPDIR="true" && argTempDirPriority="$2"; shift;; # Set time zone
+           -R | --recipient-dir) OPTION_RECIPIENTS="true"; OPTION_RECDIR="true" && argRecDir="$2"; shift;; # Add recipient watch dir
+           -b | --buffer-ttl) OPTION_CUSTOM_BUFFERTTL="true" && argCustomBufferTTL="$2"; shift;; # Set custom buffer period (default: 300 seconds)
+           -B | --script-ttl) OPTION_CUSTOM_SCRIPTTTL_TE="true" && argCustomScriptTTL="$2"; shift;; # Set custom script TTL (default: "day")
            *) echoerr "ERROR: Unrecognized argument: $1"; echoerr "STATUS:All arguments:$*"; exit 1;; # Handle unrecognized options.
        esac
        shift
            *) echoerr "ERROR: Unrecognized argument: $1"; echoerr "STATUS:All arguments:$*"; exit 1;; # Handle unrecognized options.
        esac
        shift
@@ -637,34 +643,34 @@ displayMissing() {
 
     #==END Display errors==
 } # Display missing apps, files, dirs
 
     #==END Display errors==
 } # Display missing apps, files, dirs
-setScriptTTL() {
-    #Desc: Sets script TTL
-    #Usage: setScriptTTL arg1
-    #Input: arg1: "day" or "hour"
-    #Output: scriptTTL
-    #Depends: timeUntilNextHour or timeUntilNextDay
-    local ARG1
-    ARG1="$1"
-    if [[ "$ARG1" = "day" ]]; then
+magicSetScriptTTL() {
+    #Desc: Sets script_TTL seconds from provided time_element string argument
+    #Usage: magicSetScriptTTL [str time_element]
+    #Input: arg1: string (Ex: SCRIPT_TTL_TE; "day" or "hour")
+    #Output: var: SCRIPT_TTL (integer seconds)
+    #Depends: timeUntilNextHour, timeUntilNextDay
+    local argTimeElement
+    argTimeElement="$1"
+    if [[ "$argTimeElement" = "day" ]]; then
            # Set script lifespan to end at start of next day
            # Set script lifespan to end at start of next day
-       if ! scriptTTL="$(timeUntilNextDay)"; then
-           if [[ "$scriptTTL" -eq 0 ]]; then
-           ((scriptTTL++)); # Add 1 because 0 would cause 'timeout' to never timeout.
+       if ! SCRIPT_TTL="$(timeUntilNextDay)"; then
+           if [[ "$SCRIPT_TTL" -eq 0 ]]; then
+           ((SCRIPT_TTL++)); # Add 1 because 0 would cause 'timeout' to never timeout.
            else
            yell "ERROR: timeUntilNextDay exit code $?"; exit 1;
            fi;
        fi;
            else
            yell "ERROR: timeUntilNextDay exit code $?"; exit 1;
            fi;
        fi;
-    elif [[ "$ARG1" = "hour" ]]; then
+    elif [[ "$argTimeElement" = "hour" ]]; then
        # Set script lifespan to end at start of next hour
        # Set script lifespan to end at start of next hour
-       if ! scriptTTL="$(timeUntilNextHour)"; then
-           if [[ "$scriptTTL" -eq 0 ]]; then
-               ((scriptTTL++)); # Add 1 because 0 would cause 'timeout' to never timeout.
+       if ! SCRIPT_TTL="$(timeUntilNextHour)"; then
+           if [[ "$SCRIPT_TTL" -eq 0 ]]; then
+               ((SCRIPT_TTL++)); # Add 1 because 0 would cause 'timeout' to never timeout.
            else
                yell "ERROR: timeUntilNextHour exit code $?"; exit 1;
            fi;
        fi;
     else
            else
                yell "ERROR: timeUntilNextHour exit code $?"; exit 1;
            fi;
        fi;
     else
-       yell "ERROR:Invalid argument for setScriptTTL function."; exit 1;
+       yell "ERROR:Invalid argument for setScriptTTL function:$argTimeElement"; exit 1;
     fi
 } # Seconds until next (day|hour).
 checkMakeTar() {
     fi
 } # Seconds until next (day|hour).
 checkMakeTar() {
@@ -836,7 +842,7 @@ checkAgePubkey() {
 validateInput() {
     # Desc: Validates Input
     # Usage: validateInput [str input] [str input type]
 validateInput() {
     # Desc: Validates Input
     # Usage: validateInput [str input] [str input type]
-    # Version: 0.2.1
+    # Version: 0.3.0
     # Input: arg1: string to validate
     #        arg2: string specifying input type (ex:"ssh_pubkey")
     # Output: return code 0: if input string matched specified string type
     # Input: arg1: string to validate
     #        arg2: string specifying input type (ex:"ssh_pubkey")
     # Output: return code 0: if input string matched specified string type
@@ -858,7 +864,7 @@ validateInput() {
     ### Check for alnum/dash base64 (ex: "ssh-rsa AAAAB3NzaC1yc2EAAA")
     if [[ "$argType" = "ssh_pubkey" ]]; then
        if [[ "$argInput" =~ ^[[:alnum:]-]*[\ ]*[[:alnum:]+/=]*$ ]]; then
     ### Check for alnum/dash base64 (ex: "ssh-rsa AAAAB3NzaC1yc2EAAA")
     if [[ "$argType" = "ssh_pubkey" ]]; then
        if [[ "$argInput" =~ ^[[:alnum:]-]*[\ ]*[[:alnum:]+/=]*$ ]]; then
-       return 0; fi; fi;
+           return 0; fi; fi;
 
     ## age_pubkey
     ### Check for age1[:bech32:]
 
     ## age_pubkey
     ### Check for age1[:bech32:]
@@ -866,6 +872,22 @@ validateInput() {
        if [[ "$argInput" =~ ^age1[qpzry9x8gf2tvdw0s3jn54khce6mua7l]*$ ]]; then
            return 0; fi; fi
 
        if [[ "$argInput" =~ ^age1[qpzry9x8gf2tvdw0s3jn54khce6mua7l]*$ ]]; then
            return 0; fi; fi
 
+    ## integer
+    if [[ "$argType" = "integer" ]]; then
+       if [[ "$argInput" =~ ^[[:digit:]]*$ ]]; then
+           return 0; fi; fi;
+
+    ## time element (year, month, week, day, hour, minute, second)
+    if [[ "$argType" = "time_element" ]]; then
+       if [[ "$argInput" = "year" ]] || \
+              [[ "$argInput" = "month" ]] || \
+              [[ "$argInput" = "week" ]] || \
+              [[ "$argInput" = "day" ]] || \
+              [[ "$argInput" = "hour" ]] || \
+              [[ "$argInput" = "minute" ]] || \
+              [[ "$argInput" = "second" ]]; then
+           return 0; fi; fi;
+    
     # Return error if no condition matched.
     return 1;
 } # Validates strings
     # Return error if no condition matched.
     return 1;
 } # Validates strings
@@ -953,30 +975,31 @@ magicGatherWriteBuffer() {
 } # write buffer to disk
 magicParseRecipientDir() {
     # Desc: Updates recPubKeysValid with pubkeys in dir specified by '-R' option ("recipient directory")
 } # write buffer to disk
 magicParseRecipientDir() {
     # Desc: Updates recPubKeysValid with pubkeys in dir specified by '-R' option ("recipient directory")
-    # Inputs: vars: OPTION_RECDIR, argRecDir, 
-    #         arry: recPubKeysValid
-    # Outputs: arry: recPubKeysValid (modified with pubkeys in argRecDir if pubkeys valid)
+    # Inputs:  vars: OPTION_RECDIR, argRecDir, OPTION_ENCRYPT
+    #          arry: recPubKeysValid
+    # Outputs: arry: recPubKeysValid
     # Depends: processArguments,
     local recFileLine updateRecipients recipientDir
     declare -a candRecPubKeysValid
     # Depends: processArguments,
     local recFileLine updateRecipients recipientDir
     declare -a candRecPubKeysValid
-    
-    if [[ "$OPTION_RECDIR" = "true" ]]; then
+
+    # Check that '-e' and '-R' set
+    if [[ "$OPTION_ENCRYPT" = "true" ]] && [[ "$OPTION_RECDIR" = "true" ]]; then
        ### Check that argRecDir is a directory.
        if [[ -d "$argRecDir" ]]; then
        ### Check that argRecDir is a directory.
        if [[ -d "$argRecDir" ]]; then
-           recipientDir="$argRecDir";
+           recipientDir="$argRecDir" && vbm "STATUS:Recipient watch directory detected:\"$recipientDir\"";
            #### Initialize variable indicating outcome of pubkey review
            unset updateRecipients
            #### Add existing recipients
            #### Initialize variable indicating outcome of pubkey review
            unset updateRecipients
            #### Add existing recipients
-           candRecPubKeysValid=(${recPubKeysValid[@]});
+           candRecPubKeysValid=("${recPubKeysValidStatic[@]}");
            #### Parse files in recipientDir
            for file in "$recipientDir"/*; do
                ##### Read first line of each file
            #### Parse files in recipientDir
            for file in "$recipientDir"/*; do
                ##### Read first line of each file
-               recFileLine="$(cat "$file" | head -n1)";
+               recFileLine="$(head -n1 "$file")" && vbm "STATUS:Checking if pubkey:\"$recFileLine\"";
                ##### check if first line is a valid pubkey
                if checkAgePubkey "$recFileLine" && \
                        ( validateInput "$recFileLine" "ssh_pubkey" || validateInput "$recFileLine" "age_pubkey"); then
                    ###### T: add candidate pubkey to candRecPubKeysValid
                ##### check if first line is a valid pubkey
                if checkAgePubkey "$recFileLine" && \
                        ( validateInput "$recFileLine" "ssh_pubkey" || validateInput "$recFileLine" "age_pubkey"); then
                    ###### T: add candidate pubkey to candRecPubKeysValid
-                   candRecPubKeysValid+=("$recFileLine");
+                   candRecPubKeysValid+=("$recFileLine") && vbm "STATUS:RecDir pubkey is valid pubkey:\"$recFileLine\"";
                else
                    ###### F: throw warning;
                    yell "ERROR:Invalid recipient file detected. Not modifying recipient list."
                else
                    ###### F: throw warning;
                    yell "ERROR:Invalid recipient file detected. Not modifying recipient list."
@@ -984,24 +1007,28 @@ magicParseRecipientDir() {
                fi;
            done
            #### Write updated recPubKeysValid array to recPubKeysValid if no failure detected
                fi;
            done
            #### Write updated recPubKeysValid array to recPubKeysValid if no failure detected
-           if ! updateRecipients="false"; then
-               recPubKeysValid=(${candRecPubKeysValid[@]});
+           if ! [[ "$updateRecipients" = "false" ]]; then
+               recPubKeysValid=("${candRecPubKeysValid[@]}") && vbm "STATUS:Wrote candRecPubkeysValid to recPubKeysValid:\"${recPubKeysValid[@]}\"";
            fi;
        else
            yell "ERROR:$0:Recipient directory $argRecDir does not exist. Exiting."; exit 1;
        fi;
     fi;
            fi;
        else
            yell "ERROR:$0:Recipient directory $argRecDir does not exist. Exiting."; exit 1;
        fi;
     fi;
+    # Handle case if '-R' set but '-e' not set
+    if [[ ! "$OPTION_ENCRYPT" = "true" ]] && [[ "$OPTION_RECDIR" = "true" ]]; then
+       yell "ERROR: \\'-R\\' is set but \\'-e\\' is not set."; fi;
 } # Update recPubKeysValid with argRecDir
 magicParseRecipientArgs() {
     # Desc: Parses recipient arguments specified by '-r' option
     # Input:  vars: OPTION_ENCRYPT from processArguments()
     #         arry: argRecPubKeys from processArguments()
     # Output: vars: CMD_ENCRYPT, CMD_ENCRYPT_SUFFIX
 } # Update recPubKeysValid with argRecDir
 magicParseRecipientArgs() {
     # Desc: Parses recipient arguments specified by '-r' option
     # Input:  vars: OPTION_ENCRYPT from processArguments()
     #         arry: argRecPubKeys from processArguments()
     # Output: vars: CMD_ENCRYPT, CMD_ENCRYPT_SUFFIX
-    #         arry: recPubKeysValid
+    #         arry: recPubKeysValid, recPubKeysValidStatic
     # Depends: checkapp(), checkAgePubkey(), validateInput(), processArguments()
     local recipients
 
     # Depends: checkapp(), checkAgePubkey(), validateInput(), processArguments()
     local recipients
 
-    if [[ "$OPTION_ENCRYPT" = "true" ]]; then # Check if encryption option active.
+    # Check if encryption option active.
+    if [[ "$OPTION_ENCRYPT" = "true" ]] && [[ "$OPTION_RECIPIENTS" = "true" ]]; then 
        if checkapp age; then # Check that age is available.
            for pubkey in "${argRecPubKeys[@]}"; do # Validate recipient pubkey strings by forming test message
                vbm "DEBUG:Testing pubkey string:$pubkey";
        if checkapp age; then # Check that age is available.
            for pubkey in "${argRecPubKeys[@]}"; do # Validate recipient pubkey strings by forming test message
                vbm "DEBUG:Testing pubkey string:$pubkey";
@@ -1015,21 +1042,29 @@ magicParseRecipientArgs() {
                    recPubKeysValid+=("$pubkey") && vbm "DEBUG:recPubkeysValid:pubkey added:$pubkey";
                else
                    yell "ERROR:Exit code ""$?"". Invalid recipient pubkey string. Exiting."; exit 1;
                    recPubKeysValid+=("$pubkey") && vbm "DEBUG:recPubkeysValid:pubkey added:$pubkey";
                else
                    yell "ERROR:Exit code ""$?"". Invalid recipient pubkey string. Exiting."; exit 1;
-               fi
+               fi;
            done
            vbm "DEBUG:Finished processing argRecPubKeys array";
            done
            vbm "DEBUG:Finished processing argRecPubKeys array";
+           vbm "STATUS:Array of validated pubkeys:${recPubKeysValid[@]}";
+           recPubKeysValidStatic="${recPubKeysValid[@]}"; # Save static image of pubkeys validated by this function
 
            ##  Form age command string
            CMD_ENCRYPT="age ""$recipients " && vbm "CMD_ENCRYPT:$CMD_ENCRYPT";
            CMD_ENCRYPT_SUFFIX=".age" && vbm "CMD_ENCRYPT_SUFFIX:$CMD_ENCRYPT_SUFFIX";
        else
            yell "ERROR:Encryption enabled but \"age\" not found. Exiting."; exit 1;
 
            ##  Form age command string
            CMD_ENCRYPT="age ""$recipients " && vbm "CMD_ENCRYPT:$CMD_ENCRYPT";
            CMD_ENCRYPT_SUFFIX=".age" && vbm "CMD_ENCRYPT_SUFFIX:$CMD_ENCRYPT_SUFFIX";
        else
            yell "ERROR:Encryption enabled but \"age\" not found. Exiting."; exit 1;
-       fi
+       fi;
     else
        CMD_ENCRYPT="tee /dev/null " && vbm "CMD_ENCRYPT:$CMD_ENCRYPT";
        CMD_ENCRYPT_SUFFIX="" && vbm "CMD_ENCRYPT_SUFFIX:$CMD_ENCRYPT_SUFFIX";
        vbm "DEBUG:Encryption not enabled."
     else
        CMD_ENCRYPT="tee /dev/null " && vbm "CMD_ENCRYPT:$CMD_ENCRYPT";
        CMD_ENCRYPT_SUFFIX="" && vbm "CMD_ENCRYPT_SUFFIX:$CMD_ENCRYPT_SUFFIX";
        vbm "DEBUG:Encryption not enabled."
-    fi    
+    fi;
+    # Catch case if '-e' is set but '-r' or '-R' is not
+    if [[ "$OPTION_ENCRYPT" = "true" ]] && [[ ! "$OPTION_RECIPIENTS" = "true" ]]; then
+       yell "ERROR:\\'-e\\' set but no \\'-r\\' or \\'-R\\' set."; exit 1; fi;
+    # Catch case if '-r' or '-R' set but '-e' is not
+    if [[ ! "$OPTION_ENCRYPT" = "true" ]] && [[ "$OPTION_RECIPIENTS" = "true" ]]; then
+       yell "ERROR:\\'-r\\' or \\'-R\\' set but \\'-e\\' is not set."; exit 1; fi; 
 } # Populate recPubKeysValid with argRecPubKeys; form encryption cmd string and filename suffix
 magicParseCompressionArg() {
     # Desc: Parses compression arguments specified by '-c' option
 } # Populate recPubKeysValid with argRecPubKeys; form encryption cmd string and filename suffix
 magicParseCompressionArg() {
     # Desc: Parses compression arguments specified by '-c' option
@@ -1051,7 +1086,8 @@ magicParseCompressionArg() {
 } # Form compression cmd string and filename suffix
 magicInitWorkingDir() {
     # Desc: Determine temporary working directory from defaults or user input
 } # Form compression cmd string and filename suffix
 magicInitWorkingDir() {
     # Desc: Determine temporary working directory from defaults or user input
-    # Input:  vars: OPTION_TEMPDIR, argTmpDirPriority, DIR_TMP_DEFAULT
+    # Usage: magicInitWorkignDir
+    # Input:  vars: OPTION_TEMPDIR, argTempDirPriority, DIR_TMP_DEFAULT
     # Input:  vars: SCRIPT_TIME_START
     # Output: vars: DIR_TMP
     # Depends: processArguments(), vbm(), yell()
     # Input:  vars: SCRIPT_TIME_START
     # Output: vars: DIR_TMP
     # Depends: processArguments(), vbm(), yell()
@@ -1063,7 +1099,7 @@ magicInitWorkingDir() {
        if [[ -d "$argTempDirPriority" ]]; then
            DIR_TMP_PARENT="$argTempDirPriority"; 
        else
        if [[ -d "$argTempDirPriority" ]]; then
            DIR_TMP_PARENT="$argTempDirPriority"; 
        else
-           yell "WARNING:Specified temporary working directory not valid:$OPTION_TMPDIR";
+           yell "WARNING:Specified temporary working directory not valid:$argTempDirPriority";
            exit 1; # Exit since user requires a specific temp dir and it is not available.
        fi;
     else
            exit 1; # Exit since user requires a specific temp dir and it is not available.
        fi;
     else
@@ -1081,31 +1117,69 @@ magicInitWorkingDir() {
     ## Set DIR_TMP using DIR_TMP_PARENT and nonce (SCRIPT_TIME_START)
     DIR_TMP="$DIR_TMP_PARENT"/"$SCRIPT_TIME_START""..bkgpslog" && vbm "DEBUG:Set DIR_TMP to:$DIR_TMP"; # Note: removed at end of main().    
 } # Sets working dir
     ## Set DIR_TMP using DIR_TMP_PARENT and nonce (SCRIPT_TIME_START)
     DIR_TMP="$DIR_TMP_PARENT"/"$SCRIPT_TIME_START""..bkgpslog" && vbm "DEBUG:Set DIR_TMP to:$DIR_TMP"; # Note: removed at end of main().    
 } # Sets working dir
+magicParseCustomTTL() {
+    # Desc: Set user-specified TTLs for buffer and script
+    # Input: vars: argCustomBufferTTL (integer), argCustomScriptTTL_TE (string)
+    # Input: vars: OPTION_CUSTOM_BUFFERTTL, OPTION_CUSTOM_SCRIPTTTL
+    # Input: vars: BUFFER_TTL (integer), SCRIPT_TTL_TE (string)
+    # Output: BUFFER_TTL (integer), SCRIPT_TTL_TE (string)
+    # Depends validateInput(), showUsage(), yell
+
+    # React to '-b, --buffer-ttl' option
+    if [[ "$OPTION_CUSTOM_BUFFERTTL" = "true" ]]; then
+       ## T: Check if argCustomBufferTTL is an integer
+       if validateInput "$argCustomBufferTTL" "integer"; then
+           ### T: argCustomBufferTTL is an integer
+           BUFFER_TTL="$argCustomBufferTTL";
+       else
+           ### F: argcustomBufferTTL is not an integer
+           yell "ERROR:Invalid integer argument for custom buffer time-to-live."; showUsage; exit 1;
+       fi;
+       ## F: do not change BUFFER_TTL
+    fi;
+    
+    # React to '-B, --script-ttl' option
+    if [[ "$OPTION_CUSTOM_SCRIPTTTL_TE" = "true" ]]; then
+       ## T: Check if argCustomScriptTTL is a time element (ex: "day", "hour")
+       if validateInput "$argCustomScriptTTL" "time_element"; then
+           ### T: argCustomScriptTTL is a time element
+           SCRIPT_TTL_TE="$argCustomScriptTTL";
+       else
+           ### F: argcustomScriptTTL is not a time element
+           yell "ERROR:Invalid time element argument for custom script time-to-live."; showUsage; exit 1;
+       fi;
+       ## F: do not change SCRIPT_TTL_TE
+    fi;    
+} # Sets custom script or buffer TTL if specified
+
 
 main() {
     # Process arguments
     processArguments "$@";
     ## Act upon arguments
     ### Determine working directory
 
 main() {
     # Process arguments
     processArguments "$@";
     ## Act upon arguments
     ### Determine working directory
-    magicInitWorkingDir;
+    magicInitWorkingDir; # Sets DIR_TMP from argTempDirPriority
     ### Set output encryption and compression option strings
     #### React to "-r" ("encryption recipients") option
     ### Set output encryption and compression option strings
     #### React to "-r" ("encryption recipients") option
-    magicParseRecipientArgs;
+    magicParseRecipientArgs; # Updates recPubKeysValid, CMD_ENCRYPT[_SUFFIX] from argRecPubKeys
     #### React to "-c" ("compression") option
     #### React to "-c" ("compression") option
-    magicParseCompressionArg;
+    magicParseCompressionArg; # Updates CMD_COMPRESS[_SUFFIX]
     #### React to "-R" ("recipient directory") option
     #### React to "-R" ("recipient directory") option
-    magicParseRecipientDir;
+    magicParseRecipientDir; # Updates recPubKeysValid
+    #### React to custom buffer and script TTL options ("-b", "-B")
+    magicParseCustomTTL; # Sets custom SCRIPT_TTL_TE and/or BUFFER_TTL if specified
 
     # Check that critical apps and dirs are available, display missing ones.
     if ! checkapp gpspipe tar && ! checkdir "$DIR_OUT" "DIR_TMP"; then
        yell "ERROR:Critical components missing.";
        displayMissing; yell "Exiting."; exit 1; fi
 
 
     # Check that critical apps and dirs are available, display missing ones.
     if ! checkapp gpspipe tar && ! checkdir "$DIR_OUT" "DIR_TMP"; then
        yell "ERROR:Critical components missing.";
        displayMissing; yell "Exiting."; exit 1; fi
 
-    # Set script lifespan
-    setScriptTTL "$SCRIPT_TTL"; # seconds until next new SCRIPT_TTL (ex: "day" or "hour")
+    # Set script lifespan (SCRIPT_TTL from SCRIPT_TTL_TE)
+    magicSetScriptTTL "$SCRIPT_TTL_TE";
+    ## Note: SCRIPT_TTL_TE is time element string (ex: "day") while SCRIPT_TTL is integer seconds
 
 
-    # File name substring: encoded bufferTTL
-    bufferTTL_STR="$(timeDuration $BUFFER_TTL)";
+    # File name substring (ISO-8601 duration from BUFFER_TTL)
+    bufferTTL_STR="$(timeDuration "$BUFFER_TTL")";
 
     # Init temp working dir
     try mkdir "$DIR_TMP" && vbm "DEBUG:Working dir creatd at:$DIR_TMP";
 
     # Init temp working dir
     try mkdir "$DIR_TMP" && vbm "DEBUG:Working dir creatd at:$DIR_TMP";
@@ -1125,7 +1199,8 @@ main() {
     CMD_CONV_KML="gpsbabel -i nmea -f - -o kml -F - " && vbm "STATUS:Set CMD_CONV_KML to:$CMD_CONV_KML"; # convert NMEA to KML
 
     # MAIN LOOP:Record gps data until script lifespan ends
     CMD_CONV_KML="gpsbabel -i nmea -f - -o kml -F - " && vbm "STATUS:Set CMD_CONV_KML to:$CMD_CONV_KML"; # convert NMEA to KML
 
     # MAIN LOOP:Record gps data until script lifespan ends
-    while [[ "$SECONDS" -lt "$scriptTTL" ]]; do
+    while [[ "$SECONDS" -lt "$SCRIPT_TTL" ]]; do
+       magicParseRecipientDir
        magicGatherWriteBuffer &
        sleep "$BUFFER_TTL";
     done    
        magicGatherWriteBuffer &
        sleep "$BUFFER_TTL";
     done