#! /bin/sh
### BEGIN INIT INFO
# Provides:          swupdate
# Required-Start:    $local_fs
# Should-Start:
# Required-Stop:     $local_fs
# Should-Stop:
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Start swupdate application
### END INIT INFO

# The definition of actions: (From LSB 3.1.0)
# start         start the service
# stop          stop the service
# restart       stop and restart the service if the service is already running,
#               otherwise start the service
# try-restart	restart the service if the service is already running
# reload	cause the configuration of the service to be reloaded without
#               actually stopping and restarting the service
# force-reload	cause the configuration to be reloaded if the service supports
#               this, otherwise restart the service if it is running
# status	print the current status of the service

# The start, stop, restart, force-reload, and status actions shall be supported
# by all init scripts; the reload and the try-restart actions are optional

# PATH should only include /usr/* if it runs after the mountnfs.sh script

DESC="swupdate"
NAME="swupdate"
DAEMON=/usr/bin/swupdate
PIDFILE=/var/run/${NAME}.pid
NETWORK_INITIALIZED_FLAG=/tmp/network-initialized

SWUPDATE_ARGS="-v -k /home/root/verified-boot.pem"
TFTP=/tmp/dhcpc-tftp
USB=/mnt
USBIMAGE="$USB/image.swu"

WEBSRV_DOCUMENT_ROOT="/www"
WEBSRV_PORT="80"

USB_DISCOVERY_DELAY="2"

REPORT_STATUS_INIT="initialize"

REPORT_STATUS_COPY="copy"
REPORT_STATUS_COPY_BEGIN="begin"
REPORT_STATUS_COPY_PROGRESS="progress"
REPORT_STATUS_COPY_SUCESS="success"
REPORT_STATUS_COPY_FAILED="failed"

REPORT_STATUS_START="start"
REPORT_STATUS_START_URI="uri"
REPORT_STATUS_START_USB="usb"
REPORT_STATUS_START_FILE="file"
REPORT_STATUS_START_WEBSERVER="webserver"

REPORT_STATUS_EXIT="exit"
REPORT_STATUS_EXIT_SUCESS="success"
REPORT_STATUS_EXIT_FAILED="failed"
REPORT_STATUS_EXIT_FAILED_NETWORK="network_error"
REPORT_STATUS_EXIT_FAILED_SWUPDATE="swupdate_error"

. /etc/init.d/functions || exit 1

# Exit if the package is not installed
[ -x "$DAEMON" ] || exit 0

# Read configuration variable file if it is present
[ -r /etc/default/${NAME} ] && . /etc/default/${NAME}

debug()
{
	echo "[swupdate-init]: $@"
}


# $1 - name of the kernel command line argument
# $2 - fallback value in case $1 is not present
read_flag_from_cmdline()
{
	local FLAG=$(cat /proc/cmdline | tr ' ' '\n' | grep "$1" | sed 's|.*=||')
	if [ -z "${FLAG}" ]; then
		debug "INFO: unable to read $1 from the kernel command line, using fallback to '$2'!"
		FLAG="$2"
	fi

	# shell's true means 0, so we invert
	! return "${FLAG}"
}

factory_state()
{
	read_flag_from_cmdline "factory_state" "0"
	return $?
}

user_request()
{
	read_flag_from_cmdline "usb_update_req" "0"
	USER_REQUEST=$?

	# IMPORTANT NOTE: the environmental variable is TRUE when 1,
	# but in the shell script environment we treat TRUE as a 0 !!!

	# USB updates can also be triggered through an environmental
	# variable on ARCAM devices which lack USB update buttons
	if [ "$USER_REQUEST" == "1" ]; then # if FALSE
		local USER_REQUEST_ENV=$(fw_printenv usb_update_req 2>/dev/null | sed 's/^[^=]*=//')
		if [ "$USER_REQUEST_ENV" == "1" ]; then # if TRUE
			debug "INFO: found USB upgrade flag in the environmental variables!"
			USER_REQUEST="0"
		else
			debug "ERROR: no USB upgrade flag found in the environmental variables!"
		fi
	fi

	# return 1 on FALSE, return 0 on TRUE
	return "${USER_REQUEST}"
}

secure_board()
{
	read_flag_from_cmdline "secure_board" "1"
	return $?
}

factoryswu()
{
	read_flag_from_cmdline "factoryswu" "0"
	return $?
}

