#!/usr/bin/env bash script_name="$(basename $(realpath $0))" script_dir="$(dirname $(realpath $0))" conf_dir="${script_dir}/conf" snippet_dir="${script_dir}/snippets" declare -a unsorted_website_arr declare -a website_arr declare -a selected_user_arr log_file="$(mktemp)" backup_date=$(date +%Y-%m-%d-%H%M) # ============= # --- Some functions # ============= usage() { [[ -n "$1" ]] && error "$1" [[ $terminal ]] && echo -e " \033[1mUsage:\033[m $(basename $0) [-s ] [-u ]... \033[1mDescription\033[m Runs 'occ files:scan --path=' for Nextcloud accounts. Without -u: scans ALL accounts found in the data directory (skipping internal directories like appdata_, files_, updater-). With -u: scans only the specified account(s). -u may be given multiple times for multiple accounts. \033[1mOptions\033[m -s The site of the nextcloud instance. If omitted, you will be asked interactively. -u Nextcloud account (uid) to scan. May be repeated for multiple accounts. If omitted, all accounts are scanned. \033[1mExamples:\033[m Scan all users on 'cloud-01.oopen.de': $(basename $0) -s cloud-01.oopen.de Scan only user 'alice' on 'cloud-01.oopen.de': $(basename $0) -s cloud-01.oopen.de -u alice Scan users 'alice' and 'bob': $(basename $0) -s cloud-01.oopen.de -u alice -u bob " clean_up 1 } clean_up() { # Perform program exit housekeeping rm -f $log_file blank_line exit $1 } is_number() { return $(test ! -z "${1##*[!0-9]*}" > /dev/null 2>&1); } echononl(){ if $terminal ; then echo X\\c > /tmp/shprompt$$ if [ `wc -c /tmp/shprompt$$ | awk '{print $1}'` -eq 1 ]; then echo -e -n "$*\\c" 1>&2 else echo -e -n "$*" 1>&2 fi rm /tmp/shprompt$$ fi } echo_done() { if $terminal ; then echo -e "\033[75G[ \033[32mdone\033[m ]" fi } echo_ok() { if $terminal ; then echo -e "\033[75G[ \033[32mok\033[m ]" fi } echo_warning() { if $terminal ; then echo -e "\033[75G[ \033[33m\033[1mwarn\033[m ]" fi } echo_failed(){ if $terminal ; then echo -e "\033[75G[ \033[1;31mfailed\033[m ]" fi } echo_skipped() { if $terminal ; then echo -e "\033[75G[ \033[37mskipped\033[m ]" fi } fatal (){ echo "" echo "" if $terminal ; then echo -e " [ \033[31m\033[1mFatal\033[m ]: \033[37m\033[1m$*\033[m" echo "" echo -e " \033[31m\033[1mScript will be interrupted..\033[m!" else echo " [ Fatal ]: $*" echo "" echo " Script was terminated...." fi clean_up 1 } error(){ echo "" if $terminal ; then echo -e " [ \033[31m\033[1mError\033[m ]: $*" else echo " [ Error ]: $*" fi echo "" } warn (){ if $terminal ; then echo "" echo -e " [ \033[33m\033[1mWarning\033[m ]: $*" echo "" fi } info (){ if $terminal ; then echo "" echo -e " [ \033[32m\033[1mInfo\033[m ]: $*" echo "" fi } # - Remove leading/trailing whitespaces # - trim() { local var="$*" var="${var#"${var%%[![:space:]]*}"}" var="${var%"${var##*[![:space:]]}"}" echo -n "$var" } containsElement () { local e for e in "${@:2}"; do [[ "$e" == "$1" ]] && return 0; done return 1 } blank_line() { if $terminal ; then echo "" fi } # - Running in a terminal? # - if [[ -t 1 ]] ; then terminal=true else terminal=false fi # ---------- # - Jobhandling # ---------- trap clean_up SIGHUP SIGINT SIGTERM # ------------- # - Read in Commandline arguments # ------------- while getopts hs:u: opt ; do case $opt in h) usage ;; s) WEBSITE=$OPTARG ;; u) selected_user_arr+=("$OPTARG") ;; \?) usage esac done if [[ -z "$WEBSITE" ]] ; then while IFS='' read -r -d '' _conf_file ; do source $_conf_file if [[ -n "$WEBSITE" ]] ; then unsorted_website_arr+=("${WEBSITE}:$_conf_file") fi WEBSITE="" done < <(find "${conf_dir}" -maxdepth 1 -type f -name "*.conf" -print0) IFS=$'\n' website_arr=($(sort <<<"${unsorted_website_arr[*]}")) source ${snippet_dir}/get-cloud-instance-to-update.sh else while IFS='' read -r -d '' _conf_file ; do if $(grep -E -q "WEBSITE=\"?${WEBSITE}\"?" ${_conf_file} 2> /dev/null) ; then conf_file="${_conf_file}" break fi done < <(find "${conf_dir}" -maxdepth 1 -type f -name "*.conf" -print0) fi IFS="${CUR_IFS:-$' \t\n'}" DEFAULT_SRC_BASE_DIR="/usr/local/src/nextcloud" DEFAULT_HTTP_USER="www-data" DEFAULT_HTTP_GROUP="www-data" DEFAULT_PHP_ENGINE='FPM' echo "" echononl " Include Configuration file '$(basename "${conf_file}")'.." if [[ ! -f $conf_file ]]; then echo_failed fatal "Missing configuration file '$conf_file'." else source $conf_file echo_ok fi echo "" DEFAULT_WEB_BASE_DIR="/var/www/${WEBSITE}" [[ -n "$WEB_BASE_DIR" ]] || WEB_BASE_DIR=$DEFAULT_WEB_BASE_DIR if [[ ! -d ${WEB_BASE_DIR} ]] ; then fatal "Web base directory '$WEB_BASE_DIR' not found!" fi DATA_DIR="$(realpath ${WEB_BASE_DIR}/data)" [[ -n "$PHP_ENGINE" ]] || PHP_ENGINE=$DEFAULT_PHP_ENGINE INSTALL_DIR="$(realpath ${WEB_BASE_DIR}/nextcloud)" CURRENT_VERSION="$(basename $INSTALL_DIR | cut -d"-" -f2)" # ============= # --- Some # ============= SYSTEMD_EXISTS=false systemd=$(which systemd) systemctl=$(which systemctl) if [[ -n "$systemd" ]] || [[ -n "$systemctl" ]] ; then SYSTEMD_EXISTS=true fi # ============= # --- Build the list of users to scan # --- If -u was given: validate each against DATA_DIR. # --- If not given: collect all real accounts from DATA_DIR. # ============= # Collect all real account directories in DATA_DIR. declare -a all_data_users=() for _d in "${DATA_DIR}"/*/ ; do _u="$(basename "$_d")" [[ "$_u" =~ ^appdata_ ]] && continue [[ "$_u" =~ ^files_ ]] && continue [[ "$_u" =~ ^updater- ]] && continue [[ ! -d "${DATA_DIR}/${_u}" ]] && continue all_data_users+=("$_u") done if [[ ${#all_data_users[@]} -eq 0 ]] ; then fatal "Keine Accounts im Datenverzeichnis '${DATA_DIR}' gefunden." fi # --- Interaktive Account-Auswahl --- # -u-Flags dienen als Vorauswahl (Default beim Enter-Drücken). # Im Non-Interactive-Modus wird die Vorauswahl (oder alle) direkt verwendet. if $terminal ; then blank_line echo -e "\033[37m\033[1mAccount-Auswahl\033[m" echo "" echo " Verfügbare Accounts:" for _i in "${!all_data_users[@]}" ; do printf " [%2d] %s\n" $((_i+1)) "${all_data_users[$_i]}" done echo "" # Determine default string for the prompt. if [[ ${#selected_user_arr[@]} -gt 0 ]] ; then _default_input="${selected_user_arr[*]}" echo -e " Vorauswahl via -u: \033[1;37m${_default_input}\033[m" else _default_input="all" fi echo "" echo " Eingabe: 'all' für alle, leerzeichen-getrennte Account-Namen," echo -n " oder Enter für Vorauswahl [${_default_input}]: " read -r _user_input _user_input="$(trim "$_user_input")" [[ -z "$_user_input" ]] && _user_input="$_default_input" else # Non-interactive: use -u pre-selection or all. if [[ ${#selected_user_arr[@]} -gt 0 ]] ; then _user_input="${selected_user_arr[*]}" else _user_input="all" fi fi # --- Auflösen und validieren --- selected_user_arr=() if [[ "$_user_input" == "all" ]] ; then selected_user_arr=("${all_data_users[@]}") else # Use read -ra to split on spaces regardless of current IFS state. read -ra _input_tokens <<< "$_user_input" for _u in "${_input_tokens[@]}" ; do if [[ ! -d "${DATA_DIR}/${_u}" ]] ; then warn "Account '${_u}' nicht im Datenverzeichnis gefunden — wird übersprungen." else selected_user_arr+=("$_u") fi done fi if [[ ${#selected_user_arr[@]} -eq 0 ]] ; then fatal "Keine gültigen Accounts ausgewählt." fi if [[ "$_user_input" == "all" ]] ; then _scan_mode="Alle Accounts (${#selected_user_arr[@]})" else _scan_mode="Ausgewählte Accounts (${#selected_user_arr[@]}): ${selected_user_arr[*]}" fi # ============= # --- Some checks # ============= DEFAULT_HTTP_USER="www-data" DEFAULT_HTTP_GROUP="www-data" NGINX_IS_ENABLED=false APACHE2_IS_ENABLED=false source ${snippet_dir}/get-webservice-environment.sh source ${snippet_dir}/get-php-major-version.sh source ${snippet_dir}/get-path-of-php-command.sh if [[ ! -x "$PHP_BIN" ]]; then fatal "No PHP binary found!" fi if $terminal ; then echo "" echo -e "\033[32m-----\033[m" echo -e "Trigger \033[1mocc files:scan\033[m for system \033[1m${WEB_BASE_DIR}\033[m" echo -e "\033[32m-----\033[m" echo "" echo -e "\033[1;32mStarting Script for \033[1;37m${WEBSITE}\033[m" echo "" echo -e " Cloud instance...............................: $WEBSITE" echo "" echo -e " Current version of nextcloud.................: $CURRENT_VERSION" echo "" echo -e " Web base directory...........................: $WEB_BASE_DIR" echo -e " Install directory............................: $INSTALL_DIR" echo -e " Data directory...............................: $DATA_DIR" echo "" echo -e " Webserver user...............................: $HTTP_USER" echo -e " Webserver group..............................: $HTTP_GROUP" echo "" echo -e " PHP command..................................: $PHP_BIN" echo "" echo -e " Zu scannende Accounts........................: ${_scan_mode}" echo "" echo -n " Type upper case 'YES' to continue executing with this parameters: " read OK if [[ "$OK" = "YES" ]] ; then echo "" echo "" echo -e "\033[1;32mGoing to run \033[1;37mfiles:scan\033[1;32m for selected accounts on \033[1;37m$WEBSITE \033[m" else fatal "Abort by user request - Answer as not 'YES'" fi fi # ----- # - Main part of the script # ----- if $terminal ; then echo "" echo "" echo -e "\033[37m\033[1mMain part of the script\033[m" echo "" fi echononl " Running 'occ maintenance:repair'.." su -c "$PHP_BIN ${INSTALL_DIR}/occ maintenance:repair" -s /bin/bash $HTTP_USER > $log_file 2>&1 if [[ $? -eq 0 ]]; then echo_ok else echo_failed error "$(cat "$log_file")" fi blank_line echononl " Running 'occ files:cleanup'.." su -c "$PHP_BIN ${INSTALL_DIR}/occ files:cleanup" -s /bin/bash $HTTP_USER > $log_file 2>&1 if [[ $? -eq 0 ]]; then echo_ok else echo_failed error "$(cat "$log_file")" fi blank_line for _user in "${selected_user_arr[@]}" ; do echononl " Running 'occ files:scan --path=${_user}'.." su -c "$PHP_BIN ${INSTALL_DIR}/occ files:scan --path=${_user}" -s /bin/bash $HTTP_USER > $log_file 2>&1 if [[ $? -eq 0 ]]; then echo_ok else echo_failed blank_line if $terminal ; then echo "" echo -e "\033[37m\033[1mcommandline was:\033[m" echo "su -c \"$PHP_BIN ${INSTALL_DIR}/occ files:scan --path=${_user}\" -s /bin/bash $HTTP_USER" error "$(cat "$log_file")" else error "Running filescan for user \"${_user}\" failed!" fi fi done blank_line clean_up 0