#!/bin/sh

###############################################
# CONFIGURATION
###############################################

# Set default home for start-stop-status script
if [ -z "$HOME" ]
then
    export HOME="/root"
fi

INSTALL_DIR="/opt/oxibackup"

if ! . "$INSTALL_DIR/etc/oxibackupd/service.conf" >/dev/null 2>&1
then
    >&2 echo "Could not load service configuration file"
    exit 1
fi

# Paths from previous versions
OLD_LOCK_DIR="/tmp/oxibackupd"
OLD_PID_FILE="${OLD_LOCK_DIR}/PID"

# Latest lock attempt log file
LATEST_LOCK_LOG_FILE="${LOG_DIR}/latest_lock_attempt.log"

# Exit codes
# See get_exit_code and get_exit_text for the list of codes and texts matching given ENO* exit
ENO_SUCCESS=0;
ENO_GENERAL=1;
ENO_LOCKFAIL=2;
ENO_RECVSIG=3;
ENO_ALRDY_RUNNING=4;
ENO_NOT_REGISTRED=5;
ENO_NO_SSH_KEY=6;

###############################################
# FUNCTIONS
###############################################

# Get current timestamp using RFC format
rfc_timestamp () {
    date --rfc-3339=seconds 2>/dev/null
}

# Get current timestamp using custom format
file_name_timestamp () {
    date +%F_%T 2>/dev/null
}

# Create files and directories, set permissions and rotate files if size exceeds limit
rotate_log () {
    # Log diectory
    mkdir -p "$LOG_DIR"
    chmod 700 "$LOG_DIR"

    # Reset last lock attempt log file
    rm -f "$LATEST_LOCK_LOG_FILE" 2>/dev/null
    touch "$LATEST_LOCK_LOG_FILE"
    chmod 600 "$LATEST_LOCK_LOG_FILE"

    # Main log file
    if [ ! -f "$LOG_FILE" ]
    then
        touch "$LOG_FILE"
    fi
    chmod 600 "$LOG_FILE"

    # Create a new file if it exceeds size limit
    LOG_FILE_SIZE=$(du -b "$LOG_FILE" 2>/dev/null | tr -s '\t' ' ' | cut -d' ' -f1)
    if [ ! -z "$LOG_FILE_SIZE" ] && [ "$LOG_FILE_SIZE" -gt "$LOG_MAX_SIZE" ]
    then
        LOG_ROTATE_FILE="${LOG_DIR}/${LOG_FILE_NAME}_"`file_name_timestamp`".log"
        if mv "$LOG_FILE" "$LOG_ROTATE_FILE" 2>/dev/null
        then
            if command -v tar >/dev/null 2>&1
            then
                tar -czf "${LOG_ROTATE_FILE}.gz" "${LOG_ROTATE_FILE}" 2>/dev/null
                chmod 600 "${LOG_ROTATE_FILE}.gz"
                rm -f "${LOG_ROTATE_FILE}" 2>/dev/null
            fi
        fi
        touch "$LOG_FILE"
        chmod 600 "$LOG_FILE"
    fi

    # Remove oldest files if we exceed maximum number of files
    LOG_NB_FILES=$(ls -l "${LOG_DIR}/${LOG_FILE_NAME}_"*".log.gz" 2>/dev/null | wc -l)
    if [ $LOG_NB_FILES -gt $LOG_MAX_FILES ]
    then
        MAX_INDEX=$(($LOG_NB_FILES-$LOG_MAX_FILES))
        CURRENT_INDEX=1
        for ARCHIVED_LOG_FILE in "${LOG_DIR}/${LOG_FILE_NAME}_"*".log.gz"
        do
            if [ $CURRENT_INDEX -le $MAX_INDEX ]
            then
                rm -f "$ARCHIVED_LOG_FILE" 2>/dev/null
            fi
            CURRENT_INDEX=$(($CURRENT_INDEX+1))
        done
    fi
}

# Log to stdout and log file
log_out () {
    echo "[$1] $2"
    echo "["`rfc_timestamp`"][$$][$1] $2" >> "$LOG_FILE"
}

# Log to stderr and log file
log_error () {
    >&2 echo "[$1][ERROR] $2"
    echo "["`rfc_timestamp`"][$$][$1][ERROR] $2" >> "$LOG_FILE"
}

# Log latest lock attempt
log_lock_attempt () {
    echo "[LOCKING] $1"
    echo "["`rfc_timestamp`"][$$][LOCKING] $1" >> "$LATEST_LOCK_LOG_FILE"
}

