#!/bin/bash

set -e
set +x

SNAP_MOUNT_DIR="@SNAP_MOUNT_DIR@"

show_help() {
    exec cat <<'EOF'
Usage: snap-mgmt-selinux.sh [OPTIONS]

A helper script to manage SELinux contexts used by snapd

Arguments:
  --snap-mount-dir=<path>                   Provide a path to be used as $SNAP_MOUNT_DIR
  --patch-selinux-mount-context=<context>   Add SELinux context to mount units
  --remove-selinux-mount-context=<context>  Remove SELinux context from mount units
EOF
}

SNAP_UNIT_PREFIX="$(systemd-escape -p ${SNAP_MOUNT_DIR})"

patch_selinux_mount_context() {
    if ! command -v selinuxenabled > /dev/null; then
        return
    fi
    if ! selinuxenabled; then
        # The tools are there, but SELinux is not enabled
        return
    fi

    selinux_mount_context="$1"
    remove="$2"
    if ! echo "$selinux_mount_context" | grep -qE '[a-zA-Z0-9_]+(:[a-zA-Z0-9_]+){2,3}'; then
        echo "invalid mount context '$selinux_mount_context'"
        exit 1
    fi
    context_opt="context=$selinux_mount_context"

    mounts=$(systemctl list-unit-files --no-legend --full "$SNAP_UNIT_PREFIX-*.mount" | cut -f1 -d ' ' || true)
    changed_mounts=
    for unit in $mounts; do
        # Ensure its really a snap mount unit or systemd unit
        if ! grep -q 'What=/var/lib/snapd/snaps/' "/etc/systemd/system/$unit" && ! grep -q 'X-Snappy=yes' "/etc/systemd/system/$unit"; then
            echo "Skipping non-snapd systemd unit $unit"
            continue
        fi

        if [ "$remove" == "" ]; then
            if grep -q "Options=.*,$context_opt" < "/etc/systemd/system/$unit"; then
                # already patched
                continue
            fi

            if ! sed -i -e "s#^\\(Options=nodev.*\\)#\\1,$context_opt#" "/etc/systemd/system/$unit"; then
                echo "Cannot patch $unit"
            fi

            changed_mounts="$changed_mounts $unit"
        elif [ "$remove" == "remove" ]; then
            if ! grep -q "Options=.*,$context_opt" < "/etc/systemd/system/$unit"; then
                # Not patched
                continue
            fi

            if ! sed -i -e "s#^\\(Options=nodev.*\\),$context_opt\\(,.*\\)\\?#\\1\\2#" "/etc/systemd/system/$unit"; then
                echo "Cannot patch $unit"
            fi

            changed_mounts="$changed_mounts $unit"
        fi
    done

    if [ -z "$changed_mounts" ]; then
        # Nothing changed, no need to reload
        return
    fi

    systemctl daemon-reload

    for unit in $changed_mounts; do
        if ! systemctl try-restart "$unit" ; then
            echo "Cannot restart $unit"
        fi
    done
}

while [ -n "$1" ]; do
    case "$1" in
        --help)
            show_help
            exit
            ;;
        --snap-mount-dir=*)
            SNAP_MOUNT_DIR=${1#*=}
            SNAP_UNIT_PREFIX=$(systemd-escape -p "$SNAP_MOUNT_DIR")
            shift
            ;;
        --patch-selinux-mount-context=*)
            patch_selinux_mount_context "${1#*=}"
            shift
            ;;
        --remove-selinux-mount-context=*)
            patch_selinux_mount_context "${1#*=}" remove
            shift
            ;;
        *)
            echo "Unknown command: $1"
            exit 1
            ;;
    esac
done
