#!/usr/bin/bash

# SHLINT:yes
# Copyright (c) 2020-2021 by NetApp, Inc.
# All Rights Reserved.

#
# This script:
#   1. updates the server.cfg file for the AKV client
#   2. initializes kmip.db and creates an admin user (if needed)
#   3. creates a partition for the client
#   4. creates a user in the partition created in step 3
#   5. creates an admin for the partition created in step 3
#
# Example usage with the AKV emulator:
#
# ./netapp_setup_for_AKV.sh server1.AKV key1 user-eKMIP /cfcard/kmip/server/client.pem 3600
#

# set up for extended globbing
shopt -s extglob

readonly KMIP_SERVER_DIR="/cfcard/kmip/server"
readonly KMIP_SERVER_CFG="${KMIP_SERVER_DIR}/server.cfg"

readonly LOG_FILE="${KMIP_SERVER_DIR}/netapp_setup_for_AKV.log"
readonly EPK_AGENT_LOG_FILE="${KMIP_SERVER_DIR}/epk_agent.log"

readonly CAT="/bin/cat"
readonly EPK_AGENT="/usr/sbin/epk_agent"
readonly FILE="/usr/bin/file"
readonly GREP="/usr/bin/grep"
readonly PS="/bin/ps"
readonly RECOVER_SERVER_CFG="${KMIP_SERVER_DIR}/netapp_recover_server_cfg.sh"
readonly SPMCTL="/usr/sbin/spmctl"
readonly SUDO_VCONTEXT="sudo vcontext -v 4294967293"
readonly TOUCH="/usr/bin/touch"

readonly CREATE_EKMIP_PARTITION="/usr/sbin/create_ekmip_partition"

# for debugging... verbosity increases with level
readonly DEBUG_LEVEL=1

PWD=`pwd`

if [ ${DEBUG_LEVEL} -gt 1 ]; then
    echo "Before locking check: $0 $* called at `date` (`date +%s`)" >> ${LOG_FILE}
fi

# for locking
readonly LOCKED="locked"
readonly LOCK_FILE="${KMIP_SERVER_DIR}/netapp_setup_for_AKV.lock"

function check_prerequisites
{
    local progname=$1
    local n_args=$2
    local retVal=0

    if [[ ! -d ${KMIP_SERVER_DIR} ]]; then
        retVal=1
    fi

    if [[ $n_args -ne 4 ]]; then
        echo "usage: $progname server key client_name path_to_client_pem"
        retVal=2
    fi

    if [ ${DEBUG_LEVEL} -gt 1 ]; then
        echo "check_prerequisites: returning $retVal at `date` (`date +%s`)" >> ${LOG_FILE}
    fi

    return $retVal
}

# check if file is an ASCII file (returns 0 if it is ASCII)
is_server_cfg_ascii()
{
    ${FILE} $1 | ${GREP} -q "ASCII text"

    echo -n $?
}

function check_server_cfg
{
    # XXX: clean up...
    local service="AKV"
    local retVal=0
    local is_corrupted

    is_corrupted=$(is_server_cfg_ascii ${SERVER_CFG})
    # check if server.cfg needs to be recovered
    if [ ! -s ${SERVER_CFG} ] || [ "${is_corrupted}" == "1" ]; then
	# need to recover server.cfg
	${RECOVER_SERVER_CFG}
	retVal=$?
    fi

    return $retVal
}

