#!/bin/sh
# Copyright (C) 2000-2014 Synology Inc. All rights reserved.

. `dirname $0`/common

GetMajorVersion()
{
	sed 's|\([0-9]*\.[0-9]*\).*|\1|'
}

DATADIR=/var/services/mysql
BINFOLDER=/usr/syno/mysql
PKGVERSION=`/bin/get_key_value /var/packages/$PACKAGE_NAME/INFO version`
PID_FILE=/run/mysqld/mysqld.pid
DATABASE_HAS_BACKUPED_FLAGS="/root/.mariadb_database_has_backedup"

################################
# Hook
################################
HOOK_MARIADB_CHPW=/usr/local/libexec/mariadb_chpw

################################
# UI Related Setting
################################
PKG_INDEXDB_PATH="/var/packages/$PACKAGE_NAME/target/indexdb"
UI_PATH="/var/packages/$PACKAGE_NAME/target/ui"
DSM_INDEX_ADD="/usr/syno/bin/pkgindexer_add"
DSM_INDEX_DEL="/usr/syno/bin/pkgindexer_del"

################################
# Backup Setting
################################
DO_NETBKP_CANCEL_TMP_FILE="/tmp/do_netbkp_cancel.chk"
LOCALBKP_CANCEL_TMP_FILE="/tmp/do_localbkp_cancel.chk"
MYSQL_RCVR_DB_CONFLICT_LIST="/tmp/mysql_rcvr_db_conflict_list.tmp"
MYSQL_RCVR_DB_NEW_LIST="/tmp/mysql_rcvr_db_new_list.tmp"
MYSQLDB_ESSENTIAL_TABLE="user db host tables_priv columns_priv func"
MYSQL_FAIL=1
CANCEL_RET=2

ChkExecEnv()
{
	local found="N"
	local UsbStation=`/bin/get_key_value /etc.defaults/synoinfo.conf usbstation`
	local mysqlSubPath="@database/mysql"
	if [ "yes" = "${UsbStation}" ]; then
		mysqlSubPath="@sharebin/@database/mysql"
	fi

	local mysqlVol=`/usr/syno/bin/servicetool --get-service-volume mysql`
	local mysqlPath="${mysqlVol}/${mysqlSubPath}"

	if /usr/syno/bin/servicetool --get-service-volume mysql; then
		# try to find old mysql volume
		for volume in `grep " /volume" /proc/mounts | awk '{ print $2 }'`; do
			if [ -d "${volume}/${mysqlSubPath}" ]; then
				mysqlVol="${volume}"
				found=Y
				break;
			fi
		done

		if [ "$found" = "N" ]; then
			mysqlVol=`/usr/syno/bin/servicetool --get-first-alive-volume`
			if [ ! -d "$mysqlVol" ]; then # no volume found
				exit 1
			fi
		fi
		mysqlPath="${mysqlVol}/${mysqlSubPath}"
		/bin/ln -fs "${mysqlPath}" "${DATADIR}"
	fi
	if [ ! -d "${mysqlPath}" ]; then
		mkdir -p "${mysqlPath}"

		# register for USB Station
		/usr/syno/sbin/synoservice --regvol -n "${PACKAGE_NAME}" -p "${mysqlVol}" -t SERVICE_PKG
	fi

	[ ! -d /run/mysqld ] && /bin/mkdir -p /run/mysqld
	/bin/chown -R mysql:mysql /run/mysqld "${DATADIR}"/
}

ChkDB()
{
	for tbl in ${MYSQLDB_ESSENTIAL_TABLE}; do
		if [ ! -f ${DATADIR}/mysql/${tbl}.frm -o ! -f ${DATADIR}/mysql/${tbl}.MYI -o ! -f ${DATADIR}/mysql/${tbl}.MYD ]; then
			echo "Initialize MySQL database"
			/usr/bin/mysql_install_db --user=mysql --basedir=/usr --datadir=${DATADIR}
			echo ${PKGVERSION} | sed 's|\(.*\)-[0-9]*|\1|' > ${DATADIR}/VERSION
			/bin/sync; /bin/sync; /bin/sync
		fi
	done
}

