#!/bin/sh

# apt-dater - terminal-based remote package update manager
#
# Implementation of the protocol described in
#   https://github.com/DE-IBH/apt-dater-host/blob/master/doc/ADP-0.6
# using Busybox ash, awk and sed for use with Alpine Linux
#
# Author:
#   Henrik Riomar <henrik.riomar@gmail.com>
#
# Copyright Holder:
#   2016, 2017 (C) Henrik Riomar
#
# License:
#   This program is free software; you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation; either version 2 of the License, or
#   (at your option) any later version.
#
#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
#
#   You should have received a copy of the GNU General Public License
#   along with this package; if not, write to the Free Software
#   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
#

ADP_VERSION="0.6"
ROOT_CMD="sudo"
APK_CMD="/sbin/apk"
VIRT_WHAT_CMD="/usr/sbin/virt-what --test-root=/"

err=255 # exit code returned by Perl from die()

# LSBREL: ${Distri}|{Version}|${Codename}
get_lsbrel()
{
	name=$(awk -F= '/^ID/ {print $NF}' /etc/os-release)
	version=$(awk -F= '/^VERSION_ID/ {print $NF}' /etc/os-release)
	echo "LSBREL: ${name}|${version}|"
}

# PRL: ${URI}
get_prl()
{
	sed -n 's/^http/PRL: http/p' /etc/apk/repositories
}

# check if a pkg is held back
is_held()
{
	grep -E -q -e ^$1\= -e ^$1\< /etc/apk/world
}

# check if $1 > $2
newer_then()
{
	[ x"$($APK_CMD version -t $1 $2)" = x">" ] && return 0

	return 1
}

# STATUS: ${Package}|${InstVersion}|${Status}...
get_pkg_stat()
{
	tmp=$(mktemp)
	$APK_CMD version -v | grep -v "^Installed:" > $tmp
	$APK_CMD info | while read -r pkg
	do
		line=$(grep -E "^${pkg}-[0-9]" $tmp)
		new_ver=$(echo $line | sed 's/ @.\+$//' | awk '{print $NF}')
		cur_ver=$(echo $line | awk '{print $1}' | sed "s/$pkg-//")
		if [ x"$new_ver" = x"$cur_ver" ]; then
			echo "STATUS: $pkg|$cur_ver|i"
		elif is_held $pkg; then
			echo "STATUS: $pkg|$cur_ver|h"
		elif [ x"$new_ver" = x"?" ]; then
			echo "STATUS: $pkg|$cur_ver|x"
		elif newer_then $new_ver $cur_ver; then
			echo "STATUS: $pkg|$cur_ver|u=$new_ver"
		else
			# newer installed ($cur_ver) than available,
			# ADP 0.6 does not describe this case
			# x should be suitable for now.
			echo "STATUS: $pkg|$cur_ver|x"
		fi
	done
	rm $tmp
}

# VIRT: ${Name}
get_virt()
{
	virt=$(dmesg | awk '/Hypervisor detected:/ {print $NF}')
	if [ -n "$virt" ]; then
		echo "VIRT: $virt"
	else
		virt=$($VIRT_WHAT_CMD 2> /dev/null)
		ret=$?
		if [ -z "$virt" ]; then
			[ $ret -eq 0 ] && echo "VIRT: Physical" || echo "VIRT: Unknown"
		else
			echo "VIRT: $(echo $virt | awk '{print $1}')"
		fi
	fi
}

# UNAME: ${KERNEL-NAME}|${MACHINE}
get_uname()
{
	echo "UNAME: $(uname -s)|$(uname -m)"
}

# KERNELINFO: ${Code} ${Release}
get_kern()
{
	# 0 - latest running
	# 1 - reboot
	# 9 - Unknown
	running=$(uname -r)
	flavor=$(uname -r | awk -F- '{print $NF}')
	vmlinuz="/boot/vmlinuz-$flavor"
	if [ -r $vmlinuz ]; then
		installed=$($APK_CMD info --who-owns $vmlinuz | grep -E -o "[0-9]+.[0-9]+.[0-9]+-r[0-9]")
		with_r=$(echo $running | sed -e "s/[0-9]-${flavor}$/r&/g" | sed "s/-${flavor}//")
		if [ x"$($APK_CMD version -t $with_r $installed)" = x'=' ]; then
			echo "KERNELINFO: 0 $running"
		else
			echo "KERNELINFO: 1 $running"
		fi
	else
		echo "KERNELINFO: 9 $running"
	fi

}

# FORBID: ${Operations}
check_forbid()
{
	echo "FORBID: 0"
}

#  ADPROTO: ${ProtoVersion}
say_hi()
{
	echo "ADPROTO: $ADP_VERSION"
}

do_status()
{
	get_lsbrel
	get_prl
	get_virt
	get_uname
	check_forbid
	get_pkg_stat
	get_kern
}

run_as_root()
{
	err_str="ADPERR:"
	interactive=$1
	[ $interactive -eq 1 ] && err_str="ERROR:"
	shift

	proxy="/etc/profile.d/proxy.sh"
	[ -r $proxy ] && source $proxy

	cmd="$ROOT_CMD $*"
	$cmd
	ret=$?
	if [ $ret -ne 0 ]; then
		echo "$err_str \"$cmd\" returned $ret"
		exit $err
	fi
}


if [ -z "$1" ]; then
	echo "Don't call this script directly!"
	exit $err
fi

case "$1" in
	refresh)
		say_hi
		run_as_root 0 $APK_CMD update
		do_status
		;;

	status)
		say_hi
		do_status
		;;

	upgrade)
		run_as_root 1 $APK_CMD upgrade
		;;

	install)
		shift
		echo "Installing PKG: $*"
		run_as_root 1 $APK_CMD add $*
		;;

	kernel)
		say_hi
		get_kern
		;;

	source-only)
		# do nothing (used for unit testing)
		;;

	*)
		echo Invalid command \'$1\'\!
		exit $err
		;;
esac
