#!/bin/sh
# Copyright (c) 2012 - 2019 Jolla Ltd.
# Copyright (c) 2019 - 2020 Open Mobile Platform LLC.
#
# This adds oneshot job to be run later or now
usage() {
    echo "usage: add-oneshot [--now] [--late] [--user|--all-users|--uid <uid>] [--privileged] [--] <name of the job> [<more job names>]"
    echo "or:    add-oneshot --new-users [--all-users] [--late] [--privileged] [--] <name of the job> [<more job names>]"
    exit 1
}
# If --now is given, job is run immediately instead of postponing it later
# If instant run fails or if --now is not given, the link is created for later run
# If --user is given, job is run as current user, otherwise as root
# If --all-users is given then the job is ran for all users
# If --new-users is given then the job is ran for each newly created user
# If --uid <uid> is given then that user is used to run the job
# If --late is given then job is run after first boot is over (after init-done)
# If --privileged is given, job is run in privileged group (unless run as root)
# Without --late or --now job is run at the very beginning of the boot or user session
# Job names must follow options and you may use -- to specify files beginning with --
# Job name is given as name of the executable located in /usr/lib/oneshot.d
# ie. no path mentioned

run_as_user() {
    GROUP_PARAMS=""
    [ -n "$GROUP" ] && [ "$1" != "root" ] && GROUP_PARAMS="-g $GROUP"
    su -l $1 $GROUP_PARAMS /usr/lib/oneshot.d/$2 && RUN_OK="1"
}

save_job() {
    SAVE_OK=""
    FOLDER=$2
    [ -n "$4" ] && FOLDER=${FOLDER}/$4

    mkdir -p /etc/oneshot.d/$FOLDER
    ln -sf /usr/lib/oneshot.d/$1 /etc/oneshot.d/$FOLDER && SAVE_OK="1"
    if [ -n "$SAVE_OK" ]; then
        echo "add-oneshot: /etc/oneshot.d/$FOLDER/$1 - job saved OK"
        if [ -n "$3" ]; then
            # Change ownership so user can remove the link after running
            chown $3:$3 /etc/oneshot.d/$2
            # If privileged, ensure only privileged user can remove it
            if [ -n "$4" ]; then
                chown $4:$4 /etc/oneshot.d/$FOLDER
                chmod 775 /etc/oneshot.d/$FOLDER
            fi
            chown -h $3:$3 /etc/oneshot.d/$FOLDER/$1
        fi
    else
        echo "add-oneshot: /etc/oneshot.d/$2/$1 - job saving FAILED"
    fi
}

run_for_all() {
    USERS=$(getent group users | cut -d ":" -f 4 | tr "," "\n")
    for u in $USERS; do
        RUN_OK=""
        run_as_user $u $1
        if [ -z "$RUN_OK" ]; then
            USER_UID=$(getent passwd $u | cut -d ":" -f 3)
            save_job $1 $USER_UID $u $GROUP
        fi
    done
}

# Define PATH in case we are called in build time (PATH empty or sbin not included)
if [ -z "$PATH" ] || echo "$PATH" | grep -q sbin; then
    PATH="/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin"
fi

RUN_NOW=""
AS_USER=""
LATE_RUN=""
ALL_USERS=""
NEW_USERS=""
USER_UID=""
GROUP=""

while [ -n "$1" ]; do
    case "$1" in
        "--now") RUN_NOW=1 ;;
        "--user") AS_USER=1 ;;
        "--late") LATE_RUN=1 ;;
        "--all-users") ALL_USERS=1 ;;
        "--new-users") NEW_USERS=1 ;;
        "--privileged") GROUP="privileged" ;;
        "--uid") shift ; USER_UID=$1 ;;
        "--"?*) # Catch wrong arguments
            echo "add-oneshot: wrong argument: $1"
            exit 1 ;;
        "--") # The rest are filenames
            shift ; break ;;
        *) # Must be a filename already
            break ;;
    esac
    shift
done

# Show usage if there are no job names specified
[ -z "$1" ] && usage

set +e
while [ -n "$1" ]; do
    # Skip execution when called during bootstrap
    [ -f "/.bootstrap" ] && RUN_NOW=
    JOB=$1
    # Job must be located in oneshot dir and must be ok to run it
    [ ! -x /usr/lib/oneshot.d/$JOB ] && echo "add-oneshot: /usr/lib/oneshot.d/$JOB does not exist - FAIL" && exit 2

    if [ -n "$NEW_USERS" ]; then
        # Job must be run for each newly created user
        LOCATION="newuser"
        [ -n "$LATE_RUN" ] && LOCATION=${LOCATION}/late
        [ -n "$GROUP" ] && LOCATION=${LOCATION}/${GROUP}
        save_job $JOB $LOCATION
    elif [ -n "$RUN_NOW" ]; then
        RUN_OK=""
        # Job is wanted to be run right now
        if [ -n "$ALL_USERS" ]; then
            # Run for all users
            run_for_all $JOB
        else
            DEVICEUSER=""
            if [ -n "$AS_USER" ]; then
                # Run as user (find out correct user name)
                DEVICEUSER=$(loginctl list-sessions | grep seat0 | tr -s " " | cut -d " " -f 4)
                if [ -z "$DEVICEUSER" ]; then
                    # Fall back to the default user
                    DEF_UID=$(grep "^UID_MIN" /etc/login.defs |  tr -s " " | cut -d " " -f 2)
                    DEVICEUSER=$(getent passwd $DEF_UID | sed 's/:.*//')
                fi
            elif [ -n "$USER_UID" ]; then
                # Run with the specified user
                DEVICEUSER=$(getent passwd $USER_UID | sed 's/:.*//')
            fi
            if [ -n "$DEVICEUSER" ]; then
                run_as_user $DEVICEUSER $JOB
            else
                # Run as root
                /usr/lib/oneshot.d/$JOB && RUN_OK="1"
            fi
        fi

        if [ -n "$RUN_OK" ]; then
            echo "add-oneshot: /usr/lib/oneshot.d/$JOB - run OK"
            shift && continue
        else
            echo "add-oneshot: /usr/lib/oneshot.d/$JOB - could not be run, save for later"
        fi
    fi

    # Either --now was not defined or our run failed
    # Create link so we run this job later
    if [ -n "$ALL_USERS" ]; then
        USERS=$(getent group users | cut -d ":" -f 4 | tr "," "\n")
        for u in $USERS; do
            LOCATION=$(getent passwd $u | cut -d ":" -f 3)
            [ -n "$LATE_RUN" ] && LOCATION=${LOCATION}/late
            save_job $JOB $LOCATION $u $GROUP
        done
    # Only --all-users can follow --new-users
    elif [ -z "$NEW_USERS" ] ; then
        if [ -n "$AS_USER" ]; then
            USER_UID=$(loginctl list-sessions | grep seat0 | tr -s " " | cut -d " " -f 3)
            [ -z "$USER_UID" ] && USER_UID=$(grep "^UID_MIN" /etc/login.defs |  tr -s " " | cut -d " " -f 2)
            LOCATION=$USER_UID
            USER_NAME=$(getent passwd $USER_UID | cut -d ":" -f 1)
        elif [ -n "$USER_UID" ]; then
            LOCATION=$USER_UID
            USER_NAME=$(getent passwd $USER_UID | cut -d ":" -f 1)
        else
            LOCATION="0"
            USER_NAME="root"
        fi
        [ -n "$LATE_RUN" ] && LOCATION=${LOCATION}/late
        [ "$USER_NAME" = "root" ] && GROUP=""
        save_job $JOB $LOCATION $USER_NAME $GROUP
    fi
    shift
done
exit 0