imageswu_size()
{
	local IMAGESWU_SIZE=$(cat /proc/cmdline | tr ' ' '\n' | grep imageswu_size | sed 's|.*=||')
	echo -n "${IMAGESWU_SIZE}"
}

check_usb()
{
	# Wait for the USB device to become available. From experience, some USB drives are
	# enumerated just a few hundred milliseconds after the following check is executed.
	sleep ${USB_DISCOVERY_DELAY}

	# Check if we have at least one block device on the SCSI bus, if that is
	# the case we sleep again for USB_DISCOVERY_DELAY, since with some USB drives
	# we observed that they are too slow to appear as /dev/sdX
	for i in `ls -1 /sys/bus/scsi/devices/ | grep -e '^.*:.*:.*:.*'`; do
		if [ `cat /sys/bus/scsi/devices/${i}/type` -eq 0 ]; then
			# scsi device is a disk (not CD-ROM), wait and break out of loop
			sleep ${USB_DISCOVERY_DELAY}
			break
		fi
	done

	for i in $(blkid | grep /dev/sd | cut -d ':' -f1); do
		if mount -o ro $i ${USB} 2>/dev/null; then
			debug "INFO: $i mounted"
		else
			debug "INFO: $i is not mounted, continuing..."
			continue
		fi

		if [ -f "${USBIMAGE}" ]; then
			debug "INFO: Found USB with image: $i"
			SRC_FILE="${USBIMAGE}"
			return 0
		fi

		umount ${USB} 2> /dev/null
	done

	debug "INFO: No USB with update file found"
	return 1
}

load_url()
{
	SRC_URI="$(fw_printenv ${1} 2>/dev/null | sed 's/^[^=]*=//')"
}

cp_progress()
{
	[ -f "$1" ] || return 1

	local SIZE=$(stat -c %s "$1")
	report_status.sh ${REPORT_STATUS_COPY} ${REPORT_STATUS_COPY_BEGIN}

	# the redirections here cause the data file (stdout) to be written to $2 while progress (stderr) is piped to the while loop
	pv -n "$1" 2>&1 > "$2" | while read PERC; do
		debug "INFO: loading: $PERC%" 1>&2
		report_status.sh ${REPORT_STATUS_COPY} ${REPORT_STATUS_COPY_PROGRESS} ${PERC}
	done

	if [[ $SIZE -eq $(stat -c %s "$2") ]]; then
		debug "INFO: Loading from flash successful"
		report_status.sh ${REPORT_STATUS_COPY} ${REPORT_STATUS_COPY_SUCCESS}
		return 0
	else
		debug "ERROR: Error while loading from flash!!!"
		report_status.sh ${REPORT_STATUS_COPY} ${REPORT_STATUS_COPY_FAILED}
		return 1
	fi
}

load_local_file()
{
	# uri format ubi://ubi0:name/fileName
	# uri format blk://partition/fileName
	local PARTITION=$(echo "${1}" | cut -d '/' -f 3)
	local FILENAME=$(echo "${1}" | cut -d '/' -f 4)
	local MOUNTPOINT="/mnt/$(echo ${PARTITION} | tr ':' '_')"

	if nsdk-flash.sh mount "${PARTITION}" "${MOUNTPOINT}"; then
		debug "INFO: Loading ${1} from flash to /tmp/${FILENAME}"
		if cp_progress "${MOUNTPOINT}/${FILENAME}" "/tmp/${FILENAME}"; then
			nsdk-flash.sh unmount "${PARTITION}" "${MOUNTPOINT}"
			SRC_FILE="/tmp/${FILENAME}"
			return 0
		fi
		nsdk-flash.sh unmount "${PARTITION}" "${MOUNTPOINT}"
	fi
	return 1
}