UpgradeDatabase()
{
	local _DBVERSION=`cat ${DATADIR}/VERSION | GetMajorVersion`
	local _PKGVERSION=`echo $PKGVERSION | GetMajorVersion`
	if [ x${_DBVERSION} = x${_PKGVERSION} ]; then
		return 0
	fi

	logger -p 0 "MariaDB: UpgradeDatabase: Compatibility check is failed. (Version is not matched)"
	local _DATADIR="`readlink ${DATADIR}`"

	if [ ! -f $DATABASE_HAS_BACKUPED_FLAGS ]; then
		/bin/cp -a ${_DATADIR} "`dirname ${_DATADIR}`/.mysql.`/bin/date +%s`"
		/bin/chown mysql:mysql ${_DATADIR}
		touch $DATABASE_HAS_BACKUPED_FLAGS
	fi

	if ! mysql_upgrade --force -u root --password="$2"; then
		if [ "$1" != "dont_show_msg" ]; then
			synodsmnotify @administrators dsmnotify:system_event mariadb:need_upgrade
		fi
		logger -p 0 "MariaDB: UpgradeDatabase: Failed to upgrade MySQL"
		return 1
	fi

	#TODO: remove backup files
	#rm $DATABASE_HAS_BACKUPED_FLAGS
	echo ${PKGVERSION} | sed 's|\(.*\)-[0-9]*|\1|' > ${DATADIR}/VERSION
	return 0
}

HupPHPFPM()
{
	if initctl status php-fpm | grep -q start; then
		initctl restart php-fpm
	fi
}

CheckCancellingAction()
{
	Ret=0;

	# check if user has canceld the backup task
	if [ $1 = "netbkp" ]; then
		if [ -f "${DO_NETBKP_CANCEL_TMP_FILE}" ]; then
			Ret=${CANCEL_RET}
		fi
	elif [ $1 = "localbkp" ]; then
		if [ -f "${LOCALBKP_CANCEL_TMP_FILE}" ]; then
			Ret=${CANCEL_RET}
		fi
	else
		Ret=${MYSQL_FAIL}
	fi

	return $Ret
}

BackupDatabase()
{
	# Dump MySQL database
	Ret=0
	DST_PATH=$1
	BKPTYPE=$2

	# Start dump
	for i in `find /var/services/mysql/* -type d`
	do
		# check if user has canceld the backup task
		CheckCancellingAction ${BKPTYPE}
		Ret=$?
		if [ ${Ret} -ne 0 ]; then
			return ${Ret}
		fi

		f=`basename ${i}`
		f_tmp=`echo $f | sed -e 's/@00/\\\\x/g'`
		f_tmp=`echo -e "$f_tmp"`
		# skip mysql and test databases
		if [ "${f_tmp}" = "mysql" -o "${f_tmp}" = "test" -o "${f_tmp}" = "performance_schema" ]; then
			continue
		fi

		# dump database
		rm -f "$DST_PATH/${f}.sql"
		/usr/bin/mysqldump --single-transaction --database "${f_tmp}" > "$DST_PATH/${f}.sql"

		# If fail to dump, return ${MYSQL_FAIL}
		if [ $? -ne 0 ]; then
			return ${MYSQL_FAIL}
		fi

		# check if user has canceld the backup task before excuting gzip command
		CheckCancellingAction ${BKPTYPE}
		Ret=$?
		if [ ${Ret} -ne 0 ]; then
			return ${Ret}
		fi

		rm -f "$DST_PATH/${f}.sql.gz"
		gzip -f  "$DST_PATH/${f}.sql"
	done

	return $Ret
}

TablesSet()
{
	db="$1"
	ext=$2
	set_type=$3

	`echo "use '${db}'; show tables;" | /usr/bin/mysql > /tmp/table_list.${ext}`
	cat /tmp/table_list.${ext} | while read line
	do
		if [ "${line}" = "Tables_in_${db}" ]; then
			continue;
		fi
		if [ ${set_type} = "rename" ]; then
			echo "use '${db}'; rename table \`${line}\` to \`${line}_${ext}\`;" | /usr/bin/mysql
		elif [ ${set_type} = "recover" ]; then
			ori_table=${line%_${ext}}
			echo "use '${db}'; rename table \`${line}\` to \`${ori_table}\`;" | /usr/bin/mysql
		elif [ ${set_type} = "drop_ori" ]; then
			# check if ${line} is the original table
			echo "${line}" | grep -q "_${ext}"
			if [ $? -eq 0 ]; then
				echo "use '${db}'; drop table \`${line}\`;" | /usr/bin/mysql
			fi
		elif [ ${set_type} = "drop_new" ]; then
			# check if ${line} is the original table
			echo "${line}" | grep -q "_${ext}"
			if [ $? -eq 0 ]; then
				continue;
			fi
			echo "use '${db}'; drop table \`${line}\`;" | /usr/bin/mysql
		fi
	done
	rm /tmp/table_list.${ext}
}

