From b821d4dfa7d8e61dd9d1eb185fe55b6738ca7ee8 Mon Sep 17 00:00:00 2001 From: Tristan Ancelet Date: Sat, 1 Jun 2024 16:57:13 -0500 Subject: [PATCH] initial commit --- config.sh | 111 +++++++++++++++++++++++++++++++++++++++++++++++++++++ import.sh | 50 ++++++++++++++++++++++++ logging.sh | 99 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 260 insertions(+) create mode 100644 config.sh create mode 100644 import.sh create mode 100644 logging.sh diff --git a/config.sh b/config.sh new file mode 100644 index 0000000..f992978 --- /dev/null +++ b/config.sh @@ -0,0 +1,111 @@ +# A variable to contain the configs provided manually or via function calls +declare -A CONFIG + +########################## +# Function: load_section +# Usage: This will return the section of a aggragate config file +# +# EX: +# [TEST_BEGIN] +# test=1 +# test2=34 +# [TEST_END] +# +# load_section /path/to/config TEST [OPT_VAR] +function load_section () { + local SECTION_NAME=${1:?"$FUNCNAME: No section name was provided"} + local FILE=${2:?"$FUNCNAME: No file was provided"} + local BUFFER_ARRAY BUFFER_STRING + local SECTION_BEGIN="\[${SECTION_NAME}_BEGIN\]" + local SECTION_END="\[${SECTION_NAME}_END\]" + + if [[ $3 != "" ]]; then + local OPT_VAR_PROVIDED=1 + local -n OPT_OUTPUT_VAR=$3 + fi + + if [[ $FILE == '-' ]]; then + FILE=/dev/stdin + elif [[ ! -f $FILE ]]; then + echo "$FUNCNAME: $FILE does not exist" + exit + fi + + ## Loading contents, since it will clear /dev/stdin on a read (in the case that it is being used to provide the content) + local FILE_CONTENTS=$(<$FILE) + + if ! grep -q "$SECTION_BEGIN" <<< "$FILE_CONTENTS" || ! grep -q "$SECTION_END" <<< "$FILE_CONTENTS"; then + echo "$FUNCNAME: Either the begin or end $SECTION_NAME section was missing from the file ($FILE) provided" + exit + fi + + mapfile -t BUFFER_ARRAY <<< "$FILE_CONTENTS" + + local CAN_READ=0 + for ((i=0; i < ${#BUFFER_ARRAY[@]}; i++)); do + if [[ ${BUFFER_ARRAY[$i]} =~ ^$SECTION_BEGIN ]]; then + CAN_READ=1 + continue + elif [[ ${BUFFER_ARRAY[$i]} =~ ^$SECTION_END ]]; then + break + elif [[ $CAN_READ -eq 1 ]]; then + BUFFER_STRING+="\n${BUFFER_ARRAY[$i]}" + fi + done + + if [[ $OPT_VAR_PROVIDED -eq 1 ]]; then + OPT_OUTPUT_VAR=$( echo -e "$BUFFER_STRING" ) + else + echo -e "$BUFFER_STRING" + fi +} + + +############################## +# Function: load_config_ini +# Usage: You would call this function to load an ini formated config file into the global CONFIG associative array +# or into a Associative Array variable you provide as the second argument +function load_config_ini () { + local FILE=${1:?"$FUNCNAME: No file was provided"} + if [[ $2 != "" ]]; then + local -n CONFIG_VAR=${2} + + else + local -n CONFIG_VAR="CONFIG" + fi + + if ! declare -p ${!CONFIG_VAR} 2>/dev/null | grep -q 'declare -A'; then + echo "The output variable (${!CONFIG_VAR}) provided was either not an Associative array or was not declared." + echo "Please declare your variable correctly as 'declare -A ${!CONFIG_VAR}'" + exit + fi + + if [[ ! -f $FILE ]]; then + echo "$FUNCNAME: $FILE does not exist" + exit + fi + + local SECTION_REGEX='\[(\S+)\]' + local KEY_VAL_REGEX='[[:space:]]*(\S+)[[:space:]]*=[[:space:]]*(.+)' + local -a BUFFER_ARRAY + local SECTION="" KEY="" VAL="" + + mapfile -t BUFFER_ARRAY <$FILE + + for ((i=0; i < ${#BUFFER_ARRAY[@]}; i++)); do + LINE=${BUFFER_ARRAY[$i]} + if [[ $LINE =~ ^$SECTION_REGEX ]]; then + SECTION=${BASH_REMATCH[1],,} + + elif [[ $LINE =~ ^$KEY_VAL_REGEX ]]; then + KEY=${BASH_REMATCH[1],,} + VAL=${BASH_REMATCH[2]} + if [[ $SECTION != "" ]]; then + CONFIG_VAR["$SECTION.$KEY"]=$VAL + else + CONFIG_VAR["$KEY"]=$VAL + fi + fi + done +} + diff --git a/import.sh b/import.sh new file mode 100644 index 0000000..e5ec60f --- /dev/null +++ b/import.sh @@ -0,0 +1,50 @@ +declare LIB_PATH=/opt/bash-lib/ + +######################## +# Function: dep_exists +# Usage: You provide it a utility name and it checks to see if it can find an +# executable of the same name in path +function dep_exists () { + local DEP_UTIL=${1:?"$FUNCNAME: No dep utility was provided for search"} + for path in ${PATH//:/ }; do + if [[ -x $path/$DEP_UTIL ]]; then + return 0 + fi + done + + # The above is just more bash friendly since it can be error handled better + ## OR + # find ${PATH//:/ } -name $DEP_UTIL -executable | grep $DEP_UTIL + # return $? + + echo "$FUNCNAME: $DEP_UTIL is not installed, or is not in path" + return 1 +} + +#################### +# Function: import +# Usage: You provide the script/lib name that you want to import from the LIB_PATH +function import () { + local LIB_NAME=${1:?"$FUNCNAME: No lib provided for import"} + ## Fixing lib_name in case user/dev provides a non *.sh ending script (as all should have extension) + if [[ ${LIB_NAME##*.} != 'sh' ]]; then + LIB_NAME=${LIB_NAME}.sh + fi + + # Creating REGEX pattern to match if lib has already been sourced + local SOURCED_LIBS_REGEX="($( IFS='|'; echo "${BASH_SOURCE[*]}" ))" + if [[ $LIB_NAME =~ ^$SOURCED_LIBS_REGEX$ ]]; then + echo "$FUNCNAME: This lib ($LIB_NAME) has already been sourced" + return 0 + fi + + ## Getting FULL PATH for convinence + FULLPATH=$LIB_PATH/$LIB_NAME + + if [[ -f $FULLPATH ]]; then + . $FULLPATH + else + echo "$FUNCNAME: $LIB_NAME ($FULLPATH) does not exist or is not accessible by you." + exit + fi +} diff --git a/logging.sh b/logging.sh new file mode 100644 index 0000000..fdf73ef --- /dev/null +++ b/logging.sh @@ -0,0 +1,99 @@ +########################## +# An associative array containing varnames that the user want's to keep track of in log output (specific to each function log is being called from) +# +# [$FUNCNAME]="string of varnames" +# +# It will keep track of vars seperately of each function that log will be used with +declare -A LOG_WATCH_VARS + + +###################### +# Function: log_import +# Usage: This is used to import logging functions in an embedded shell or over ssh +log_import () { + declare -f log_watch_vars log_unwatch_vars log + declare -p LOG_WATCH_VARS +} + +##################### +# Function: log_watch_vars +# Usage: Provide a list of variable names to add to the watchlist for the calling function +log_watch_vars () { + local -a REQUESTED_WATCH_VARS=( $* ) + local CALLING_FUNCTION=${FUNCNAME[1]} + local FUNC_VARS=${LOG_WATCH_VARS[$CALLING_FUNCTION]} + local MATCH_REGEX=${FUNC_VARS//[[:space:]]/|} + + for VARNAME in ${REQUESTED_WATCH_VARS[@]}; do + if [[ ! "$VARNAME" =~ ^$MATCH_REGEX$ ]]; then + FUNC_VARS+=" $VARNAME" + fi + done + + LOG_WATCH_VARS[$CALLING_FUNCTION]=$FUNC_VARS +} + +##################### +# Function: log_unwatch_vars +# Usage: Provide a list of variable names to remove from the watchlist +log_unwatch_vars () { + ## Serialize the variable names provided by user into REGEX filter "(var1|var2|var3|var4|var_n)" + local MATCH_REGEX="(${@// /|})" + local CALLING_FUNCTION=${FUNCNAME[1]} + local FUNC_VARS=${LOG_WATCH_VARS[$CALLING_FUNCTION]} + local -a TEMP_ARRAY + + for VARNAME in $FUNC_VARS; do + if [[ ! "$VARNAME" =~ ^$MATCH_REGEX$ ]]; then + TEMP_ARRAY+=( $VARNAME ) + fi + done + LOG_WATCH_VARS[$CALLING_FUNCTION]="${TEMP_ARRAY[@]}" +} + +########### +# Function: log +# Usage: Use to print out debug statements for the developer (or user) to display a log output +# including variable values & names +log () { + local MESSAGE=${1:?"$FUNCNAME: No message provided"} + local CALLING_FUNCTION=${FUNCNAME[1]} + local FUNC_VARS=( ${LOG_WATCH_VARS[$CALLING_FUNCTION]} ) + local LEVEL=${2:-0} + local DATE=$(date) + local VAR_WATCH_STRING="" OUTPUT_MESSAGE="" + + if [[ ${CONFIG[debug]} -eq 1 ]]; then + case ${LEVEL,,} in + 0 | i | info) LEVEL="INFO";; + 1 | w | warn) LEVEL="WARN";; + 2 | c | crit) LEVEL="CRIT";; + 3 | f | fail) LEVEL="FAIL";; + *) LEVEL="UNDEF";; + esac + + local VARNAME VALUE + if [[ ${#FUNC_VARS[@]} -gt 0 ]]; then + for VARNAME in ${FUNC_VARS[@]}; do + local -n VARVALUE=$VARNAME + if [[ $VARVALUE == "" ]]; then + VALUE='N/A' + else + VALUE=$VARVALUE + fi + if [[ $VAR_WATCH_STRING == "" ]]; then + VAR_WATCH_STRING+="$VARNAME=$VALUE " + else + VAR_WATCH_STRING+=": $VARNAME=$VALUE " + fi + done + + OUTPUT_MESSAGE="$DATE : $HOSTNAME : $LEVEL : $VAR_WATCH_STRING : $MESSAGE" + else + OUTPUT_MESSAGE="$DATE : $HOSTNAME : $LEVEL : $MESSAGE" + fi + + echo -e "$OUTPUT_MESSAGE" + fi +} >&2 +