#!/bin/bash

#########################################
# Name: gwsetup
#
# Summary: xFOCE disk setup script (run as root)
#
# Description: Setup a new xfoce sdcard
# - configure hostname
# - configure ops user
# - configure systemd services
# - configure SD card mounts 
# - install updates to rootfs files
# - optionally install a distribution
#
# Author: k. headley
#
# Copyright MBARI 2013
#
#########################################

#########################################
# Script configuration defaults
# casual users should not need to change
# anything below this section
#########################################

GW_HOST=""
XF_ARCH_PATH=""
XF_VERSION=""

DO_ALL="Y"
DO_HOSTNAME=""
DO_OPS_ACCT=""
DO_SUDO=""
DO_MOUNTS=""
DO_DIST=""
DO_OPS_ENV=""
DO_SYSD=""
DO_FIRM=""
DO_BUILD=""
DO_CLEAN=""
DO_XFHOME=""

VERBOSE="N"
OPS_PW=brynner

#################################
# Script variable initialization
#################################

XF_OPS_HOME="/home/ops"
LIBSYSD="lib/systemd/system"
ETCSYSD="etc/systemd/system"
ETCSYSC="etc/sysconfig"
LIBFIRM="lib/firmware"
UDEV_RULES="etc/udev/rules.d"

SD_MDEV="/dev/mmcblk0p3"
SD_MPOINT="/mnt/xfoce"

SD_FSTAB="${SD_MDEV}   ${SD_MPOINT}  auto   defaults  0  0"
XF_BUILD="${SD_MPOINT}/build"
XF_DEPLOY_NAME="deployment"
XF_DEPLOY_DIR="${SD_MPOINT}/${XF_DEPLOY_NAME}"
XF_DEPLOY_LINK="${XF_OPS_HOME}/${XF_DEPLOY_NAME}"

XF_ARCH_NAME="" #`basename ${XF_ARCH_PATH}`
XF_ARCH_DIR="${XF_ARCH_PATH%%/${XF_ARCH_NAME}}"
XF_ARCH_VERSION="${XF_ARCH_NAME%%.tar.gz}"
XFHOME="${XF_BUILD}/${XF_ARCH_VERSION}"
XF_ROOTFS="${XFHOME}/src/gateway/rootfs"
XF_SCRIPTS="${XFHOME}/src/scripts"
KSRC="/usr/src/kernel"

TEST="echo"
DATESTR=`date +%Y%m%d-%H%M%S`
WDIR=`pwd`
DTC="/usr/src/kernel/scripts/dtc/dtc"
declare -a DTS=( "XF-UART1-00A0" "XF-UART2-00A0" "XF-UART4-00A0" "XF-UART5-00A0" "XF-SPIDEV1-00A0" "XF-GWMUX-00A0"  "XF-ADC-00A0" )

#################################
# Function Definitions
#################################
#################################
# name: sigTrap
# description: signal trap callback
# will interrupt 
# args: none
#################################
sigTrap(){
    exit 0;
}

#################################
# name: printUsage
# description: print use message
# args: none
#################################
printUsage(){
    echo
    echo "usage: `basename $0` [options] -a <hostname> -f <file>"
    echo "-a <hostname> : gateway hostname "
    echo "-d <name>     : deployment name           [$XF_DEPLOYMENT_NAME]"
    echo "-f <file>     : xfoce archive tar.gz "
	echo "                Creates XFHOME using specified archive"
    echo "-B            : build distribution"
    echo "-b            : skip build distribution"
	echo ""
    echo "-C            : clean bak files         [$DO_CLEAN]"
    echo "-V            : verbose output          [$VERBOSE]"
    echo "-h            : print help message"
    echo ""
    echo
}

########################################
# name: vout
# description: print verbose message to stderr
# args:
#     msg: message
########################################
vout(){
if [ "$VERBOSE" == "Y" ] || [ "$VERBOSE" == "TRUE" ]
    then
	echo "$1" >&2
    fi
}

########################################
# name: imsg
# description: echos with indent
# args:
#     msg: message
#  indent: indent level
########################################
imsg(){
  OIFS=$IFS
  IFS=""
  msg_pad="                                        "
  let "msg_indent=${1}"
  shift
  all="${@}"
  printf "%s%s\n" ${msg_pad:0:${msg_indent}} ${all}
  IFS=$OIFS
}