# Get file or directory's owner
get_owner () {
    ls -ld "$1" 2>/dev/null | awk '{print $3}'
}

get_current_user () {
    if command -v whoami >/dev/null 2>&1
    then
        whoami
    else
        id -un
    fi
}

get_exit_code () {
    case "$1" in
        # ENO_SUCCESS
        0)
            echo 0
        ;;
        # ENO_GENERAL
        1)
            echo 1
        ;;
        # ENO_LOCKFAIL
        2)
            echo 2
        ;;
        # ENO_RECVSIG
        3)
            echo 3
        ;;
        # ENO_ALRDY_RUNNING
        4)
            echo 0
        ;;
        # ENO_NOT_REGISTRED
        5)
            echo 4
        ;;
        # ENO_NO_SSH_KEY
        6)
            echo 5
        ;;
        *)
            echo 1
        ;;
    esac
}

get_exit_text () {
    case "$1" in
        0)
            echo "ENO_SUCCESS"
        ;;
        1)
            echo "ENO_GENERAL"
        ;;
        2)
            echo "ENO_LOCKFAIL"
        ;;
        3)
            echo "ENO_RECVSIG"
        ;;
        4)
            echo "ENO_ALRDY_RUNNING"
        ;;
        5)
            echo "ENO_NOT_REGISTRED"
        ;;
        6)
            echo "ENO_NO_SSH_KEY"
        ;;
        *)
            echo "ENO_UNKNOWN"
        ;;
    esac
}

###############################################
# SERVICE STARTUP
###############################################

rotate_log

# Compatibility with previous installations
if [ -f "$OLD_PID_FILE" ] && [ $(get_owner "$OLD_PID_FILE") = "root" ]
then
    USER_LOCK_DIR="$OLD_LOCK_DIR"
    PID_FILE="$OLD_PID_FILE"
elif [ -f "$PID_FILE" ] && [ $(get_owner "$PID_FILE") != $(get_current_user) ]
then
    # Remove non-legitimate lock file
    rm -f "$PID_FILE" 2>/dev/null
fi

###############################################
# SERVICE LOCK
###############################################

trap 'ECODE_INDEX=$?
      ECODE=$(get_exit_code "$ECODE_INDEX")
      ETXT="$(get_exit_text "$ECODE_INDEX")"
      if [ "$ECODE_INDEX" -eq 4 ]
      then
          log_lock_attempt "Exit: ${ETXT}(${ECODE})"
      else
          log_out "LOCKING" "Exit: ${ETXT}(${ECODE})"
      fi
      exit ${ECODE}' 0
log_lock_attempt "Process started, lock attempt"

mkdir -p "$COMMON_LOCK_DIR"
chmod 777 "$COMMON_LOCK_DIR"

if [ ! -d "${USER_LOCK_DIR}" ]
then
    if ! mkdir -p "${USER_LOCK_DIR}" > /dev/null 2>&1
    then
        log_lock_attempt "Lock failed, could not create lock dir $USER_LOCK_DIR"
        exit ${ENO_LOCKFAIL}
    fi
    chmod 700 "${USER_LOCK_DIR}"

    # Lock succeeded, install signal handlers before storing the PID just in case
    # storing the PID fails
    trap 'ECODE_INDEX=$?
          ECODE=$(get_exit_code "$ECODE_INDEX")
          ETXT="$(get_exit_text "$ECODE_INDEX")"
          log_out "LOCKING" "Removing lock"
          rm -rf "${USER_LOCK_DIR}"
          log_out "LOCKING" "Exit: ${ETXT}(${ECODE})"
          exit ${ECODE}' 0
    echo "$$" >"${PID_FILE}"
    chmod 600 "${PID_FILE}"

    # The following handler will exit the script upon receiving these signals
    # The trap on "0" (EXIT) from above will be triggered by this trap's "exit" command!
    trap 'log_error "LOCKING" "Killed by a signal"
          exit ${ENO_RECVSIG}' 1 2 3 15

    log_lock_attempt "Lock success, installed signal handlers"
    cat "$LATEST_LOCK_LOG_FILE" >> "$LOG_FILE"
