#!/bin/sh
#
# Copyright (c) 2022-2023, Jesús Daniel Colmenares Oviedo <DtxdF@disroot.org>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice, this
#   list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright notice,
#   this list of conditions and the following disclaimer in the documentation
#   and/or other materials provided with the distribution.
#
# * Neither the name of the copyright holder nor the names of its
#   contributors may be used to endorse or promote products derived from
#   this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

lib_load "${LIBDIR}/check_func"
lib_load "${LIBDIR}/zfs"

logs_desc="Log management for jails."

logs_main()
{
	local command="$1"; shift

	if lib_check_empty "${command}"; then
		command="list"
	fi

	case "${command}" in
		list|read|remove|tail) ;;
		*) logs_usage; exit ${EX_USAGE} ;;
	esac

	logs_${command} "$@"
}

logs_list()
{
	local _o
	local file
	local type entity subtype log
	local opt_escape=1
	local opt_columns=1
	local opt_pretty=1

	while getopts ":eHp" _o; do
		case "${_o}" in
			e)
				opt_escape=0
				;;
			H)
				opt_columns=0
				;;
			p)
				opt_pretty=0
				;;
			*)
				logs_usage
				exit ${EX_USAGE}
				;;
		esac
	done
	shift $((OPTIND-1))
	
	file="$1"

	type=`printf "%s" "${file}" | awk -F/ '{print $1}'`
	entity=`printf "%s" "${file}" | awk -F/ '{print $2}'`
	subtype=`printf "%s" "${file}" | awk -F/ '{print $3}'`
	log=`printf "%s" "${file}" | awk -F/ '{print $4}'`

	if [ -n "${log}" ]; then
		keywords="LOG"

		if [ ! -f "${LOGDIR}/${type}/${entity}/${subtype}/${log}" ]; then
			lib_err ${EX_NOINPUT} "Log ${log} not found."
		fi
	elif [ -n "${subtype}" ]; then
		keywords="LOG"

		if [ ! -d "${LOGDIR}/${type}/${entity}/${subtype}" ]; then
			lib_err ${EX_NOINPUT} "Subtype ${subtype} not found."
		fi
	elif [ -n "${entity}" ]; then
		keywords="SUBTYPE\tLOG"

		if [ ! -d "${LOGDIR}/${type}/${entity}" ]; then
			lib_err ${EX_NOINPUT} "Entity ${entity} not found."
		fi
	elif [ -n "${type}" ]; then
		keywords="ENTITY\tSUBTYPE\tLOG"

		if [ ! -d "${LOGDIR}/${type}" ]; then
			lib_err ${EX_NOINPUT} "Type ${type} not found."
		fi
	else
		keywords="TYPE\tENTITY\tSUBTYPE\tLOG"

		if ! lib_zfs_mkdir "${LOGDIR}" "${ZFS_LOGS_NAME}"; then
			lib_err ${EX_IOERR} "Error creating ${LOGDIR}"
		fi
	fi

	if [ -n "${log}" ]; then
		basename "${LOGDIR}/${type}/${entity}/${subtype}/${log}"
	elif [ -n "${subtype}" ]; then
		(cd "${LOGDIR}/${type}/${entity}/${subtype}"; find . -depth 1 -maxdepth 4)
	elif [ -n "${entity}" ]; then
		(cd "${LOGDIR}/${type}/${entity}"; find . -depth 2 -maxdepth 4)
	elif [ -n "${type}" ]; then
		(cd "${LOGDIR}/${type}"; find . -depth 3 -maxdepth 4)
	else
		(cd "${LOGDIR}"; find . -depth 4 -maxdepth 4)
	fi |\
		sed -Ee 's#\./##' |\
		{
			if [ ${opt_columns} -eq 1 ]; then
				echo -e "${keywords}"
			fi
			while IFS= read -r file; do
				if [ ${opt_pretty} -eq 1 -o ${opt_escape} -eq 1 ]; then
					file=`printf "%s" "${file}" | sed -Ee 's/\t/<TAB>/g'`
				fi

				file=`printf "%s" "${file}" | tr '/' '\t'`

				printf "%s\n" "${file}"
			done
		} | \
			if [ ${opt_pretty} -eq 1 ]; then
				column -ts $'\t'
			else
				cat
			fi
}

logs_read()
{
	local errlevel=0
	local file="$1"; shift
	if lib_check_empty "${file}"; then
		logs_usage
		exit ${EX_USAGE}
	fi

	file=`_logs_get_filename "${file}"`
	errlevel=$?
	if [ ${errlevel} -ne 0 ]; then
		exit ${errlevel}
	fi

	${PAGER:-less} "$@" -- "${file}"
}

