#!/bin/bash -ue
#
# Scripts to run by PXC systemd service
# 

parse_cnf()
{
    local var=$1
    local def=$2
    shift
    local groups=$*
    reval=$(my_print_defaults $groups | awk -F= '{if ($1 ~ /_/) { gsub(/_/,"-",$1); print $1"="$2 } else { print $0 }}' | grep -- "--$var=" | cut -d= -f2- | tail -n 1)
    if [[ -z ${reval:-} ]];then 
        val=$(tr ' ' '\n' <<< "$env_args" | awk -F= '{if ($1 ~ /_/) { gsub(/_/,"-",$1); print $1"="$2 } else { print $0 }}' | grep -- "--$var=" | cut -d= -f2- | tail -n 1)
        if [[ -n ${val:-} ]];then
            reval=$val
        else 
            reval=$def
        fi
    fi
    echo $reval
}

install_db () {    
    # Note: something different than datadir=/var/lib/mysql requires SELinux policy changes (in enforcing mode)
    log=$(parse_cnf log-error server mysqld mysqld_safe)


    # Check that pid-file directory exists and has valid permissions
    pidfiledir=$(dirname $pid_path)
    if [ ! -z $pidfiledir ]; then
        [ -d "$pidfiledir" ] || install -d -m 0755 -omysql -gmysql "$pidfiledir" || exit 1
    fi
    
    # Restore log, dir, perms and SELinux contexts
    [ -d "$datadir" ] || install -d -m 0755 -omysql -gmysql "$datadir" || exit 1
    if [ -x /usr/sbin/restorecon ]; then
        /usr/sbin/restorecon "$datadir"
        if [ ! -z $pidfiledir ]; then
            /usr/sbin/restorecon "$pidfiledir"
        fi
    fi

    # If log file is not specified it's put into datadir by default
    if [ ! -z $log ]; then
        if [ ! -e "$log" -a ! -h "$log" -a x$(dirname "$log") = "x/var/log" ]; then
            case $(basename "$log") in
                mysql*.log) install /dev/null -m0640 -omysql -gmysql "$log" ;;
                *) ;;
            esac
        fi
        if [ -x /usr/sbin/restorecon ]; then
            [ -e "$log" ] && /usr/sbin/restorecon $log
        fi
    fi

    # If special mysql dir is in place, skip db install 
    [ -d "$datadir/mysql" ] && exit 0
    if [ -f "$datadir/sst_in_progress" ]; then
        rm -rf $datadir/*
    fi

    # Create initial db
    /usr/sbin/mysqld --initialize --datadir="$datadir" --user=mysql
    exit 0
}

log_success_msg() {
    echo " SUCCESS! $@"
}

log_failure_msg() {
    echo " ERROR! $@"
}

log_info_msg() {
    echo " INFO: $@"
}


log_warning_msg() {
    echo " WARNING: $@"
}

wait_for_pid () {
  local verb=$1          
  local pid_file_path="$2" 
  local mpid=0

  if [[ $verb = 'removed' ]];then 
    if [[ ! -s $pid_file_path ]];then 
        log_success_msg
        return 0
    fi
    mpid=$(cat $pid_file_path)
  fi

  local sst_progress_file=$datadir/sst_in_progress
  i=0

  while [[ $i -lt $service_startup_timeout ]]; do

    set +e
    case "$verb" in
      'created')
            if [[ -s "$pid_file_path" ]]; then 
                mpid=$(cat $pid_file_path)
                if kill -0 $mpid;then
                    i='' 
                    break
                fi
            fi
        ;;
      'removed')
            if [[ ! -s "$pid_file_path" ]] && ! kill -0 $mpid;then 
                i=''
                break
            fi
        ;;
      *)
        echo "wait_for_pid () usage: wait_for_pid created|removed pid pid_file_path"
        exit 1
        ;;
    esac
    set -e


    if [[ $verb = 'created' ]];then 
        if ([[ -e $sst_progress_file ]] || grep -q -- '--wsrep-new-cluster' <<< "$env_args" ) \
            && [[ $startup_sleep -ne 10 ]];then
            echo "State transfer in progress, setting sleep higher"
            startup_sleep=10
        fi
    fi

    i=$(( i+1 ))
    sleep $startup_sleep

    if [[ $verb = 'created' ]];then 
        if ! kill -0 $manager;then 
            log_failure_msg "mysqld_safe with PID $manager has already exited: FAILURE"
            exit 1
        fi
    fi

  done

  if [[ -z "$i" ]]; then
    log_success_msg
    return 0
  elif [[ -e $sst_progress_file ]]; then 
    log_info_msg
    return 2
  else
    log_failure_msg
    return 1
  fi
}

pinger () {
    # Wait for ping to answer to signal startup completed,
    # might take a while in case of e.g. crash recovery
    # MySQL systemd service will timeout script if no answer
    datadir=$(parse_cnf datadir server mysqld)
    if [[ -z ${datadir:-} ]]; then
        datadir="/var/lib/mysql"
    fi
    socket=$(parse_cnf socket server mysqld)
    case $socket in
        /*) adminsocket="$socket" ;;
        "") adminsocket="$datadir/mysql.sock" ;;
        *) adminsocket="$datadir/$socket" ;;
    esac

    while /bin/true ; do
        sleep 1
        mysqladmin --no-defaults --socket="$adminsocket" --user=UNKNOWN_MYSQL_USER ping >/dev/null 2>&1 && break
    done
    exit 0
}


action=$1
manager=${2:-0}
service_startup_timeout=900
startup_sleep=1
basedir=/usr
bindir="$basedir/bin"
sbindir="$basedir/sbin"
libexecdir="$basedir/libexec"

env_args="${EXTRA_ARGS:-}"


PATH="/sbin:/usr/sbin:/bin:/usr/bin:$bindir:$sbindir"
export PATH

pid_path=$(parse_cnf pid-file "" server mysqld mysqld_safe)
datadir=$(parse_cnf datadir "" server mysqld mysqld_safe)
service_startup_timeout=$(parse_cnf service-startup-timeout $service_startup_timeout mysqld_safe)

if [[ -z ${datadir:-} ]];then
    datadir="/var/lib/mysql"
    if [[ ! -d $datadir ]];then 
        echo "FATAL: datadir not found in cnf or is /var/lib/mysql"
        exit 2
    fi
fi

grastate_loc="${datadir}/grastate.dat"

if [[ -z "${pid_path:-}" ]];then
  hostname=$(hostname)
  pid_path=$datadir/${hostname%.*}.pid
else
  case "$pid_path" in
    /* ) ;;
    * )  pid_path="$datadir/$pid_path" ;;
  esac
fi

if [[ $action == 'reload' || $action == 'stop' || $action == 'stop-post' ]];then
    if [[ ! -s $pid_path ]];then 
        log_warning_msg "mysql pid file $pid_path empty or not readable"
        [[ $action == 'stop' ]] && log_failure_msg "mysql already dead"
        if [[ $action == 'stop-post' ]];then 
            log_warning_msg "mysql may be already dead"
            exit
        fi
        exit 2
    fi
    mysql_pid=$(cat $pid_path)
    if [[ $action == 'stop-post' && -n ${mysql_pid:-} ]]  && ! kill -0 $mysql_pid;then 
        log_warning_msg "mysql already dead"
        log_failure_msg "Stale PID file: $pid_path"
        exit 3
    fi
fi

if [[ $action == 'start-post' ]];then 
    if ! kill -0 $manager;then 
        log_failure_msg "mysqld_safe with PID $manager has already exited"
        exit 1
    fi
fi

check_running()
{
    if [[ -s $pid_path ]];then
	    mysql_pid=$(cat $pid_path)
	    if [[ -n ${mysql_pid:-} ]]  && kill -0 $mysql_pid 2>/dev/null;then
                mysql_existed_pid=$(ps wwaux | grep mysql | grep "wsrep-new-cluster" | awk '{print $2}')
                if [[ ${mysql_pid} == ${mysql_existed_pid} ]]; then
                    log_warning_msg "PXC is in bootstrap mode.  To switch to normal operation, first stop the mysql@bootstrap.service then start the mysql service"
                else
                    log_warning_msg "Another instance of mysqld running on $mysql_pid, exiting.."
                fi
                exit 1

	    fi
    fi
}

# Run mysqld with --wsrep-recover and parse recovered position from log.
# Position will be stored in wsrep_start_position_opt global.
wsrep_start_position_opt=""
wsrep_recover_position() {
  local mysqld_cmd="/usr/sbin/mysqld"
  local ret=0
  local uuid=""
  local seqno=0
  local wsrep_logfile=$(mktemp $datadir/wsrep_recovery.XXXXXX)
  local err_log=$(parse_cnf log-error server mysqld)
  if [ -f $grastate_loc ]; then
    uuid=$(grep 'uuid:' $grastate_loc | cut -d: -f2 | tr -d ' ')
    seqno=$(grep 'seqno:' $grastate_loc | cut -d: -f2 | tr -d ' ')

  # If sequence number is not equal to -1, wsrep-recover co-ordinates aren't used.
  # So, directly pass whatever is obtained from grastate.dat
    if [ ! -z $seqno ] && [ $seqno -ne -1 ]; then
      log_info_msg "Skipping wsrep-recover for $uuid:$seqno pair" >> $wsrep_logfile
      log_info_msg "Assigning $uuid:$seqno to wsrep_start_position" >> $wsrep_logfile

      wsrep_start_position_opt="--wsrep_start_position=$uuid:$seqno"
      if [ -f $err_log ]; then
        cat $wsrep_logfile >> $err_log
      fi
      rm $wsrep_logfile

      echo ${wsrep_start_position_opt}
      return $ret
    fi
  fi

  local euid=$(id -u)
  local wsrep_pidfile="$datadir/"`hostname`"-recover.pid"
  local wsrep_verbose_logfile=$(mktemp $datadir/wsrep_recovery_verbose.XXXXXX)
  local wr_options="--log_error='$wsrep_verbose_logfile' --pid-file='$wsrep_pidfile'"

  [ "$euid" = "0" ] && chown 'mysql' $wsrep_logfile $wsrep_verbose_logfile
  chmod 600 $wsrep_logfile $wsrep_verbose_logfile

  log_info_msg "WSREP: Running position recovery with $wr_options" >> $wsrep_logfile

  eval "$mysqld_cmd --user=mysql --log-error-verbosity=3 --wsrep_recover $wr_options"
  local rp="$(grep '\[WSREP\] Recovered position:' $wsrep_verbose_logfile)"
  if [ -z "$rp" ]; then
    local skipped="$(grep WSREP $wsrep_verbose_logfile | grep 'skipping position recovery')"
    if [ -z "$skipped" ]; then
      log_failure_msg "WSREP: Failed to recover position: " >> $wsrep_logfile
      cat $wsrep_verbose_logfile >> $wsrep_logfile
      ret=2
    else
      log_info_msg "WSREP: Position recovery skipped" >> $wsrep_logfile
    fi
  else
    local start_pos="$(echo $rp | sed 's/.*WSREP\]\ Recovered\ position://' \
        | sed 's/^[ \t]*//')"
    log_info_msg "WSREP: Recovered position $start_pos" >> $wsrep_logfile
    wsrep_start_position_opt="--wsrep_start_position=$start_pos"
  fi

  if [ -f $err_log ]; then
      echo "Log of wsrep recovery (--wsrep-recover):" >> $err_log
      cat $wsrep_logfile >> $err_log
  fi
  rm $wsrep_logfile $wsrep_verbose_logfile

  echo ${wsrep_start_position_opt}
  return $ret
}