DBSet()
{
	db="$1"
	ext=$2
	set_type=$3
	Ret=0

	case $3 in
		rename)
			RenameDB "${db}" "${db}_${ext}"
			Ret=$?
			;;
		recover)
			RenameDB "${db}_${ext}" "$db"
			Ret=$?
			;;
		drop_ori)
			ori_db=${db}_${ext}
			echo "drop database if exists \`${ori_db}\`;" | /usr/bin/mysql
			Ret=$?
			;;
		drop_new)
			echo "drop database if exists \`${db}\`;" | /usr/bin/mysql
			Ret=$?
			;;
	esac

	return $Ret
}

RenameDB()
{
	ori_dbname=$1
	new_dbname=$2
	Ret=0

	echo "drop database if exists \`${new_dbname}\`;" | /usr/bin/mysql
	echo "create database \`${new_dbname}\`;" | /usr/bin/mysql
	Ret=$?
	if [ ${Ret} -ne 0 ]; then
		logger -p 0 "MariaDB: RenamDB: Failed to create database ${new_dbname}"
		return ${Ret}
	fi
	
	/usr/bin/mysqldump --max-allowed-packet=512M -- ${ori_dbname} | /usr/bin/mysql -- ${new_dbname}
	Ret=$?
	if [ ${Ret} -ne 0 ]; then
		logger -p 0 "MariaDB: RenameDB: Failed to rename db from ${ori_dbname} to ${new_dbname}"
		echo "drop database if exists \`${new_dbname}\`;" | /usr/bin/mysql
		return ${Ret}
	fi
	
	echo "drop database if exists \`${ori_dbname}\`;" | /usr/bin/mysql
	return $Ret
}

RestoreDatabase()
{
	# Dump MySQL database
	Ret=0
	OVERWRITE_DB=${2}

	# MySQL database has not been enabled.
	if [ ! -d "${DATADIR}" ]; then
		return ${MYSQL_FAIL}
	fi

	time=`date +%s`
	RESTORE_RET=0
	echo -n > ${MYSQL_RCVR_DB_CONFLICT_LIST}
	echo -n > ${MYSQL_RCVR_DB_NEW_LIST}
	echo $1
	for i in `find "$1"/*.sql.gz -type f`
	do
		DB_NAME=`basename "$i"`
		DB_NAME=${DB_NAME%.sql.gz}

		DB_NAME_TMP=`echo $DB_NAME | sed -e 's/@00/\\\\x/g'`
		DB_NAME_TMP=`echo -e "$DB_NAME_TMP"`
		# if database is conflict, need to rename its tables
		if [ -d "${DATADIR}/${DB_NAME}" ]; then
			if [ ${OVERWRITE_DB} = "yes" ]; then
				DBSet "${DB_NAME_TMP}" ${time} "rename"
				if [ $? -ne 0 ]; then
					Ret=${MYSQL_FAIL};
					break;
				fi
				# record conflict restored db to temp file
				echo "${DB_NAME_TMP}" >> ${MYSQL_RCVR_DB_CONFLICT_LIST}
			else
				continue;
			fi
		else
			# record new restored db to temp file
			echo "${DB_NAME_TMP}" >> ${MYSQL_RCVR_DB_NEW_LIST}
		fi

		gunzip -c "$1/${DB_NAME}.sql.gz" | /usr/bin/mysql

		# check if database is restored sucessfully
		if [ $? -ne 0 ]; then
			Ret=${MYSQL_FAIL};
			break;
		fi
	done

	# Check If the restoration task is successful,
	# If failed, drop all new database and recover all tables in all conflict databases
	# If successful, drop all original tables in all conflct databases
	if [ ${Ret} -eq 0 ]; then
		# drop all original tables
		cat ${MYSQL_RCVR_DB_CONFLICT_LIST} | while read line
		do
			DBSet "${line}" ${time} "drop_ori"
		done
	else
		# drop all new databases
		cat ${MYSQL_RCVR_DB_NEW_LIST} | while read line
		do
			echo "drop database if exists ${line}" | /usr/bin/msyql
		done

		cat ${MYSQL_RCVR_DB_CONFLICT_LIST} | while read line
		do
			DBSet "${line}" ${time} "drop_new"
			DBSet "${line}" ${time} "recover"
		done
	fi

	rm -f ${MYSQL_RCVR_DB_CONFLICT_LIST}
	rm -f ${MYSQL_RCVR_DB_NEW_LIST}

	return $Ret
}

StartSingleUser()
{
	/usr/share/mysql/mysql.server start --skip-grant-tables --skip-networking --max-allowed-packet=512M
	for i in $(seq 15); do
		if ! /usr/share/mysql/mysql.server status; then
			sleep 1
			continue
		fi
		exit 0
	done
	exit 1
}