logs_remove()
{
	local _o
	local use_glob=0

	while getopts ":g" _o; do
		case "${_o}" in
			g)
				use_glob=1
				;;
			*)
				logs_usage
				exit ${EX_USAGE}
				;;
		esac
	done
	shift $((OPTIND-1))

	local file="$1"; shift
	if lib_check_empty "${file}"; then
		logs_usage
		exit ${EX_USAGE}
	fi

	if [ "${file}" = "all" ]; then
		if [ ! -d "${LOGDIR}" ]; then
			lib_err ${EX_NOINPUT} -- "${LOGDIR}: does not exist."
		fi

		if [ "${ENABLE_ZFS}" != "0" ]; then
			if ! lib_zfs_rrmfs -fR "${ZFS_LOGS_NAME}"; then
				lib_err ${EX_IOERR} "Error destroying ${ZFS_LOGS_NAME}"
			fi
		else
			rm -rf "${LOGDIR}"
		fi

		return $?
	fi

	local file_count

	file=`printf "%s" "${file}" | grep -oEe '([^/]+)/?'`
	if lib_check_empty "${file}"; then
		logs_usage
		exit ${EX_USAGE}
	fi

	file_count=`printf "%s\n" "${file}" | wc -l`
	if [ ${file_count} -gt 4 ]; then
		logs_usage
		exit ${EX_USAGE}
	fi

	file=`printf "%s" "${file}" | tr -d '\n'`

	if [ ${file_count} -le 3 ]; then
		if [ ! -d "${LOGDIR}/${file}" ]; then
			lib_err ${EX_NOINPUT} -- "${file}: does not exist."
		fi

		if [ "${ENABLE_ZFS}" != "0" ]; then
			if ! lib_zfs_rrmfs -fR "${ZFS_LOGS_NAME}/${file}"; then
				lib_err ${EX_IOERR} "Error destroying ${ZFS_LOGS_NAME}/${file}"
			fi
		else
			rm -rf "${LOGDIR}/${file}"
		fi
	elif [ ${use_glob} -eq 1 ]; then
		local bs_file
		bs_file=`basename -- "${file}"` || exit $?

		local bs_dir
		bs_dir=`dirname -- "${file}"` || exit $?

		if [ ! -d "${LOGDIR}/${bs_dir}" ]; then
			lib_err ${EX_NOINPUT} -- "${bs_dir}: does not exist."
		fi

		local match
		match=`find "${LOGDIR}/${bs_dir}" -name "${bs_file}" -mindepth 1 -maxdepth 1` || exit $?

		if lib_check_empty "${match}"; then
			lib_err ${EX_NOINPUT} -- "${file}: no matches found."
		fi

		printf "%s\n" "${match}" | while IFS= read -r logfile; do
			rm -f "${logfile}"
		done
	else
		if [ ! -f "${LOGDIR}/${file}" ]; then
			lib_err ${EX_NOINPUT} -- "${file}: does not exist."
		fi

		rm -rf "${LOGDIR}/${file}"
	fi

	return $?
}

logs_tail()
{
	local errlevel=0
	local file="$1"; shift
	if lib_check_empty "${file}"; then
		logs_usage
		exit ${EX_USAGE}
	fi

	file=`_logs_get_filename "${file}"`
	errlevel=$?
	if [ ${errlevel} -ne 0 ]; then
		exit ${errlevel}
	fi

	tail "$@" -- "${file}"
}

_logs_get_filename()
{
	local file="$1"
	if [ -z "${file}" ]; then
		lib_err ${EX_USAGE} "_logs_get_filename file"
	fi

	file=`printf "%s" "${file}" | grep -Eo '[^/]+/[^/]+/[^/]+/[^/]+'`
	if [ `printf "%s" "${file}" | tr '/' '\n' | wc -l` -ne 3 ]; then
		lib_err ${EX_DATAERR} "The syntax should be \`type/entity/subtype/log\`"
	fi

	file="${LOGDIR}/${file}"
	if [ ! -f "${file}" ]; then
		lib_err ${EX_NOINPUT} "Cannot find the log: ${file}"
	fi

	printf "%s" "${file}"
}

logs_help()
{
	man 1 appjail-logs
}

logs_usage()
{
	cat << EOF
usage: logs
       logs list [-eHp] <type>[/<entity>[/<subtype>[/<log>]]]
       logs read <type>/<entity>/<subtype>/<log> [<args> ...]
       logs remove all
       logs remove [-g] <type>[/<entity>[/<subtype>[/<log>]]]
       logs tail <type>/<entity>/<subtype>/<log> [<args> ...]
EOF
}