load_versions_and_revision()
{
	local UBOOT_VERSION=""
	local SWUFIT_VERSION=""
	local FIT_VERSION=""
	local ROOTFS_VERSION=""

	if factory_state || factoryswu; then
		debug "INFO: board is in factory or factoryswu state, forcing install on everything"
	else
		# load installed versions from u-boot environment for swupdate
		UBOOT_VERSION=$(fw_printenv uboot_vers 2>/dev/null | sed 's/^[^=]*=//')
		SWUFIT_VERSION=$(fw_printenv swu-fit_vers 2>/dev/null | sed 's/^[^=]*=//')
		FIT_VERSION=$(fw_printenv fit_vers 2>/dev/null | sed 's/^[^=]*=//')
		ROOTFS_VERSION=$(fw_printenv rootfs_vers 2>/dev/null | sed 's/^[^=]*=//')

		# If fail flag is set or user request is active, force rootfs and FIT flashing
		if [ "$(sfuflags -f fail)" = "1" ] || user_request; then
			if secure_board; then
				debug "INFO: FAIL flags is set or USB update request is active, enabling rootfs and FIT reinstall"
				FIT_VERSION="force-reinstall-if-${FIT_VERSION}"
				ROOTFS_VERSION="force-reinstall-if-${ROOTFS_VERSION}"
			else
				debug "INFO: FAIL flags is set or USB update request is active, forcing rootfs and FIT flashing"
				FIT_VERSION=force-install
				ROOTFS_VERSION=force-install
			fi
		fi
	fi;

	echo "uboot ${UBOOT_VERSION:-force-install}" > /etc/sw-versions
	echo "swu-fit ${SWUFIT_VERSION:-force-install}" >> /etc/sw-versions
	echo "fit ${FIT_VERSION:-force-install}" >> /etc/sw-versions
	echo "rootfs ${ROOTFS_VERSION:-force-install}" >> /etc/sw-versions

	# Optionally overwrite hw revision file
	# If board is NOT locked, we should use generic board name here (something like GenericStream810),
	# which should already be written in hwrevision. If it IS locked, we should use the real machine
	# name (or an machine-specific override).
	if secure_board; then
		if [ -f /etc/hwrevision-locked ]; then
			HW_REV="$(cat /etc/hwrevision-locked)"
		else
			debug "ERROR: missing /etc/hwrevision-locked in swu-fit (fallback to /etc/machine)"
			HW_REV="$(cat /etc/machine)"
		fi
	else
		HW_REV="$(cat /etc/hwrevision-unlocked)"
	fi
	# Add module version at the end so downgrades are avoided with incompatible modules
	if [ -f /proc/device-tree/sue/moduleversion ]; then
		HW_REV="${HW_REV}-V$(cat /proc/device-tree/sue/moduleversion)"
	fi

	echo "${HW_REV}" > /etc/hwrevision
}

initialize_network()
{
	if [ -e ${NETWORK_INITIALIZED_FLAG} ]; then
		debug "INFO: Network is already initialized"
		return 0
	fi

	debug "INFO: Initializing network"

	# in factory state we can't attach the UBI as that will write some headers to flash
	if ! factory_state; then
		nsdk-flash.sh mount_settings
	fi

	if simple-networking.sh; then
		debug "INFO: Network initialized"
		nsdk-flash.sh unmount_settings
		touch ${NETWORK_INITIALIZED_FLAG}
		return 0
	else
		debug "ERROR: Unable to initialize network"
		return 1
	fi
}

reboot_to_application()
{
	debug "INFO: Clearing update and fail flags"
	sfuflags -f update -v 0
	sfuflags -f fail -v 0

	# report exit status
	report_status.sh ${REPORT_STATUS_EXIT} "$@"

	debug "INFO: rebooting to application"
	# If a lot of data is printed (usually by the swupdate) at once, it may not have been printed yet
	# (when using UART). This can happen if for example swupdate exits due to error when updating
	# from a USB drive. Because of this, we are adding 1 second sleep here. It should be long enough
	# to ensure all data is written, but short enough not to be an inconvenience. 
	sleep 1
	reboot -f
	exit 1
}

clear_settings_partition()
{
	debug "INFO: Clearing settings partition"
	nsdk-flash.sh format_settings
}

_tftp()
{
	cat "${TFTP}" 2> /dev/null | egrep "^$1 " | sed -e 's/[^\ ]*\ //'
}