########################################
# name: msg
# description: echos with default indent
# args:
#     msg: message
########################################
msg(){
 imsg $MSG_INDENT "${*}"
}

########################################
# name: exitError
# description: print use message to stderr
# args:
#     msg:        error message
#     returnCode: exit status to return
########################################
exitError(){
    echo >&2
    echo "$1" >&2
    echo >&2
    exit $2
}

########################################
# name: mkbak
# description: create backup copy of a file
# args:
#        file: target file
#     DATESTR: date/time string
#              defined above
#  returnCode: exit status to return
########################################
mkbak(){
	local target="${1}"
	local orig="$target.orig.gws"
	local bak="$target.${DATESTR}.bak.gws"

	if [ -f "${target}" ]
	then
		if [ ! -f "${orig}" ]
		then
			cp $target $orig
		else
			cp $target $bak
		fi
	fi
}

########################################
# name: validateArgs
# description: check arguments/requirements
# args:
# returnCode:
########################################
validateArgs(){
	# REQUIRED: run as root
	amroot=`whoami`
	if [ ${amroot} != "root" ]
	then
		exitError "This utility must be run as root" -1
	fi

	# REQUIRED: valid date
	let "now_date=`date +%s`"
	let "valid_date=`date  +%s -u --date=\"2013-11-01 00:00\"`"

	if [ "${now_date}" -lt "${valid_date}" ]
	then
		now_hr=`date`
		echo
		echo " `basename $0`: Error - system time is invalid [${now_hr}]"
		echo " Set system clock to current UTC time before continuing, e.g."
		#echo "     date -s \"Aug 26 23:07:59 UTC 2014\""
		echo "     date -u -s \"2014-08-06 12:00\""
		echo
		exit 1
	fi

	# REQUIRED: valid hostname 
	if [ -z "${GW_HOST}" ] && [ "${DO_HOSTNAME}" == "Y" ]
	then
		printUsage
		exitError "hostname required" 1
	fi


	# REQUIRED: valid archive file
	if [ ! -f "${XF_ARCH_PATH}" ]
	then
		exitError " `basename $0`: Error - specified archive not valid [${XF_ARCH_PATH}]"
	fi
}
########################################
# name: setEnv
# description: set environment (after options)
# args:
# returnCode:
########################################
setEnv(){
	XF_ARCH_NAME=`basename ${XF_ARCH_PATH}`
	XF_ARCH_VERSION="${XF_ARCH_NAME%%.tar.gz}"
	XFHOME="${XF_BUILD}/${XF_ARCH_VERSION}"
	XF_ROOTFS="${XFHOME}/src/gateway/rootfs"
	XF_SCRIPTS="${XFHOME}/src/scripts"
}

########################################
# name: cfgHostName
# description: set host name
# args:
# returnCode:
########################################
cfgHostName(){
	vout "Configuring hostname"
	# network config
	# - hostname
	# - /etc/hostname

	now_host=`hostname`
	if [ "${now_host}" != "${GW_HOST}" ]
	then
		mkbak /etc/hostname
		hostname ${GW_HOST}
		echo ${GW_HOST} > /etc/hostname
	else
	vout "Hostname OK - skipping"
	fi
}

########################################
# name: cfgUsers
# description: setup users 
# args:
# returnCode:
########################################
cfgUsers(){
	vout "Creating user accounts"
# do not indent
adduser ops <<RESPONSE
$OPS_PW
$OPS_PW
RESPONSE

	vout "Configuring groups"
	addgroup ops
	usermod -aG sudo ops
	usermod -aG users ops
	usermod -aG lock ops
	usermod -aG uucp ops
	usermod -aG dialout ops
    usermod -aG operator ops
    usermod -aG systemd-journal ops
	usermod -aG tty ops

}

########################################
# name: cfgSudo
# description: configure sudo
# args:
# returnCode:
########################################
cfgSudo(){
	vout "Configuring sudo"
	# sudo
	# - add ops
	# modify /etc/sudoers (enable sudo group)
	sudo adduser ops sudo

	grep -e "^[ \t]*%sudo.*ALL" /etc/sudoers
	sudo_modfied=$?
	if [ "${sudo_modfied}" -ne 0 ]
	then
		mkbak /etc/sudoers
		echo " %sudo	ALL=(ALL) ALL" >> /etc/sudoers
	else
		vout "sudo config exists - skipping"
	fi
}

