#!/bin/bash
#
# Top-level system-update logic.
#
#   DEVNAME=<blk-dev> /usr/sbin/system-updater
#   /usr/sbin/system-updater <path>
#
# When DEVNAME is provided, it refers to a block device containing a valid
# filesystem, containing the update materials, i.e. /dev/sdb1. This method is
# favored by tools like usbmount.
#
# When a command-line parameter is provided, it refers to a directory
# containing update materials. This method is favored by tools like systemd and
# inotify.
#
# "Update materials" can be one or more .deb package files, or an ISO image
# file containing a single APT repository.
#
# When users provide individual package files, we make no attempt here to
# resolve inter-package installation dependencies. If there are any, it's up to
# the user to either send us the files one at a time in the correct order, or
# to lexically name them so that a wildcard regex will accomplish the same
# thing.
#
# Inter-package dependencies are handled correctly when the incoming materials
# are an APT repository.

if [ -z "$DEVNAME" ]; then
    MOUNTPOINT=$1
else
    MOUNTPOINT=$(lsblk --noheadings --output MOUNTPOINT $DEVNAME)
fi

if [ -z "$MOUNTPOINT" ]; then
    exit 1
fi

DEBHELPER=system-updater-debhelper
ISOHELPER=system-updater-apthelper

# The user provided a package file. Install it using systemd-run, so that we
# don't nuke ourselves if the package being updated is ourselves.
do_deb() {
    systemd-run --wait --collect --unit="$DEBHELPER" /usr/sbin/$DEBHELPER $1
}

# The user provided an APT repository. Feed it to ISOHELPER.
do_iso() {
    TMP=$(mktemp -d)
    mount $1 $TMP
    systemd-run --wait --collect --unit="$ISOHELPER" /usr/sbin/$ISOHELPER $TMP
    umount $TMP
    rm -rf $TMP
}

# Examine the incoming materials, and forward them to the correct helper based
# on type. We use --mime-type here so that users can name the incoming file
# whatever they like without confusing us. The receiving tools are generally
# smart enough to detect if a file is corrupted or the wrong type, so we don't
# need to double-check --mime-type's work all that carefully here.
#
# We intentionally disallow gzip'ed types here, so that we don't preclude a
# highly-constrained file/memory situations. In particular, we don't want to
# have to require intermediate storage for a large repository. It isn't obvious
# where we'd reliably find that much storage, so we leave that problem for
# another day.
#
# Recall that ISO is both read-only and mountable, so (a) we don't have to
# worry about accidentally modifying the incoming media, because that isn't
# possible; and (b) we don't have to reserve room to "extract" anything from
# it.
#
for file in ${MOUNTPOINT}/*; do
    if [[ ! -f "$file" ]]; then
        continue
    fi

    type=$(file --mime-type -b "$file")
    case $type in

        application/vnd.debian.binary-package)
            do_deb "$file"
            ;;

        application/x-iso9660-image)
            do_iso "$file"
            ;;

        *)
            echo "${file##*/}($type) ignored."
            ;;
    esac
done

# At this point we've done all that can be done with the update materials. If
# we want to send an event to inform the system, DON'T modify this script: add
# an override instead.