#
# Function that starts the daemon/service
#
do_start() {
	# Make sure that a running watchdog is disabled
	echo V > /dev/watchdog

	# check if swupdate is already running
	local pid=`pidofproc swupdate`
	if [ -n "${pid}" ]; then
		debug "INFO: Swupdate is already running on pid ${pid}."
		exit 1
	fi

	# source all files from /etc/swupdate/conf.d and /usr/lib64/swupdate/conf.d/
	# A file found in /etc replaces the same file in /usr
	for f in `(test -d /usr/lib64/swupdate/conf.d/ && ls -1 /usr/lib64/swupdate/conf.d/; test -d /etc/swupdate/conf.d && ls -1 /etc/swupdate/conf.d) | sort -u`; do
		if [ -f /etc/swupdate/conf.d/$f ]; then
			. /etc/swupdate/conf.d/$f
		else
			. /usr/lib64/swupdate/conf.d/$f
		fi
	done

	report_status.sh ${REPORT_STATUS_INIT}

	# Harman specific: inform the MCU that the AVATAR upgrade has been started [JBLL75MS-884]
	# Set State AvatarUpgradeStatus (0x30B) with uint8 value of AvatarUpgradeInProgress (0x4)
	hostlink_cli -C set_state 0x030B uint8 4 -T 1 &

	# If image is downloaded from a remote source, swupdate stores its contents in /tmp in order
	# to verify the signature before applying them. However, /tmp by default uses half the available RAM
	# and it is not enough on 256MB modules -> increase it to all currently available RAM - 20MB.
	mount -o remount,size=$(($(free | grep Mem | awk '{ print $4 }') / 1024 - 20))M /var/volatile/

	load_versions_and_revision

	# Determine the update source
	# If in factoryswu state:
	#	In this state we load an USB gadget driver so the module appears as an USB mass storage device.
	#	The flashing PC/device will write the full image.swu directly to the new mass storage device with
	#	dd, after this is done the flashing PC/device will eject the mass storage device and the swupdate
	#	script will detect the ejection. After ejection is done the file, which was used for the mass
	#	storage device, will be directly used for the swupdate.
	#
	# If in factory state:
	#	In this state, we will always check for USB image. If not found,
	#	we will run TFTP.
	#
	# If USB update request is active:
	#	It can only be active if Bootcount = 1. In this state,
	#	we check whether or not we find update image on the USB.
	#	If there is a problem, we will start webserver for interactive use.
	#
	# If updating from the application (Bootcount = 1):
	#	'update_url' must be set. If the url contains local://update or
	#	is empty, webserver is started. If 'update_url_cache' is set,
	#	cached url is used instead. If cached url is ubi://<partition>/<file_name>, or
	#	blk://<partition>/<file_name> update from local file is performed, else the update from web
	#	is executed
	#
	# If recovery from crash (Bootcount > 1)
	#	If Bootcount is 2 or 3 or 4, reflash from 'update_url_cache' or 'update_url'
	#	If Bootcount is 5 or 6, clear settings partition and reflash from 'update_url_cache' or 'update_url'
	#	If Bootcount greather then 6, starts local webserver

	# If set, settings partition is cleared at the end of update
	CLEAR_SETTINGS=0

	# determine update source
	if factoryswu; then
		debug "INFO: factoryswu-state detected"
		IMAGESWU_SIZE=$(imageswu_size)

		# Use 1M block_size
		local block_size=$((1000 * 1000))
		local imageswu_blocks=$((($IMAGESWU_SIZE / $block_size) + 1))
		dd if=/dev/zero of=/tmp/image.swu bs=$block_size count=$imageswu_blocks
		modprobe g_mass_storage file=/tmp/image.swu removable=y

		# When the mass storage device is plugged in, the sysfs path below will have the
		# path of the backing file passed in modprobe. When the mass storage device is
		# ejected on the host side this file will become empty when the removable=y flag
		# was set.
		if [ -f /sys/devices/platform/ff400000.dwc2_a/gadget/lun0/file ]; then
			debug "INFO: Stream1832 is waiting for mass storage device to be ejected on host side"
			while [ $(wc -l < /sys/devices/platform/ff400000.dwc2_a/gadget/lun0/file) != "0" ]; do
				usleep 100000
			done
		elif [ -f /sys/devices/platform/soc@0/32c00000.bus/32e40000.usb/ci_hdrc.0/gadget/lun0/file ]; then
			debug "INFO: Stream195x is waiting for mass storage device to be ejected on host side"
			while [ $(wc -l < /sys/devices/platform/soc@0/32c00000.bus/32e40000.usb/ci_hdrc.0/gadget/lun0/file) != "0" ]; do
				usleep 100000
			done
		else
			debug "ERROR: unable to wait for mass storage device to be ejected on host side!"
		fi

		debug "INFO: mass storage device was ejected removing module"
		modprobe -r g_mass_storage
		SRC_FILE="/tmp/image.swu"
	elif factory_state; then
		debug "INFO: Factory-state detected - checking USB/TFTP"

		if ! check_usb; then
			if initialize_network; then
				debug "INFO: Trying TFTP"

				TFTP_IP=`_tftp dhcp_ip`
				TFTP_SERVERIP=`_tftp dhcp_serverip`
				TFTP_ROOTPATH=`_tftp dhcp_rootpath`
				TFTP_BOOTFILE=`_tftp dhcp_bootfile`

				# default bootfile
				[ -z "$TFTP_BOOTFILE" ] && TFTP_BOOTFILE="image.swu"

				# default serverip - replace last part of our own ip with ".10"
				[ -z "$TFTP_SERVERIP" ] && TFTP_SERVERIP=`echo $TFTP_IP | sed -e 's/\.[0-9]*$/\.10/'`

				SRC_URI="tftp://$TFTP_SERVERIP/$TFTP_ROOTPATH$TFTP_BOOTFILE"
				debug "INFO: Found TFTP: $SRC_URI"
			else
				# We are in factory state, no USB was plugged in and we were
				# unable to initialize network. What should we do now?
				debug "ERROR: Unable to initialize network - FAIL."
			fi
		fi
	else
		debug "INFO: NOT in factory state - checking USB if user request is active, otherwise trying remote URL"

		if user_request; then
			if ! check_usb; then
				# If no USB upgrade can be used
				debug "ERROR: no USB upgrade found, rebooting back to application"
				reboot_to_application ${REPORT_STATUS_EXIT_FAILED} ${REPORT_STATUS_EXIT_FAILED_SWUPDATE}
			fi
		else
			BOOTCOUNT=$(sfuflags -f boot_count)
			CHECK_URL=1

			if [ "${BOOTCOUNT}" -eq 5 ] || [ ${BOOTCOUNT} -eq 6 ]; then
				# clear settings partition
				debug "INFO: boot count is ${BOOTCOUNT}: clear settings partition"
				CLEAR_SETTINGS=1
			fi

			if [ "${BOOTCOUNT}" -ge 7 ]; then
				debug "INFO: boot count is ${BOOTCOUNT}: starting local update"
				# backup to local update
				CHECK_URL=0
			fi

			if [ "${CHECK_URL}" -eq 1 ]; then
				# normal network update from main app
				load_url "update_url_cache"
				if [ -z "${SRC_URI}" ]; then
					debug "INFO: update_url_cache is empty, using update_url instead"
					load_url "update_url"
				# check update URI starts with ubi:// or "blk://"
				elif [[ "${SRC_URI:0:6}" == "ubi://" || "${SRC_URI:0:6}" == "blk://" ]]; then
					# load file to RAM
					debug "INFO: update_url_cache is ubi/blk, starting update from file"
					if load_local_file "${SRC_URI}"; then
						# clear URI, update from local file
						SRC_URI=""
					else
						debug "ERROR: unable to load image from ${SRC_URI}, using update url"
						load_url "update_url"
					fi
				fi
			fi
		fi
	fi

	if ! secure_board; then
		# if board is not yet locked, we allow flashing images that fail verifications,
		# because when boards are shipped to the customer from factory, they contain different
		# verification key than the customer uses
		debug "INFO: board is NOT locked, ignoring signed image verification failures"
		SWUPDATE_ARGS="${SWUPDATE_ARGS} -I"
	else
		debug "INFO: board IS locked, honoring signed image verification failures"
		# debug "INFO: board IS locked, disabling downgrades"
		# SWUPDATE_ARGS="${SWUPDATE_ARGS} -n"
	fi

	STATUS_REPORT_SOURCE=${REPORT_STATUS_START_WEBSERVER}
	if [ -n "${SRC_FILE}" ]; then
		debug "INFO: Starting update from file \"${SRC_FILE}\""
		SWUPDATE_ARGS="${SWUPDATE_ARGS} -i ${SRC_FILE}"

		# check if the file is stored on usb or not
		if [ ${SRC_FILE} = ${USBIMAGE} ]; then
			STATUS_REPORT_SOURCE=${REPORT_STATUS_START_USB}
		else
			STATUS_REPORT_SOURCE=${REPORT_STATUS_START_FILE}
		fi
	else
		# initialize network first
		if initialize_network; then
			if [ -n "${SRC_URI}" ]; then
				# SRC_URI is valid. Check if it has a special meaning (e. g. local update)
				if [ "${SRC_URI}" = "local://update" ]; then
					debug "INFO: Local update requested, starting webserver"
					SRC_URI=""
				else
					debug "INFO: Starting update from URL \"${SRC_URI}\""
					STATUS_REPORT_SOURCE=${REPORT_STATUS_START_URI}
				fi
			else
				debug "INFO: No valid update source found, starting webserver"
			fi
		else
			debug "ERROR: Unable to initialize network, rebooting back to application"
			[ $CLEAR_SETTINGS -eq 1 ] && clear_settings_partition
			reboot_to_application ${REPORT_STATUS_EXIT_FAILED} ${REPORT_STATUS_EXIT_FAILED_NETWORK}
		fi
	fi

	[ $CLEAR_SETTINGS -eq 1 ] && clear_settings_partition

	# run in a subshell, so we are able to work with the machine while swupdate runs
	(
		AES_KEY_IV=$(fw_printconst enc_key 2>/dev/null | sed 's/^[^=]*=//')
		if [ -n "${AES_KEY_IV}" ]; then
			echo "${AES_KEY_IV}" > /tmp/aes_key
			SWUPDATE_ARGS="${SWUPDATE_ARGS} -K /tmp/aes_key"
		fi
		# While expanding variables, shell won't honor quotes and would split the -w argument parameter into
		# multiple paremeters. That's why we have to pass them outside of SWUPDATE_ARGS :(
		# Same for the download URL.
		report_status.sh ${REPORT_STATUS_START} ${STATUS_REPORT_SOURCE}
		if [ -n "${SRC_URI}" ]; then
			# Get the date from ntp pool if we ever do cold boots into swu-fit.
			# Needed for checking HTTPS certificate validity.
			/usr/sbin/sntp -S pool.ntp.org
			swupdate ${SWUPDATE_ARGS} -d "-u ${SRC_URI} -r 3 -t 30" -w "-r ${WEBSRV_DOCUMENT_ROOT} --p ${WEBSRV_PORT}" &
		else
			swupdate ${SWUPDATE_ARGS} -w "-r ${WEBSRV_DOCUMENT_ROOT} --p ${WEBSRV_PORT}" &
		fi
		EXIT_STATUS=${REPORT_STATUS_EXIT_SUCESS}
		if wait $!; then
			debug "INFO: swupdate process finished successfully"
		else
			EXIT_CODE=$?
			# Check if stopped manually. In this case, error code is 128 + signal number (KILL = 9)
			if [ ${EXIT_CODE} = 137 ]; then
				debug "INFO: swupdate process STOPPED"
				exit 0
			else
				EXIT_STATUS="${REPORT_STATUS_EXIT_FAILED} ${REPORT_STATUS_EXIT_FAILED_SWUPDATE}"
				debug "ERROR: swupdate process FAILED"
			fi
		fi

		reboot_to_application $EXIT_STATUS
	) &
}