ret=0

case $action in
    "galera-recovery") wsrep_recover_position ;;
    "start-pre") check_running; install_db ;;
    "start-post") 
      wait_for_pid created  "$pid_path"; ret=$?
      if [[ $ret -eq 1 ]];then 
          log_failure_msg "MySQL (Percona XtraDB Cluster) server startup failed!"
      elif [[ $ret -eq 2 ]];then
          log_info_msg "MySQL (Percona XtraDB Cluster) server startup failed! State transfer still in progress"
      fi
      exit $ret
    ;;
    "stop-post") 
      wait_for_pid removed  "$pid_path"; ret=$?
      if [[ $ret -ne 0 ]];then 
        log_failure_msg "MySQL (Percona XtraDB Cluster) server stop failed!"
      fi
      exit $ret
    ;;
    'reload')
        kill -HUP $mysql_pid || ret=$?
        if [[ $ret -ne 0 ]]; then 
            log_failure_msg "Failed to reload the server with PID: $mysql_pid with $ret status"
            exit $ret
        else 
            log_success_msg "Reloaded the server"
        fi
        ;;
    'stop')
        kill -TERM $mysql_pid || ret=$?
        if [[ $ret -ne 0 ]]; then 
            log_failure_msg "Failed to stop the server with PID: $mysql_pid with $ret status"
            exit $ret
        else 
            log_success_msg "Stopping Percona XtraDB Cluster......"
        fi
        ;;
esac

exit 0

