#!/bin/bash # Configuration. STORAGE_PREFIX="/storage/md0" LOCATE_DB="/tmp/mirrors.db" DEF_RESULTS=500 MAX_RESULTS=3000 MAX_CONCURRENT=10 IGNORE_REGEXES=('^/\.sandbox.*' '^/\.lftp.*' '^/dead\.letter' '.*\.rsync-tmp.*') # Extglob is required. shopt -s extglob # Output an error in processing. die() { # $1 = The error code to output. # 0 = Success. # 1 = Temporary error (eg, max concurrent). # 2 = Perminant error. # $2 = The error message to output (if any). printf "%s: %s\\n" "Status" "${1:-2}" [[ -n "$2" ]] && printf "%s: %s\\n" "Error" "$2" exit 1 } # Initial headers. printf "%s: %s\\n" "Content-type" "text/plain" printf "%s: %s\\n" "Cache-Control" "no-cache, no-store, must-revalidate" printf "%s: %s\\n" "Pragma" "no-cache" printf "%s: %s\\n\\n" "Expires" "1" # Limit the number of concurrent searches to avoid DoS. if (( $(lsof -t "$LOCATE_DB" | wc -l) > (MAX_CONCURRENT - 1) )); then die "1" "Too many concurrent searches" else # Lock the DB for the lsof count above. # shellcheck disable=SC2015 exec 9<"$LOCATE_DB" && flock -s -E 10 -w 2 9 || die "1" "Too many concurrent searches" # shellcheck disable=SC2154 while read -r -d '&' QUERY; do # If the read returned an empty string, skip. [[ -z "$QUERY" ]] && continue # Extract the key and value to temporary variables. KEY="${QUERY%%=*}" VALUE="${QUERY#*=}" # Check the key is valid as a variable name. [[ ! "$KEY" =~ ^[[:digit:]_]*[[:alnum:]_]*$ ]] && die "2" "Invalid query - don't try to be clever :)" # Remove spaces from beginning and end of value. : "${VALUE/#+(+)}" : "${_/%+(+)}" # Squash multiple spaces in value. : "${_//+(+)/+}" # Convert values from %-encoded form. : "${_//%/\\x}" # Define the variable from the key name. declare "QS_$KEY"="$(printf "%b" "${_//+/ }")" done <<<"${QUERY_STRING,,}&" # The & at the end is required. # Adjust 'QS_q' for the locate command by wrapping search elements in *. : "*${QS_q// /* *}*" QS_q="${_//\/*/*}" # Adjust 'QS_l' to not go over maximum limit of results or use default if not supplied. [[ -z "$QS_l" ]] && QS_l="$DEF_RESULTS" (( QS_l > MAX_RESULTS )) && QS_l="$MAX_RESULTS" # Give success status as checks have passed. printf "%s: %s\\n" "Status" "0" I=1 # shellcheck disable=SC2154 while read -r ITEM; do # Only show specified number of items. (( I == (QS_l + 1) )) && break # Remove paths we don't want the user to see. while read -r -d " " REGEX; do [[ "${ITEM/$STORAGE_PREFIX}" =~ $REGEX ]] && continue 2 done <<<"${IGNORE_REGEXES[@]}" # List the item. printf "%s\\n" "${ITEM/$STORAGE_PREFIX}" (( I++ )) done < <(locate -A -d "$LOCATE_DB" -i -l "$(( QS_l * 2 ))" "$STORAGE_PREFIX/${QS_p##/}" "$QS_q" | sort) fi