#
# Function that stops the daemon/service
#
do_stop() {
	debug "INFO: Stopping swupdate"
	kill -KILL $(pidof swupdate)

	debug "INFO: Clearing update and fail flags"
	sfuflags -f update -v 0
	sfuflags -f fail -v 0
	fw_setenv usb_update_req ""
}

#
# Function that sends a SIGHUP to the daemon/service
#
do_reload() {
	local pid status

	status=0
	# If the daemon can reload its configuration without
	# restarting (for example, when it is sent a SIGHUP),
	# then implement that here.
	pid=`pidofproc ${NAME}` || status=$?
	case $status in
	0)
		echo "Reloading $DESC ..."
		kill -s 1 $pid || exit $?
		;;
	*)
		echo "$DESC is not running; none reloaded." >&2
		;;
	esac
	exit $status
}


#
# Function that shows the daemon/service status
#
status_of_proc () {
	local pid status

	status=0
	# pidof output null when no program is running, so no "2>/dev/null".
	pid=`pidofproc ${NAME}` || status=$?
	case $status in
	0)
		echo "$DESC is running ($pid)."
		exit 0
		;;
	*)
		echo "$DESC is not running." >&2
		exit $status
		;;
	esac
}

case "$1" in
start)
	do_start
	;;
stop)
	do_stop || exit $?
	;;
status)
	status_of_proc
	;;
restart)
	# Always start the service regardless the status of do_stop
	do_stop
	do_start
	;;
try-restart|force-reload)
	# force-reload is the same as reload or try-restart according
	# to its definition, the reload is not implemented here, so
	# force-reload is the alias of try-restart here, but it should
	# be the alias of reload if reload is implemented.
	#
	# Only start the service when do_stop succeeds
	do_stop && do_start
	;;
#reload)
	# If the "reload" action is implemented properly, then let the
	# force-reload be the alias of reload, and remove it from
	# try-restart|force-reload)
	#
	#do_reload
	#;;
*)
	echo "Usage: $0 {start|stop|status|restart|try-restart|force-reload}" >&2
	exit 3
	;;
esac