CallChpwHooks()
{
	# Execute hook files
	local user="$1"
	local pwfile="$2"
	local ret=0
	local hook
	for hook in "$HOOK_MARIADB_CHPW/"*; do
		if [ ! -x "$hook" ]; then
			continue
		fi
		if ! "$hook" "$user" "$pwfile"; then
			logger -p 0 "MariaDB: CallChpwHools: mariadb_chpw hook failed on '$hook' with dbuser '$user'"
			ret=1
		fi
	done
	return $ret
}

ResetPassword()
{
	touch /tmp/mysql_init.$$
	chown mysql:mysql /tmp/mysql_init.$$
	chmod 600 /tmp/mysql_init.$$
	cat > /tmp/mysql_init.$$ <<EOF
use mysql;
DELETE FROM user WHERE user='root';
GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' IDENTIFIED BY '' WITH GRANT OPTION;
EOF
	/usr/share/mysql/mysql.server start --init-file=/tmp/mysql_init.$$
	rm /tmp/mysql_init.$$
	CallChpwHooks root /dev/null
	return $?
}

case $1 in
	start)
		if [ -n "$SYNOPKG_PKG_STATUS" ]; then
			${DSM_INDEX_ADD} ${UI_PATH}/index.conf ${PKG_INDEXDB_PATH}/appindexdb
			${DSM_INDEX_ADD} ${UI_PATH}/helptoc.conf ${PKG_INDEXDB_PATH}/helpindexdb
		fi
		if /usr/share/mysql/mysql.server status; then
			exit 0
		fi
		[ -f /etc/my.cnf ] && /bin/mv /etc/my.cnf /usr/syno/etc/packages/MariaDB/my.cnf
		[ ! -L /etc/mysql/my.cnf ] && /bin/mv /etc/mysql/my.cnf /usr/syno/etc/packages/MariaDB/my.cnf
		DO_LINK
		HupPHPFPM
		ChkExecEnv
		ChkDB
		SetSynoInfo $RUN_MYSQL_KEY "yes"
		if [ xsingleuser = x"$2" ]; then
			StartSingleUser
		fi
		/usr/share/mysql/mysql.server start
		if ! /usr/share/mysql/mysql.server status >/dev/null 2>&1; then
			/bin/mv /usr/syno/etc/packages/MariaDB/my.cnf "/usr/syno/etc/packages/MariaDB/my.cnf.`/bin/date +%s`"
			/usr/syno/bin/synodsmnotify @administrators dsmnotify:system_event "Failed to start MariaDB with customized my.cnf, restart with default configuration."
			/usr/share/mysql/mysql.server start
		fi
		UpgradeDatabase
		/usr/share/mysql/mysql.server status
		;;
	stop)
		if [ -n "$SYNOPKG_PKG_STATUS" ]; then
			${DSM_INDEX_DEL} ${UI_PATH}/index.conf ${PKG_INDEXDB_PATH}/appindexdb
			${DSM_INDEX_DEL} ${UI_PATH}/helptoc.conf ${PKG_INDEXDB_PATH}/helpindexdb
		fi
		/bin/sed -i /mysql/d /etc/php/conf.d/extensions.ini
		HupPHPFPM
		ChkExecEnv
		if /usr/share/mysql/mysql.server status; then
			/usr/share/mysql/mysql.server stop
		fi
		;;
	restart)
		DO_LINK
		ChkExecEnv
		ChkDB
		/usr/share/mysql/mysql.server restart
		UpgradeDatabase
		;;
	upgradedatabase)
		if UpgradeDatabase "dont_show_msg" $2; then
			exit 0
		else
			exit 1
		fi
		;;
	reload)
		/usr/share/mysql/mysql.server reload
		;;
	status)
		if [ ! -d ${BINFOLDER} ]; then
			exit 150;
		fi
		/usr/share/mysql/mysql.server status
		;;
	chpw)
		CallChpwHooks "$2" "$3"
		return $?
		;;
	resetpassword)
		$0 stop
		ResetPassword
		Ret=$?
		$0 stop
		return $Ret
		;;
	backupdb)
		if [ -z "$2" -o -z "$3" ]; then
			echo "Usage: $1 backupdb destination_path backup_type[netbkp|localbkp]"
			return ${MYSQL_FAIL}
		fi
		BackupDatabase "$2" $3
		Ret=$?
		return $Ret
		;;
	restoredb)
		if [ -z "$2" -o -z "$3" ]; then
			echo "Usage: $1 restoredb source_path overwrite[yes|no]"
			return ${MYSQL_FAIL}
		fi
		RestoreDatabase "$2" $3
		Ret=$?
		return $Ret
		;;
esac
