#!/usr/bin/env bash script_name="$(basename $(realpath $0))" working_dir="$(dirname $(realpath $0))" conf_file="${working_dir}/conf/mysql_credetials.conf" # - Lock directory exists, until the script ends. So # - we can check, if a previos instanze is already running. # - LOCK_DIR="/tmp/${script_name%%.*}.LOCK" log_file="${LOCK_DIR}/${script_name%%.*}.log" # ------------- # - Variable (default) settings # ------------- DEFAULT_MYSQL_CREDENTIAL_ARGS="--defaults-file=/usr/local/mysql/sys-maint.cnf" VERBOSE=false DEFAULT_to_addresses="argus@oopen.de" # ------------- # --- Some functions # ------------- usage() { [[ -n "$1" ]] && error "$1" [[ $terminal ]] && echo -e " \033[1mUsage:\033[m $(basename $0) [-h|-v] [DB-Name] \033[1mDescription\033[m Script checks (and reorganizes) all tables of all databases by executing MySQL command 'OPTIMIZE TABLE'. If a database is given at the command line, only tables of that database will be checked. If a check on a table fails, MySQL command 'REPAIR TABLE' will be executed on that table. \033[1mOptions\033[m -h Prints this help -v Verbose output if running on a terminal. \033[1mFiles\033[m $conf_file: Configuration file " clean_up 1 } clean_up() { # Perform program exit housekeeping if [[ ${1} -gt 0 ]] ; then [[ -f "${log_file}" ]] && cp ${log_file} "/var/log/" else rm -f "/var/log/$(basename "${log_file}")" fi rm -rf "$LOCK_DIR" blank_line exit $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 } fatal(){ echo "" if $terminal ; then if [[ -n "$*" ]] ; then echo -e " [ \033[31m\033[1mFatal\033[m ]: $*" echo "" echo -e " \033[31m\033[1mScript will be interrupted.\033[m\033[m" else echo -e " \033[31m\033[1mFatal error\033[m: \033[1mScript will be interrupted.\033[m" fi else if [[ -n "$*" ]] ; then echo " [ Fatal ]: $*" echo "" echo " Script was terminated.." else echo " Fatal error: Script was terminated.." fi fi echo "" 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 "" else echo " [ Warning ]: $*" fi } info (){ if $terminal ; then echo "" echo -e " [ \033[32m\033[1mInfo\033[m ]: $*" echo "" else echo " [ Info ]: $*" fi } echo_ok() { if $terminal ; then echo -e "\033[80G[ \033[32mok\033[m ]" fi } echo_failed(){ if $terminal ; then echo -e "\033[80G[ \033[1;31mfailed\033[m ]" fi } echo_skipped() { if $terminal ; then echo -e "\033[80G[ \033[37mskipped\033[m ]" fi } trim() { local var="$*" var="${var#"${var%%[![:space:]]*}"}" # remove leading whitespace characters var="${var%"${var##*[![:space:]]}"}" # remove trailing whitespace characters echo -n "$var" } blank_line() { if $terminal ; then echo "" fi } detect_mysql_version () { _MYSQLD_VERSION="$(mysqld -V 2>/dev/null)" if [[ -z "$_MYSQLD_VERSION" ]]; then fatal "No installed MySQL server or distribution found!" elif [[ "$_MYSQLD_VERSION" =~ MariaDB ]]; then MYSQL_CUR_DISTRIBUTION="MariaDB" elif [[ "$(basename "$(realpath "/usr/local/mysql")")" =~ percona- ]]; then MYSQL_CUR_DISTRIBUTION="Percona" elif [[ "$(basename "$(realpath "/usr/local/mysql")")" =~ mysql- ]]; then MYSQL_CUR_DISTRIBUTION="MySQL" fi MYSQL_VERSION="$(echo $_MYSQLD_VERSION | grep -o -E "[0-9]+\.[0-9]+\.[0-9]+(-[0-9]+)?" | head -n 1)" MYSQL_MAJOR_VERSION="$(echo $MYSQL_VERSION | cut -d '.' -f1)" MYSQL_MINOR_VERSION="$(echo $MYSQL_VERSION | cut -d '.' -f2)" MYSQL_PATCH_LEVEL="$(echo $MYSQL_VERSION | cut -d '.' -f3)" MYSQL_MAIN_VERSION="$(echo $MYSQL_VERSION | cut -d '.' -f1,2)" } # ------------- # - Is this script running on terminal ? # ------------- if [[ -t 1 ]] ; then terminal=true else terminal=false fi # ------------- # - Read Commandline Parameters # ------------- while getopts hv opt ; do case $opt in v) VERBOSE=true ;; h) usage ;; *) usage esac done shift $(expr $OPTIND - 1) [[ "$#" -gt 1 ]] && usage "Wrong number or order of arguments given!" # - Print help? # - if [[ "$(trim $*)" =~ " -h" ]] || [[ "$(trim $*)" =~ " --help" ]] ; then usage fi # ------------- # - Job is already running? # ------------- ## - If job already runs, stop execution.. ## - if mkdir "$LOCK_DIR" 2> /dev/null ; then ## - Remove lockdir when the script finishes, or when it receives a signal trap "clean_up 1" SIGHUP SIGINT SIGTERM else datum=`date +"%d.%m.%Y"` msg="[ Error ]: A previos instance of \"`basename $0`\" seems already be running.\n\tExiting now.." echo "" echo "[ Error ]: A previos instance of that script \"`basename $0`\" seems already be running." echo "" echo -e "\tExiting now.." echo "" for _to_address in $to_addresses ; do echo -e "To:${_to_address}\n${content_type}\nSubject:Error cronjob `basename $0` -- $datum\n${msg}\n" \ | sendmail -F "Error `hostname -f`" -f $from_address $_to_address done exit 1 fi # ------------- # - Load Settings from configuration file # ------------- if [[ -n "$1 " ]] ; then GIVEN_DATABASE="$1" else GIVEN_DATABASE="" fi blank_line echononl " Loading configuration settings from $(basename ${conf_file}).." if [[ -f "$conf_file" ]]; then source "$conf_file" > $log_file 2>&1 if [[ $? -eq 0 ]]; then echo_ok else echo_failed fatal "$(cat $log_file)" fi else echo_skipped if $terminal ;then warn "No Configuration File found. Loading defaults.." fi fi [[ -z "${to_addresses}" ]] && to_addresses="${DEFAULT_to_addresses}" # ------------- # - Some default values # ------------- mysql=`which mysql` if [ -z "$mysql" ]; then if [ -x "/usr/local/mysql/bin/mysql" ]; then mysql=/usr/local/mysql/bin/mysql else fatal "No binary 'mysql' found!" fi fi if [[ -z "$mysql_credential_args" ]]; then detect_mysql_version MAJOR_VERSION="$MYSQL_MAJOR_VERSION" MINOR_VERSION="$MYSQL_MINOR_VERSION" PATCH_LEVEL="$MYSQL_PATCH_LEVEL" if [[ "$MYSQL_CUR_DISTRIBUTION" = "MariaDB" ]] && ([[ $MAJOR_VERSION -gt 10 ]] \ || ( [[ $MAJOR_VERSION -eq 10 ]] && [[ $MINOR_VERSION -gt 3 ]] )) ; then if [[ -S "/tmp/mysql.sock" ]]; then mysql_credential_args="-u root -S /tmp/mysql.sock" elif [[ -S "/run/mysqld/mysqld.sock" ]]; then mysql_credential_args="-u root -S /run/mysqld/mysqld.sock" elif [[ -S "/var/run/mysqld/mysqld.sock" ]]; then mysql_credential_args="-u root -S /var/run/mysqld/mysqld.sock" else fatal "Parameter 'MYSQL_CREDENTIAL_ARGS' cannot be determined automated. Use configuration file "$conf_file" to set parameter manually." fi else if [[ -f "/usr/local/mysql/sys-maint.cnf" ]] ; then mysql_credential_args="--defaults-file=/usr/local/mysql/sys-maint.cnf" elif [[ -f "/etc/mysql/debian.cnf" ]] ; then mysql_credential_args="--defaults-file=/etc/mysql/debian.cnf" else fatal "Parameter 'MYSQL_CREDENTIAL_ARGS' cannot be determined automated. Use configuration file "$conf_file" to set parameter manually." fi fi fi if [[ ${#mysql_credential_args_arr[@]} -eq 0 ]]; then mysql_credential_args_arr[0]="default:$mysql_credential_args" fi # ========== # - Begin Main Script # ========== # ---------- # - Headline # ---------- if $terminal ; then echo "" echo -e "\033[1m----------\033[m" echo -e "\033[32m\033[1mRunning script \033[m\033[1m$script_name\033[32m .. \033[m" echo -e "\033[1m----------\033[m" fi declare -i length_table_name declare -i number_blank_signd declare -i index_i declare -i index_arr=0 while [[ $index_arr -lt ${#mysql_credential_args_arr[@]} ]] ; do mysql_command="$mysql" _all_success=true IFS=':' read -a _val_arr <<< "${mysql_credential_args_arr[$index_arr]}" mysql_version="${_val_arr[0]}" mysql_credential_args="${_val_arr[1]}" DATABASES="$(${mysql_command} $mysql_credential_args -N -s -e "show databases" 2> $log_file)" if [[ $? -ne 0 ]] ; then if [[ "$(cat $log_file)" =~ "unknown variable 'login-path" ]] ; then if [[ -x "/usr/local/mysql/bin/mysql" ]] ; then mysql_command="/usr/local/mysql/bin/mysql" DATABASES="$(${mysql_command} $mysql_credential_args -N -s -e "show databases" 2> $log_file)" if [[ $? -ne 0 ]] ; then error "$(cat $log_file)" continue fi fi fi fi found=false if [[ -n "$GIVEN_DATABASE" ]] ; then for db in $DATABASES ; do if [[ "$db" = "$GIVEN_DATABASE" ]]; then DATABASES="$GIVEN_DATABASE" if $terminal ; then echo "" echo -e "[ \033[37m\033[1mMySQL $mysql_version\033[m ]: optimize (and repair) tables of database '$GIVEN_DATABASE'." fi echo -e "[ MySQL $mysql_version ]: optimize (and repair) tables of database '$GIVEN_DATABASE'." > $log_file found=true fi done if ! $found ; then continue fi else if $terminal ; then echo "" echo -e "[ \033[37m\033[1mMySQL $mysql_version\033[m ]: optimize (and repair) tables of databases at host '$(hostname -f)'." fi echo -e "[ MySQL $mysql_version ]: optimize (and repair) tables of databases at host '$(hostname -f)'." > $log_file fi length_table_name=0 _service_extension=PRODUCTION.$$ for db in $DATABASES ; do [ "$db" = "information_schema" ] && continue [ "$db" = "performance_schema" ] && continue [ "$db" = "mysql" ] && continue if $terminal ;then echo "" echo -e " [$(date)] Optimize tables in database '${db}'.." fi echo "" >> $log_file echo -e " [$(date)] Optimize tables in database '${db}'.." >> $log_file _htdocs_nd_moved=false _htdocs_nd_symlinked=false _htdocs_nd_archiv_moved=false _htdocs_nd_archiv_symlinked=false if [[ "$db" = "nd" ]]; then if [[ -d "/var/www/html/projekte/nd/htdocs" ]] ; then if [[ -d "/var/www/html/projekte/nd/htdocs503" ]] ; then mv /var/www/html/projekte/nd/htdocs /var/www/html/projekte/nd/htdocs.$_service_extension > $log_file 2>&1 if [[ $? -ne 0 ]]; then error "Error while moving '/var/www/html/projekte/nd/htdocs'.\n$(cat $log_file)" else _htdocs_nd_moved=true fi if $_htdocs_nd_moved ; then ln -s htdocs503 /var/www/html/projekte/nd/htdocs > $log_file 2>&1 if [[ $? -ne 0 ]]; then error "Error while linking '/var/www/html/projekte/nd/htdocs' --> 'htdocs503'.\n$(cat $log_file)" else _htdocs_nd_symlinked=true fi fi /usr/local/apache2/bin/apachectl graceful > $log_file 2>&1 if [[ $? -ne 0 ]]; then error "Restarting Apache Webservice failed.\n$(cat $log_file)" fi else error "Directory '/var/www/html/projekte/nd/htdocs503' not found." fi else error "Directory '/var/www/html/projekte/nd/htdocs503' not found." fi elif [[ "$db" = "nd_archiv" ]]; then if [[ -d "/var/www/html/projekte/nd-archiv/htdocs" ]] ; then if [[ -d "/var/www/html/projekte/nd-archiv/htdocs503" ]] ; then mv /var/www/html/projekte/nd-archiv/htdocs /var/www/html/projekte/nd-archiv/htdocs.$_service_extension > $log_file 2>&1 if [[ $? -ne 0 ]]; then error "Error while moving '/var/www/html/projekte/nd-archiv/htdocs'.\n$(cat $log_file)" else _htdocs_nd_archiv_moved=true fi ln -s htdocs503 /var/www/html/projekte/nd-archiv/htdocs > $log_file 2>&1 if [[ $? -ne 0 ]]; then error "Error while linking '/var/www/html/projekte/nd-archiv/htdocs' --> 'htdocs503'.\n$(cat $log_file)" else _htdocs_nd_archiv_symlinked=true fi /usr/local/apache2/bin/apachectl graceful > $log_file 2>&1 if [[ $? -ne 0 ]]; then error "Restarting Apache Webservice failed.\n$(cat $log_file)" fi else error "Directory '/var/www/html/projekte/nd-archiv/htdocs503' not found." fi else error "Directory '/var/www/html/projekte/nd-archiv/htdocs503' not found." fi fi TABLES=`${mysql_command} $mysql_credential_args $db -N -s -e "show tables"` for table in $TABLES ; do # - Ommit InnoDB tables # - _engine="$(${mysql_command} $mysql_credential_args -N -s -e "SELECT ENGINE FROM information_schema.TABLES WHERE TABLE_SCHEMA = '$db' AND TABLE_NAME = '$table'")" if [[ "${_engine,,}" = 'innodb' ]] ; then if $VERBOSE ; then echo -e " [$(date)] Ommit table '$table' - storage engine is InnoDB" fi echo -e " [$(date)] Ommit table '$table' - storage engine is InnoDB" >> $log_file continue fi if $terminal ; then blank_signs="" if [[ $length_table_name -gt ${#table} ]]; then number_blank_sign=$(expr $length_table_name - ${#table}) index_i=0 while [[ $index_i -lt $number_blank_sign ]] ; do blank_signs="$blank_signs " (( index_i++ )) done echo -en "\033[1G" fi echo -en "\033[1G \033[32mOptimize table \033[1m$table\033[m$blank_signs\033[1G" fi echo -e " [$(date)] Optimize table '$table'" >> $log_file length_table_name=${#table} ${mysql_command} $mysql_credential_args $db -N -s -e "OPTIMIZE TABLE \`$table\`" > $log_file 2>&1 if [[ $? -ne 0 ]]; then warn "Optimizing table \"${table}\" of database \"$db\" failed. Trying to repair.." echo "" >> $log_file echo " [$(date)] warning: Optimizing table \"${table}\" of database \"$db\" failed." >> $log_file echo "" >> $log_file echo " [$(date)] Repair table '$table'" >> $log_file ${mysql_command} $mysql_credential_args $db -N -s -e "REPAIR TABLE \`$table\`" > $log_file 2>&1 if [[ $? -ne 0 ]]; then _all_success=false error "Repairing table '$table' failed.\n$(cat "$log_file")" error_messages_arr+=("MySQL $mysql_version: Error while repairing table '${table}' of database '$db'.") echo "" >> $log_file echo -e " [$(date)] error: Repairing table '$table' failed of database \"$db\" failed..\n$(cat "$log_file")" >> $log_file echo "" >> $log_file else echo -e " [$(date)] Optimize table '$table'" >> $log_file ${mysql_command} $mysql_credential_args $db -N -s -e "OPTIMIZE TABLE \`$table\`" > $log_file 2>&1 if [[ $? -ne 0 ]]; then error "Reoptimizing table \"${table}\" of database \"$db\" failed.\n$(cat "$log_file")" error_messages_arr+=("MySQL $mysql_version: Error while (re-)optimizing table '${table}' of database '$db'.") echo "" >> $log_file echo -e " [$(date)] error: Reoptimizing table \"${table}\" of database \"$db\" failed.\n$(cat "$log_file")" >> $log_file echo "" >> $log_file else info "Reoptimizing table \"${table}\" of database \"$db\" was successfully." fi fi if $terminal ; then echo "" fi fi done if [[ "$db" = "nd" ]]; then if $_htdocs_nd_symlinked ; then rm /var/www/html/projekte/nd/htdocs > $log_file 2>&1 if [[ $? -ne 0 ]]; then error "Error while removing symlink '/var/www/html/projekte/nd/htdocs'.\n$(cat $log_file)" fi fi if $_htdocs_nd_moved ; then mv /var/www/html/projekte/nd/htdocs.$_service_extension /var/www/html/projekte/nd/htdocs > $log_file 2>&1 if [[ $? -ne 0 ]]; then error "Error while moving back '/var/www/html/projekte/nd/htdocs'.\n$(cat $log_file)" fi fi /usr/local/apache2/bin/apachectl graceful > $log_file 2>&1 if [[ $? -ne 0 ]]; then error "Restarting Apache Webservice failed.\n$(cat $log_file)" fi elif [[ "$db" = "nd_archiv" ]]; then if $_htdocs_nd_archiv_symlinked ; then rm /var/www/html/projekte/nd-archiv/htdocs > $log_file 2>&1 if [[ $? -ne 0 ]]; then error "Error while removing symlink '/var/www/html/projekte/nd-archiv/htdocs'.\n$(cat $log_file)" fi fi if $_htdocs_nd_archiv_moved ; then mv /var/www/html/projekte/nd-archiv/htdocs.$_service_extension /var/www/html/projekte/nd-archiv/htdocs > $log_file 2>&1 if [[ $? -ne 0 ]]; then error "Error while moving back '/var/www/html/projekte/nd-archiv/htdocs'.\n$(cat $log_file)" fi fi /usr/local/apache2/bin/apachectl graceful > $log_file 2>&1 if [[ $? -ne 0 ]]; then error "Restarting Apache Webservice failed.\n$(cat $log_file)" fi fi if $terminal ;then echo -en "\033[1G [$(date)] End optimize tables in database '${db}'.." echo fi echo -e " [$(date)] End optimize tables in database '${db}'" >> $log_file done if $_all_success ; then info_messages_arr+=("MySQL $mysql_version: The optimization of the MySQL tables of all databases were successful.") fi if [[ -n "$GIVEN_DATABASE" ]] ; then if $terminal ; then echo "" echo -e "[ \033[37m\033[1mMySQL $mysql_version\033[m ]: Finished optimizing MySQL database '$GIVEN_DATABASE'." echo "" fi echo "" >> $log_file echo "[ MySQL $mysql_version ]: Finished optimizing MySQL database '$GIVEN_DATABASE'." >> $log_file echo "" >> $log_file else if $terminal ; then echo "" echo -e "[ \033[37m\033[1mMySQL $mysql_version\033[m ]: Finished optimizing MySQL databases at host $(hostname -f)." echo "" fi echo "" >> $log_file echo "[ MySQL $mysql_version ]: Finished optimizing MySQL databases at host $(hostname -f)." >> $log_file echo "" >> $log_file fi (( index_arr++ )) done if [[ ${#info_messages_arr[@]} -gt 0 ]]; then for msg in "${info_messages_arr[@]}" ; do if $terminal ; then info "$msg" fi echo "[ Info ]: $msg" >> $log_file done fi if [[ ${#error_messages_arr[@]} -gt 0 ]]; then for msg in "${error_messages_arr[@]}" ; do if $terminal ; then error "$msg" fi echo "[ Error ]: $msg" >> $log_file done fi if $terminal ; then info "See logfile '$log_file' for more details." fi clean_up 0