else
    # Lock failed, check if the other PID is alive
    OTHER_PID="$(cat "${PID_FILE}")"
    CAT_EXIT=$?

    # If cat isn't able to read the file, another instance is probably
    # about to remove the lock -- exit, we're *still* locked
    if [ -f "${PID_FILE}" ] && [ $CAT_EXIT -ne 0 ]
    then
        log_lock_attempt "Lock failed, PID ${OTHER_PID} is active"
        exit ${ENO_ALRDY_RUNNING}
    fi

    if [ -z "$OTHER_PID" ] || ! kill -0 $OTHER_PID  > /dev/null 2>&1
    then
        # Lock is stale, remove it and restart
        log_out "LOCKING" "Removing stale lock of nonexistant PID ${OTHER_PID}"
        rm -rf "${USER_LOCK_DIR}"
        log_out "LOCKING" "Restarting"
        exec "$0" "$@"
    else
        # Lock is valid and OTHER_PID is active - exit, we're locked!
        log_lock_attempt "Lock failed, PID ${OTHER_PID} is active"
        exit ${ENO_ALRDY_RUNNING}
    fi
fi

###############################################
# START OXIBACKUPD
###############################################

mkdir -p "$INSTALL_DIR/var/cache/oxibackup"
mkdir -p "$HOME/.cache"

if [ ! -L "$HOME/.cache/oxibackup" ] && [ ! -f "$HOME/.cache/oxibackup" ] && [ ! -d "$HOME/.cache/oxibackup" ]
then
    ln -sfn "$INSTALL_DIR/var/cache/oxibackup" "$HOME/.cache/oxibackup"
fi

if ! . "$INSTALL_DIR/etc/oxibackupd/config/oxibackupd.conf"
then
    log_error "OXIBACKUPD" "Could not load oxibackup agent configuration file"
    exit ${ENO_GENERAL}
fi

mkdir -p "$OXIBACKUP_USER_DIR"
chmod 700 "$OXIBACKUP_USER_DIR"
mkdir -p "$OXIBACKUP_CONFIG_DIR"
chmod 700 "$OXIBACKUP_CONFIG_DIR"

# Parse machine auth file
if [ ! -f "$AUTH_FILE" ]
then
    OLD_AUTH_FILE="/opt/oxibackup/config/auth.conf"
    if [ ! -f "$OLD_AUTH_FILE" ]
    then
        log_error "OXIBACKUPD" "Machine is not registered on Oxibox services"
        exit ${ENO_NOT_REGISTRED}
    fi

    mv "$OLD_AUTH_FILE" "$AUTH_FILE"
    rm -f "/opt/oxibackup/config/synch.conf" > /dev/null 2>&1
fi

chmod 600 "$AUTH_FILE"

mkdir -p "$SSH_KEY_DIR"
chmod 700 "$SSH_KEY_DIR"

# Set path to SSH key file
if [ ! -f "$SSH_KEY_FILE" ]
then
    OLD_KEY_FILE="/opt/oxibackup/config/sshkey"
    if [ ! -f "$OLD_KEY_FILE" ]
    then
        OLD_KEY_FILE="$HOME/.oxibackup/config/sshkey"
        if [ ! -f "$OLD_KEY_FILE" ]
        then
            log_error "OXIBACKUPD" "Machine does not have any SSH key"
            exit ${ENO_NO_SSH_KEY}
        fi
    fi
    mv "$OLD_KEY_FILE" "$SSH_KEY_FILE"
    mv "${OLD_KEY_FILE}.pub" "${SSH_KEY_FILE}.pub"
    rmdir "/opt/oxibackup/config" > /dev/null 2>&1
fi

chmod 600 "$SSH_KEY_FILE"
chmod 644 "${SSH_KEY_FILE}.pub"

OPTS=""
if [ ! -z "$GC" ]
then
    if [ "$GC" -gt 0 ] 2>/dev/null && [ "$GC" -le 100 ] 2>/dev/null
    then
        OPTS="--gc=$GC"
    else
        log_error "OXIBACKUPD" "Wrong value for GC '$GC', using default"
    fi
fi
if [ ! -z "$CPUS" ]
then
    if [ "$CPUS" -gt 0 ] 2>/dev/null
    then
        OPTS="$OPTS --cpus=$CPUS"
    elif [ "$CPUS" -ne 0 ] 2>/dev/null
    then
        log_error "OXIBACKUPD" "Wrong value for CPUS '$CPUS', using default"
    fi
fi

log_out "OXIBACKUPD" "Starting oxibackupd..."
TMPDIR="$INSTALL_DIR/tmp" "$INSTALL_DIR/bin/oxibackupd" $OPTS > /dev/null 2>&1
