#!/bin/sh

if [ -n "$SHELL" ] &&
   grep -q "$SHELL" /etc/shells &&
   ! (echo "$SHELL" | grep -q "false") &&
   ! (echo "$SHELL" | grep -q "nologin"); then
  if [ "$1" != '-l' ]; then
    exec bash -c "exec -l '$SHELL' -c '$0 -l $*'"
  else
    shift
  fi
fi

# A live graphical session only exists if logind still tracks an active
# wayland session of this user besides the one we are starting.
has_live_graphical_session() {
    user=$(id -un)
    for id in $(loginctl list-sessions --no-legend 2>/dev/null | awk '{print $1}'); do
        [ "$id" = "${XDG_SESSION_ID-}" ] && continue
        if [ "$(loginctl show-session "$id" -p Name | cut -d= -f2)" = "$user" ] &&
            [ "$(loginctl show-session "$id" -p State | cut -d= -f2)" = "active" ] &&
            [ "$(loginctl show-session "$id" -p Type | cut -d= -f2)" = "wayland" ]; then
            return 0
        fi
    done
    return 1
}

# Try to detect the service manager that is being used
if hash systemctl &> /dev/null; then
    # A session that died uncleanly (crash, kill of the session wrapper) can
    # leave veshell.service and graphical-session.target active in the user
    # manager with no real session behind them, which would make every
    # further login (veshell or GNOME) fail until reboot.
    #
    # Recover from that stale state instead of failing: if some other live
    # graphical session of this user is really still running, refuse; if not,
    # tear the stale state down and proceed.
    if systemctl --user -q is-active veshell.service || systemctl --user -q is-active graphical-session.target; then
        if has_live_graphical_session; then
            echo 'A graphical session is already running.'
            exit 1
        fi

        echo 'Recovering stale veshell session state...'
        systemctl --user start --job-mode=replace-irreversibly veshell-shutdown.target

        # Wait until the user manager reports no stale state left, so the
        # start below cannot race with the recovery.
        i=0
        while systemctl --user -q is-active veshell.service || systemctl --user -q is-active graphical-session.target; do
            [ $i -lt 20 ] || exit 1
            sleep 0.5
            i=$((i + 1))
        done
    fi

    # Make sure there's no already running session.
    if systemctl --user -q is-active veshell.service; then
      echo 'A veshell session is already running.'
      exit 1
    fi

    # Reset failed state of all user units.
    systemctl --user reset-failed

    # Import the login manager environment.
    systemctl --user import-environment

    # DBus activation environment is independent from systemd. While most of
    # dbus-activated services are already using `SystemdService` directive, some
    # still don't and thus we should set the dbus environment with a separate
    # command.
    if hash dbus-update-activation-environment 2>/dev/null; then
        dbus-update-activation-environment --all
    fi

    # GDM propagates the WAYLAND_DISPLAY (and DISPLAY) of the previous session
    # in its session environment; imported above, they would make the fresh
    # compositor believe it is a nested one and connect to a dead socket.
    # The real values are published by the compositor itself once it starts.
    systemctl --user unset-environment WAYLAND_DISPLAY DISPLAY WAYLAND_SOCKET

    # Session teardown shared by the normal exit path and the signal trap
    # below. It is idempotent, so running it twice is harmless.
    teardown() {
        # Force stop of graphical-session.target.
        systemctl --user start --job-mode=replace-irreversibly veshell-shutdown.target

        # Unset environment that we've set (the compositor has published its
        # own WAYLAND_DISPLAY/DISPLAY for dbus activation; with the session
        # gone they must not linger for the next login).
        systemctl --user unset-environment WAYLAND_DISPLAY DISPLAY WAYLAND_SOCKET XDG_SESSION_TYPE XDG_CURRENT_DESKTOP
    }

    # logind ends the session by killing this wrapper with SIGTERM (GDM
    # teardown, `loginctl terminate-session`), which the compositor - a user
    # service outside the session scope - would otherwise survive. It would
    # then keep running in the background with the applications it launched
    # (music keeps playing, browsers keep running) until the next login
    # recovers the stale session. Stop it explicitly so its ExecStop tears the
    # graphical session down right away.
    #
    # The wait must run in the background: a foreground `systemctl --wait`
    # defers the trap until the service stops on its own, which never happens
    # on logout.
    trap 'systemctl --user stop veshell.service; teardown; exit 0' TERM INT HUP

    # Start veshell and wait for it to terminate.
    systemctl --user --wait start veshell.service &
    wait

    teardown
elif hash dinitctl &> /dev/null; then
    # Check that the user dinit daemon is running
    if ! pgrep -u $(id -u) dinit &> /dev/null; then
      echo "dinit user daemon is not running."
      exit 1
    fi

    # Make sure there's no already running session.
    if dinitctl --user is-started veshell &> /dev/null; then
      echo 'A veshell session is already running.'
      exit 1
    fi

    # Start veshell
    dinitctl --user start veshell
else
    echo "No systemd or dinit detected, please use veshell --session instead."
fi