function update_server_cfg
{
    local server=$1
    local partition=${server//?(.AKV|.AWS|.GCP|.IKP)/}
    local label=$2
    local retVal=0

    # /usr/sbin/epk_agent expects input of <label>\n<value>\n pairs
    # if <label> exists in server.cfg then the old <value> will be overwritten
    epk_server_label="mop_epk.${partition}.server"
    epk_server_value="${server}"
    epk_key_label="mop_epk.${partition}.kek.label"
    epk_key_value="${label}"

    echo "$0: updating server.cfg at `date` (`date +%s`)" >> ${EPK_AGENT_LOG_FILE}
    ${CAT} << EOF | ${EPK_AGENT}
${epk_server_label}
${epk_server_value}
${epk_key_label}
${epk_key_value}
EOF
    retVal=$?
    echo "$0: finished updating server.cfg at `date` (`date +%s`)" >> ${EPK_AGENT_LOG_FILE}

    if [ ${DEBUG_LEVEL} -gt 1 ]; then
        echo "update_server_cfg: returning $retVal at `date` (`date +%s`)" >> ${LOG_FILE}
    fi

    return $retVal
}

function create_kmip_db
{
    local retVal=0

    # initialize kmip.db
    if [[ ! -f kmip.db ]]; then
        ./init
        if [ ${DEBUG_LEVEL} -gt 1 ]; then
            echo "create_kmip_db: ./init returned $retVal at `date` (`date +%s`)" >> ${LOG_FILE}
        fi
        retVal=$?
    fi

    # create eKMIP-admin
    if [ $retVal -eq 0 ] && [ ! -f db/data/part-eKMIP-admin.json ]; then
        ./initadmin /cfcard/kmip/server/certs/server.pem
        if [ ${DEBUG_LEVEL} -gt 1 ]; then
            echo "create_kmip_db: ./initadmin returned $retVal at `date` (`date +%s`)" >> ${LOG_FILE}
        fi
        retVal=$?
    fi

    return $retVal
}

function add_user
{
    local user=$1
    local client_pem_pathname=$2
    local retVal=0

    if [[ -f db/data/part-${user}.json ]]; then
        echo "add_user: user ${user} already exists at `date` (`date +%s`)" >> ${LOG_FILE}
        # nothing needs to done
        return $retVal
    fi

    if [[ -f $client_pem_pathname ]]; then
        ${SUDO_VCONTEXT} ${CREATE_EKMIP_PARTITION} ${CREATE_EKMIP_PARTITION} LOCALHOST $user $client_pem_pathname
        retVal=$?
        if [[ $retVal -eq 0 ]]; then
            ${TOUCH} db/data/part-${user}.json
            echo "Added user $user to DB at `date` (`date +%s`)" >> ${LOG_FILE}
        fi
    else
        echo "Failed to add user $user to DB at `date` (`date +%s`)" >> ${LOG_FILE}
        retVal=1
    fi

    return $retVal
}

function startup_server_via_spmctl
{
    local retVal=0

    ${PS} ax | ${GREP} kmip_server | ${GREP} -v grep
    if [[ $? -eq 0 ]]; then
        # nothing needs to done
        if [ ${DEBUG_LEVEL} -gt 1 ]; then
            echo "startup_server_via_spmctl: kmip_server already running at `date` (`date +%s`)" >> ${LOG_FILE}
        fi
        return $retVal
    fi

    # needs to be done before kmip_server started up
    check_server_cfg
    retVal=$?
    if [[ $retVal -ne 0 ]]; then
        echo "Error recovering ${KMIP_SERVER_CFG} at `date` (`date +%s`)" >> ${LOG_FILE}
        exit $retVal
    fi

    create_kmip_db
    retVal=$?
    if [[ $retVal -ne 0 ]]; then
        echo "create_kmip_db returning $ret at `date` (`date +%s`)" >> ${LOG_FILE}
        exit $ret
    fi

    ${SPMCTL} -h kmip_server -e
    retVal=$?

    # give time for things to settle
    sleep 1

    echo "startup_server_via_spmctl: kmip_server started at `date` (`date +%s`)" >> ${LOG_FILE}
    return $retVal
}

# lock against "simultaneous" execution
if [ "$1" != "${LOCKED}" ]; then
    # first in, grab lock, fork process and run script
    if [ ${DEBUG_LEVEL} -gt 1 ]; then
        echo "Grabbing lock at `date` (`date +%s`)" >> ${LOG_FILE}
    fi
    lockf -t 20 ${LOCK_FILE} $0 ${LOCKED} "$@"
    retVal=$?
    if [ ${DEBUG_LEVEL} -gt 1 ]; then
        echo "Parent exiting with retVal = $retVal at `date` (`date +%s`)" >> ${LOG_FILE}
    fi
else
    # make sure lock is being held
    lockf -t 0 ${LOCK_FILE} echo > /dev/null 2>&1
    # return value of 75 means lock is being held
    retVal=$?
    if [ ${retVal} -ne 75 ]; then
        echo "Error: lock held by another process at `date` (`date +%s`). Exit with status = 1." >> ${LOG_FILE}
        exit 1
    fi
    # remove "locked" from command line
    shift;
fi

if [ ${DEBUG_LEVEL} -gt 1 ]; then
    echo "After locking check: $0 $* called at `date` (`date +%s`)" >> ${LOG_FILE}
fi

check_prerequisites $0 $#
ret=$?
if [[ $ret -ne 0 ]]; then
    echo "check_prerequisites returning $ret at `date` (`date +%s`)" >> ${LOG_FILE}
    exit $ret
fi

cd ${KMIP_SERVER_DIR}

startup_server_via_spmctl
ret=$?
if [[ $ret -ne 0 ]]; then
    cd $PWD
    echo "startup_server_via_spmtcl returning $ret at `date` (`date +%s`)" >> ${LOG_FILE}
    exit $ret
fi

add_user $3 $4
ret=$?
if [[ $ret -ne 0 ]]; then
    cd $PWD
    echo "add_user returning $ret at `date` (`date +%s`)" >> ${LOG_FILE}
    exit $ret
fi

update_server_cfg $1 $2
ret=$?
if [[ $ret -ne 0 ]]; then
    cd $PWD
    echo "update_server_cfg returning $ret at `date` (`date +%s`)" >> ${LOG_FILE}
    exit $ret
fi

echo "returning 0 at `date` (`date +%s`)" >> ${LOG_FILE}

cd $PWD
exit 0