########################################
# name: cfgLimits
# description: configure ulimit (i.e., enable core dumps)
# args:
# returnCode:
########################################
cfgLimits(){
    LIMITS_CONF="/etc/security/limits.conf"
    vout "Configuring limits.conf"
    # modify ${LIMITS_CONF} (enable core dumps)

    # check whether modified
    grep -e "^[ \t]*#.*soft.*core" ${LIMITS_CONF}
    limits_disabled=$?
    if [ "${limits_disabled}" -eq 0 ]
    then
    mkbak ${LIMITS_CONF}
    cat ${LIMITS_CONF}|sed -e "s/\(^[ \t]*#\)\(.*soft.*core\)\(.*\)/\2\t\tunlimited/g" >> ${LIMITS_CONF}.mod
    cp ${LIMITS_CONF}.mod ${LIMITS_CONF}
    rm ${LIMITS_CONF}.mod
    else
    vout "limits.conf already modified - skipping"
    fi
}

########################################
# name: cfgMounts
# description: configure disk mounts
# args:
# returnCode:
########################################
cfgMounts(){
	vout "Configuring mounts"
	
	if [ ! -d ${SD_MPOINT} ]
	then
		mkdir -p ${SD_MPOINT}
	else
		vout "xFOCE mount point exists - skipping"
	fi
	chown ops:ops ${SD_MPOINT}
	chmod 775 ${SD_MPOINT}

	vout "Mounting data partition ${SD_MDEV} on ${SD_MPOINT}"
	mount ${SD_MDEV} ${SD_MPOINT}

	if [ ! -d ${XF_BUILD} ]
	then
		vout "Creating build directory [${XF_BUILD}]"
		mkdir -p ${XF_BUILD}
	else
		vout "xFOCE build directory exists - skipping"
	fi

    if [ ! -d ${XF_DEPLOY_DIR} ]
    then
		vout "Creating deployment directory [${XF_DEPLOY_DIR}]"
		mkdir -p ${XF_DEPLOY_DIR}/conf
		mkdir -p ${XF_DEPLOY_DIR}/arch
		mkdir -p ${XF_DEPLOY_DIR}/logs
    else
		vout "xFOCE build directory exists - skipping"
    fi

	if [ ! -d ${XFHOME} ]
	then
	 mkdir -p ${XFHOME}
	fi
	
	if [ -d "${XF_BUILD}" ]
	then
		chown ops:ops ${XF_BUILD}
		chmod 775 ${XF_BUILD}	
	else
		exitError "cfgMounts failed" 1
	fi
	
	if [ -d "${SD_MPOINT}" ]
	then
		chown ops:ops ${SD_MPOINT}
		chmod 775 ${SD_MPOINT}
	fi

	if [ -d "${XF_DEPLOY_DIR}" ]
	then
        chown ops:ops ${XF_DEPLOY_DIR}
        chmod 775 ${XF_DEPLOY_DIR}
        chown -R ops:ops ${XF_DEPLOY_DIR}/*
        chmod -R 775 ${XF_DEPLOY_DIR}/*
	fi

	vout "Configuring fstab"
	grep -e "$SD_MDEV.*$SD_MPOINT" /etc/fstab 
	has_sdmount=$?
	if [ "${has_sdmount}" -ne 0 ]
	then
		mkbak /etc/fstab
		echo ${SD_FSTAB} >> /etc/fstab
	else
		vout "fstab entry exists - skipping"
	fi

}

########################################
# name: cfgOpsEnv
# description: configure ops environment
# args:
# returnCode:
########################################
cfgOpsEnv(){

	# ops environment: .profile, .bash, xfoce/build links
	vout "ops environment..."

	# Configure .profile
	mkbak ${XF_OPS_HOME}/.profile
	cp ${XF_ROOTFS}/${XF_OPS_HOME}/dot.profile ${XF_OPS_HOME}/.profile

	chown ops:ops ${XF_OPS_HOME}/.profile
	chmod 644 ${XF_OPS_HOME}/.profile

	# Configure .xfenv
	mkbak ${XF_OPS_HOME}/.xfenv

	# /////////////////////
	# write .xfenv
	# /////////////////////
	vout "writing .xfenv [using $XFHOME]"
# do not indent
cat <<XFENV_END > ${XF_OPS_HOME}/.xfenv
#!/bin/bash

# bootstrap xFOCE home
XFHOME=${XFHOME}
if [ -d "\${XFHOME}" ]
then
. \${XFHOME}/src/scripts/xfenv \${XFHOME}
else
echo "Warning: XFHOME directory not found [\${XFHOME}]"
fi
XFENV_END
# /////////////////////
		
	chown ops:ops ${XF_OPS_HOME}/.xfenv
	chmod 644 ${XF_OPS_HOME}/.xfenv

	vout "creating symlinks"	
	vout "XFHOME      :[$XFHOME]"
	vout "XF_BUILD    :[$XF_BUILD]"
    vout "XF_OPS_HOME :[$XF_OPS_HOME]"
    vout "XF_DEPLOY   :[$XF_DEPLOY_LINK]"

	# set xfoce link
	LINK="${XF_OPS_HOME}/xfoce"
	TARGET="${XFHOME}"
	if [ -e ${LINK} ] && [ -L ${LINK} ]
	then
		vout "link ${LINK} exists - removing"
		rm ${LINK}
	fi

	su -p -c "ln -s ${TARGET} ${LINK};" ops
	chown ops:ops ${LINK}
	chmod 775 ${LINK}

	# set build link
	LINK="${XF_OPS_HOME}/build"
	TARGET="${XF_BUILD}"
	if [ -e ${LINK} ] && [ -L ${LINK} ]
	then
		vout "link ${LINK} exists - removing"
		rm ${LINK}
	fi
	
	su -p -c "ln -s ${TARGET} ${LINK};" ops
	chown ops:ops ${LINK}
	chmod 775 ${LINK}

	# set deployment link
    LINK="${XF_DEPLOY_LINK}"
    TARGET="${XF_DEPLOY_DIR}"
    if [ -e ${LINK} ] && [ -L ${LINK} ]
    then
		vout "link ${LINK} exists - removing"
		rm ${LINK}
    fi

    su -p -c "ln -s ${TARGET} ${LINK};" ops
    chown ops:ops ${LINK}
    chmod 775 ${LINK}

}

########################################
# name: cfgRootFS
# description: configure root filesystem 
# args:
# returnCode:
########################################
cfgRootFS(){
	# install rootfs patches
	
	vout "Applying systemd updates"
	# systemd updates
	# - dropbear SSH (systemd)
	# - socat (systemd)
	# - xfgpio (systemd)
	vout "Systemd..."
	grep -e "[ \t]*KillMode[ \t]*=[ \t]*process" /${LIBSYSD}/dropbear\@.service
	dbssh_modified=$?
	if [ "${dbssh_modified}" -ne 0 ]
	then
	vout "/${LIBSYSD}/dropbear\@.service"
		mkbak /${LIBSYSD}/dropbear\@.service
		cp ${XF_ROOTFS}/${LIBSYSD}/dropbear\@.service /${LIBSYSD}/.
	else
	vout "/${LIBSYSD}/dropbear\@.service modified - skipping"
	fi

	if [ ! -e "/${ETCSYSC}/socatd" ]
	then
	vout "/${ETCSYSC}/socatd"
		mkbak /${ETCSYSC}/socatd
		cp ${XF_ROOTFS}/${ETCSYSC}/socatd /${ETCSYSC}/. 
	else
	vout "/${ETCSYSC}/socatd exists - skipping"
	fi

	if [ ! -e "/${ETCSYSC}/socatd-pty0" ]
	then
	vout "/${ETCSYSC}/socatd-pty0"
		mkbak /${ETCSYSC}/socatd-pty0
		cp ${XF_ROOTFS}/${ETCSYSC}/socatd-pty0 /${ETCSYSC}/. 
	else
	vout "/${ETCSYSC}/socatd-pty0 exists - skipping"
	fi

	if [ ! -e "/${ETCSYSC}/xfgpio" ]
	then
	vout "/${ETCSYSC}/xfgpio"
		mkbak /${ETCSYSC}/xfgpio
		cp ${XF_ROOTFS}/${ETCSYSC}/xfgpio /${ETCSYSC}/. 
	else
	vout "/${ETCSYSC}/xfgpio exists - skipping"
	fi

    if [ ! -e "/${ETCSYSC}/xfdigi" ]
    then
        vout "/${ETCSYSC}/xfdigi"
        mkbak /${ETCSYSC}/xfdigi
        cp ${XF_ROOTFS}/${ETCSYSC}/xfdigi /${ETCSYSC}/.
    else
    vout "/${ETCSYSC}/xfdigi exists - skipping"
    fi

	if [ ! -e "/${ETCSYSD}/socatd" ]
	then
	vout "/${ETCSYSD}/socatd.service"
		mkbak /${ETCSYSD}/socatd.service
		cp ${XF_ROOTFS}/${ETCSYSD}/socatd.service /${ETCSYSD}/. 
	else
	vout "/${ETCSYSD}/socatd.service - skipping"
	fi

	if [ ! -e "/${ETCSYSD}/socatd\@.service" ]
	then
	vout "/${ETCSYSD}/socatd\@.service"
		mkbak /${ETCSYSD}/socatd\@.service
		cp ${XF_ROOTFS}/${ETCSYSD}/socatd\@.service /${ETCSYSD}/. 
	else
	vout "/${ETCSYSD}/socatd\@.service - skipping"
	fi

	if [ ! -e "/${ETCSYSD}/xfgpio.service" ]
	then
	vout "/${ETCSYSD}/xfgpio.service"
		mkbak /${ETCSYSD}/xfgpio.service
		cp ${XF_ROOTFS}/${ETCSYSD}/xfgpio.service /${ETCSYSD}/. 
	else
	vout "/${ETCSYSD}/xfgpio.service - skipping"
	fi

    if [ ! -e "/${ETCSYSD}/xfdigi.service" ]
    then
        vout "/${ETCSYSD}/xfdigi.service"
        mkbak /${ETCSYSD}/xfdigi.service
        cp ${XF_ROOTFS}/${ETCSYSD}/xfdigi.service /${ETCSYSD}/.
    else
    vout "/${ETCSYSD}/xfdigi.service - skipping"
    fi

	# build DeviceTree compiler (dtc) if it doesn't exist
	vout "Checking Device Trees"
	if [ ! -e "${DTC}" ]
	then
		vout "building dtc"
		sdir=`pwd`
		cd ${KSRC}
		make scripts
		cd $sdir
	fi
	
	# compile Device Tree files
	sdir=`pwd`
	let "k=0"
	cd /${LIBFIRM}
	while [ "${k}" -lt "${#DTS[*]}" ]
	do
		DTS_FILE=${DTS[$k]}
		vout "copying/compiling /${T_DIR}/${DTS_FILE}"
		mkbak /${LIBFIRM}/${DTS_FILE}
		cp ${XF_ROOTFS}/${LIBFIRM}/${DTS_FILE}.dts /${LIBFIRM}/.
		if [ -e "${DTC}" ] &&  [ -f "${DTS_FILE}.dts" ]
		then
			${DTC} -O dtb -o ${DTS_FILE}.dtbo -b 0 -@ ${DTS_FILE}.dts
		else
			vout "missing ${DTC} and/or ${DTS_FILE}.dts"
		fi
		let "k=$k+1"
	done
	cd $sdir

	# copy udev rules
	vout "Checking udev rules"
	if [ -d ${XF_ROOTFS}/${UDEV_RULES} ] && [ -d /${UDEV_RULES} ]
	then
	vout "Installing udev rules"
		cp ${XF_ROOTFS}/${UDEV_RULES}/*rules /${UDEV_RULES}/.
	else
		vout "missing udev src and/or dest [${XF_ROOTFS}/${UDEV_RULES} /${UDEV_RULES}]"
	fi
	
    vout "enabling gpio init service"
    systemctl enable xfgpio.service
    vout "enabling digi init service"
    systemctl enable xfdigi.service

	if [ ! -e "/lib/libcrypto.so" ]
	then
		echo "Creating ln -s /lib/libcrypto.so.1.0.0 /lib/libcrypto.so"
		ln -s /lib/libcrypto.so.1.0.0 /lib/libcrypto.so
	else
		echo "/lib/libcrypto.so exists - skipping link"
	fi

}

########################################
# name: buildDist
# description: build xfoce distribution
# args:
# returnCode:
########################################
buildDist(){
	# install xfoce distribution

	if [ -f "${XF_BUILD}/${XF_ARCH_NAME}" ] 
	then
		vout "Distribution archive exists - skipping"
	else
		if [ ! -f ${XF_ARCH_PATH} ]
		then
			vout "Distribution archive not found - exiting"
			exit 1
		else
			vout "Installing distribution archive"
			cp ${XF_ARCH_PATH} ${XF_BUILD}/${XF_ARCH_NAME}
			chown ops:ops ${XF_BUILD}/${XF_ARCH_NAME}
			chmod 664 ${XF_BUILD}/${XF_ARCH_NAME}
		fi
	fi

	if [ ! -d "${XFHOME}" ]
	then
		vout "Installing gateway sources"
		cd ${XF_BUILD}
		su -p -c "\
		tar xzvf ${XF_ARCH_NAME};" ops
	else
		vout "Gateway source installed - skipping"
	fi

	if [ -e "${XFHOME}" ] && [ "${DO_BUILD}" == "Y" ]
	then
		vout "Building gateway sources"
		su -p -c "\
		cd ${XFHOME}; \
		. src/scripts/xfenv; \
		. src/scripts/buildenv bblinux; \
		./configure; \
		make" ops
	else
		vout "Skipping distribution build"
	fi
	cd ${WDIR}

}

########################################
# name: cleanBackups
# description: clean up backup files
# args:
# returnCode:
########################################
cleanBackups(){
if [ "${DO_CLEAN}" == "Y" ]
then
	vout "Cleaning backups"
	rm /etc/*.bak.gws
	rm ${XF_OPS_HOME}/*.bak.gws
	rm ${XF_OPS_HOME}/.*.bak.gws
	rm /${LIBSYSD}/*.bak.gws
	rm /${ETCSYSD}/*.bak.gws
	rm /${ETCSYSC}/*.bak.gws
else
	vout "Clean backups - skipping"
fi
}

##########################
# Script main entry point
##########################


# Argument processing
#if [ "$#" -lt 2 ];then
#    printUsage
#    exit 0
#fi

while getopts a:bBCd:f:hV Option
do
    case $Option in
	a ) GW_HOST="$OPTARG"
	;;
	b ) DO_BUILD="N"
	;;
	B ) DO_BUILD="Y"
	;;
    d ) XF_DEPLOY_NAME=$OPTARG
        XF_DEPLOY_DIR="${SD_MPOINT}/${XF_DEPLOY_NAME}"
        XF_DEPLOY_LINK="${XF_OPS_HOME}/${XF_DEPLOY_NAME}"
    ;;
	f ) XF_ARCH_PATH="$OPTARG"
	;;
	C ) DO_CLEAN="Y"
	;;
	V ) VERBOSE="Y"
	;;
	h)printUsage
	  exit 0
	;;
	*) exit 0 # getopts outputs error message
	;;
    esac
done


# call sigTrap on INT,TERM or EXIT
# trap sigTrap INT TERM EXIT

# reset trapped signals
# trap - INT TERM EXIT

validateArgs

setEnv

vout "hostname   : [${GW_HOST}]"
vout "archive    : [${XF_ARCH_PATH}]"
vout "deployment : [${XF_DEPLOY_NAME}]"
vout "version    : [${XF_VERSION}]"
vout "clean      : [${DO_CLEAN}]"
vout "build      : [${DO_BUILD}]"
vout "verbose    : [${VERBOSE}]"
vout "XF_ARCH_NAME    : [$XF_ARCH_NAME]"
vout "XF_ARCH_VERSION : [$XF_ARCH_VERSION]"
vout "XFHOME          : [$XFHOME]"
vout "XF_ROOTFS       : [$XF_ROOTFS]"
vout "XF_SCRIPTS      : [$XF_SCRIPTS]"

cfgHostName
cfgUsers
cfgSudo
cfgLimits
cfgMounts
cfgOpsEnv
cfgRootFS
cleanBackups
buildDist

vout "gwsetup done"
vout ""

