#!/bin/bash

## Copyright (C) 2024 - 2025 ENCRYPTED SUPPORT LLC <adrelanos@whonix.org>
## Copyright (C) 2025 - 2025 Benjamin Grande M. S. <ben.grande.b@gmail.com>
## See the file COPYING for copying conditions.

set -o errexit
set -o nounset
set -o errtrace
set -o pipefail

printf "%s\n" "$0: INFO: START" >&2

source /usr/libexec/helper-scripts/light_sleep.bsh

error_handler() {
   local last_exit_code="$?"
   printf "%s\n" "\
###
$0: ERROR:
BASH_COMMAND: '$BASH_COMMAND'
failed with exit code '$last_exit_code'.
###" >&2
   light_sleep 5
   exit "$last_exit_code"
}

trap error_handler ERR

exit_handler() {
   local exit_code="$?"
   printf "%s\n" "INFO: If installed, rads (Ram Adjusted Desktop Starter) should be starting, ok." >&2
   printf "%s\n" "INFO: (Because sysmaint-boot.target runs 'Before=rads.service' and 'Wants=rads.service'.)" >&2
   if [ "$exit_code" = "0" ]; then
      printf "%s\n" "$0: END: with OK exit code: '$exit_code'" >&2
   else
      printf "%s\n" "$0: END: with ERROR exit code: '$exit_code'" >&2
      light_sleep 5
   fi
   exit "$exit_code"
}

trap exit_handler EXIT

user_sysmaint_split_config_dir='/etc/user-sysmaint-split.conf.d'

kernel_cmdline=''
if [ -f /proc/cmdline ]; then
  kernel_cmdline="$(cat -- /proc/cmdline)"
elif [ -f /proc/1/cmdline ]; then
  kernel_cmdline="$(cat -- /proc/1/cmdline)"
fi

## greetd is the only supported Wayland greeter, use it if it exists
if [ -f /usr/lib/systemd/system/greetd.service ]; then
   default_display_manager='greetd'
else
   if [ -f /etc/X11/default-display-manager ]; then
      default_display_manager="$(basename -- "$(cat -- /etc/X11/default-display-manager)")" || true
   else
      ## Non-Qubes: File /etc/X11/default-display-manager does not exist if no desktop environment is installed.
      ## Qubes: In App Qubes file /etc/X11/default-display-manager does usually not exist.
      default_display_manager=""
   fi
fi
sysmaint_session_wayland='yes'
sysmaint_autologin='no'
lightdm_home_dir='/var/lib/lightdm'
lightdm_state_dir='/var/lib/lightdm/.cache/lightdm-gtk-greeter'
lightdm_state_file="${lightdm_state_dir}/state"
lightdm_state_backup_file="${lightdm_state_file}.sysmaint-boot"
## no need for an sddm_home_dir variable as sddm_state_dir is also sddm's home
## dir
sddm_state_dir='/var/lib/sddm'
sddm_state_file="${sddm_state_dir}/state.conf"
sddm_state_backup_file="${sddm_state_file}.sysmaint-boot"
greetd_conf_dir='/etc/greetd/config.toml.d'
greetd_master_conf_file='/etc/greetd/config.toml'
wlgreet_conf_dir='/etc/greetd/wlgreet.toml.d'
wlgreet_master_conf_file='/etc/greetd/wlgreet.toml'
sysmaint_lightdm_conf_file='/etc/lightdm/lightdm.conf.d/zzz-sysmaint-boot.conf'
sysmaint_sddm_conf_file='/etc/sddm.conf.d/zzz-sysmaint-boot.conf'
sysmaint_greetd_conf_file='/etc/greetd/config.toml.d/50_sysmaint-boot.conf'
sysmaint_wlgreet_conf_file='/etc/greetd/wlgreet.toml.d/50_sysmaint-boot.conf'
old_sysmaint_conf_file_list=(
  '/etc/lightdm/lightdm.conf.d/60_sysmaint-boot.conf'
  '/etc/sddm.conf.d/z-sysmaint-boot.conf'
)
sudo_to_sysmaint="sudo --non-interactive -u sysmaint"

bail_if_read_only() {
   if test -w /etc; then
      return
   fi
   if ! test -d /etc; then
      printf "%s\n" "$0: ERROR: Folder '/etc' does not exist!" >&2
      exit 1
   fi
   printf "%s\n" "$0: ERROR: Folder '/etc' exists but is read-only! Full read-only disk without ephemeral overlay is unsupported!" >&2
   exit 1
}

restore_pre_sysmaint_autologin_config() {
   safe-rm -f -- "${sysmaint_lightdm_conf_file}"
   safe-rm -f -- "${sysmaint_sddm_conf_file}"
   safe-rm -f -- "${old_sysmaint_conf_file_list[@]}"
   safe-rm -f -- "${sysmaint_greetd_conf_file}"
   safe-rm -f -- "${sysmaint_wlgreet_conf_file}"
   if [ -f "${lightdm_state_backup_file}" ]; then
      safe-rm -r -f -- "${lightdm_state_file}"
      mv --verbose -- "${lightdm_state_backup_file}" "${lightdm_state_file}"
      if [ ! -f "${lightdm_state_file}" ] || ! [ -s "${lightdm_state_file}" ]; then
        safe-rm -r -f -- "${lightdm_state_file}"
      fi
   fi
   if [ -f "${sddm_state_backup_file}" ]; then
      safe-rm -r -f -- "${sddm_state_file}"
      mv --verbose -- "${sddm_state_backup_file}" "${sddm_state_file}"
      if [ ! -f "${sddm_state_file}" ] || ! [ -s "${sddm_state_file}" ]; then
        safe-rm -r -f -- "${sddm_state_file}"
      fi
   fi
   rebuild_greetd_config
}

write_lightdm_enable_autologin_config() {
   if [ "${sysmaint_session_wayland}" = 'yes' ]; then
      printf "%s\n" "[Seat:*]
autologin-user=sysmaint
autologin-session=sysmaint-session-wayland" \
         | sponge -- "${sysmaint_lightdm_conf_file}"
   else
      printf "%s\n" "[Seat:*]
autologin-user=sysmaint
autologin-session=sysmaint-session" \
         | sponge -- "${sysmaint_lightdm_conf_file}"
   fi
}

write_lightdm_disable_autologin_config() {
   printf "%s\n" "[Seat:*]
autologin-user=" \
      | sponge -- "${sysmaint_lightdm_conf_file}"
}

write_lightdm_sysmaint_default_session_config() {
   if [ -d '/home/sysmaint' ]; then
      if [ "${sysmaint_session_wayland}" = 'yes' ]; then
         printf "%s\n" "[Desktop]
Session=sysmaint-session-wayland" \
            | $sudo_to_sysmaint -- sponge -- '/home/sysmaint/.dmrc'
      else
         printf "%s\n" "[Desktop]
Session=sysmaint-session" \
            | $sudo_to_sysmaint -- sponge -- '/home/sysmaint/.dmrc'
      fi
      printf "%s\n" "INFO: Created file: '/home/sysmaint/.dmrc'" >&2
   else
      printf "%s\n" "WARNING: Folder '/home/sysmaint' does not exist. Therefore, file '/home/sysmaint/.dmrc' has not been created." >&2
   fi

   if [ -f "${lightdm_state_backup_file}" ]; then
      return
   fi

   if ! [ -d "${lightdm_state_dir}" ]; then
      safe-rm -r -f -- "${lightdm_state_dir}"
      mkdir --verbose --parents -- "${lightdm_state_dir}" || exit 1
      if accountctl lightdm is-user 2>/dev/null \
         && accountctl lightdm is-group 2>/dev/null; then
         ## Not using 'chown -R' here since some of the files under
         ## $lightdm_home_dir may legitimately need to be owned by root:root.
         chown -- lightdm:lightdm "${lightdm_home_dir}"
         chown -- lightdm:lightdm "${lightdm_home_dir}/.cache"
         chown -- lightdm:lightdm "${lightdm_state_dir}"
      fi
   fi

   ## Ensure the state file exists; if it doesn't, we'll make an empty one
   ## here. The undo logic will later detect that this file is empty and
   ## delete it if necessary.
   touch -- "${lightdm_state_file}"
   safe-rm -r -f -- "${lightdm_state_backup_file}"
   mv --verbose -- "${lightdm_state_file}" "${lightdm_state_backup_file}"

   if [ "${sysmaint_session_wayland}" = 'yes' ]; then
      printf "%s\n" "[greeter]
last-user=sysmaint
last-session=sysmaint-session-wayland" \
         | sponge -- "${lightdm_state_file}"
   else
      printf "%s\n" "[greeter]
last-user=sysmaint
last-session=sysmaint-session" \
         | sponge -- "${lightdm_state_file}"
   fi
   printf "%s\n" "INFO: Created file: '${lightdm_state_file}'" >&2
}

write_sddm_enable_autologin_config() {
   # Typically files under /etc/sddm.conf.d are NOT prefixed with a number, for
   # instance KDE generates an /etc/sddm.conf.d/kde_settings.conf file, thus
   # to get this file to be loaded last we have to use a name that sorts last
   # in the alphabet without using a number prefix.
   if [ "${sysmaint_session_wayland}" = 'yes' ]; then
      printf "%s\n" "[Autologin]
User=sysmaint
Session=sysmaint-session-wayland.desktop" \
         | sponge -- "${sysmaint_sddm_conf_file}"
   else
      printf "%s\n" "[Autologin]
User=sysmaint
Session=sysmaint-session.desktop" \
         | sponge -- "${sysmaint_sddm_conf_file}"
   fi
}

write_sddm_disable_autologin_config() {
   printf "%s\n" "[Autologin]
User=
Session=" \
      | sponge -- "${sysmaint_sddm_conf_file}"
}

write_sddm_sysmaint_default_session_config() {
   if [ -f "${sddm_state_backup_file}" ]; then
      return
   fi

   if ! [ -d "${sddm_state_dir}" ]; then
      safe-rm -r -f -- "${sddm_state_dir}"
      mkdir --verbose --parents -- "${sddm_state_dir}" || exit 1
      if accountctl sddm is-user 2>/dev/null \
         && accountctl sddm is-group 2>/dev/null; then
         chown -- sddm:sddm "${sddm_state_dir}"
      fi
   fi

   ## Ensure the state file exists; if it doesn't, we'll make an empty one
   ## here. The undo logic will later detect that this file is empty and
   ## delete it if necessary.
   touch -- "${sddm_state_file}"
   safe-rm -r -f -- "${sddm_state_backup_file}"
   mv --verbose -- "${sddm_state_file}" "${sddm_state_backup_file}"

   if [ "${sysmaint_session_wayland}" = 'yes' ]; then
      printf "%s\n" "[Last]
User=sysmaint
Session=/usr/share/wayland-sessions/sysmaint-session-wayland.desktop" \
         | sponge -- "${sddm_state_file}"
   else
      printf "%s\n" "[Last]
User=sysmaint
Session=/usr/share/xsessions/sysmaint-session.desktop" \
         | sponge -- "${sddm_state_file}"
   fi
   printf "%s\n" "INFO: Created file: '/var/lib/sddm/state.conf'" >&2
}

write_greetd_enable_autologin_config() {
   printf "%s\n" "[initial_session]
command = \"/usr/libexec/user-sysmaint-split/sysmaint-session-wayland\"
user = \"sysmaint\"" \
      | sponge -- "${sysmaint_greetd_conf_file}"
}

write_greetd_disable_autologin_config() {
   # `~initial_session` masks out the `initial_session` key. Setting the keys
   # within the section to blank values resulted in greetd crashing, so we
   # just mask the section entirely now when necessary.
   printf "%s\n" "[~initial_session]" \
      | sponge -- "${sysmaint_greetd_conf_file}"
}

write_greetd_sysmaint_default_session_config() {
   ## Thanks in part to wlgreet's simplicity, we don't have to fight with
   ## backup state files here.
   printf "%s\n" "command = \"/usr/libexec/user-sysmaint-split/sysmaint-session-wayland\"" \
      | sponge -- "${sysmaint_wlgreet_conf_file}"
}

rebuild_greetd_config() {
   ## greetd doesn't actually support config drop directories, we use
   ## build-config-file from helper-scripts to "fake" support. Thus we need
   ## to rebuild the real configuration files once this is done.
   if [ -d "${greetd_conf_dir}" ]; then
      build-config-file "${greetd_conf_dir}" "${greetd_master_conf_file}"
   fi
   if [ -d "${wlgreet_conf_dir}" ]; then
      build-config-file "${wlgreet_conf_dir}" "${wlgreet_master_conf_file}"
   fi
}

handle_boot() {
   bail_if_read_only

   if ! accountctl sysmaint is-user 2>/dev/null; then
      printf "%s\n" "$0: ERROR: Account 'sysmaint' does not exist. Cleaning up system-wide sysmaint config and exiting." >&2
      restore_pre_sysmaint_autologin_config
      exit 1
   fi

   if ! [[ "${kernel_cmdline}" =~ 'boot-role=sysmaint' ]]; then
      printf "%s\n" "INFO: USER Session boot detected." >&2
      printf "%s\n" "INFO: (kernel parameter 'boot-role=sysmaint' is not present, ok.)" >&2
      restore_pre_sysmaint_autologin_config
      exit 0
   fi

   printf "%s\n" "INFO: SYSMAINT Session detected." >&2
   printf "%s\n" "INFO: (kernel parameter 'boot-role=sysmaint' present, ok.)" >&2

   if [ "${default_display_manager}" = 'lightdm' ]; then
      mkdir --verbose --parents -- '/etc/lightdm/lightdm.conf.d'

      if [ "${sysmaint_autologin}" = "yes" ]; then
         write_lightdm_enable_autologin_config
      else
         ## Prevent autologin in this instance, or the normal user account will
         ## end up logged in.
         write_lightdm_disable_autologin_config
      fi
      printf "%s\n" "INFO: Created file: '${sysmaint_lightdm_conf_file}'" >&2

      write_lightdm_sysmaint_default_session_config
   elif [ "${default_display_manager}" = 'sddm' ]; then
      mkdir --verbose --parents -- '/etc/sddm.conf.d'

      if [ "${sysmaint_autologin}" = "yes" ]; then
         write_sddm_enable_autologin_config
      else
         ## Prevent autologin in this instance, or the normal user account will
         ## end up logged in
         write_sddm_disable_autologin_config
      fi
      printf "%s\n" "INFO: Created file: '${sysmaint_sddm_conf_file}'" >&2

      write_sddm_sysmaint_default_session_config
   elif [ "${default_display_manager}" = 'greetd' ]; then
      mkdir --verbose --parents -- '/etc/greetd/config.toml.d'
      mkdir --verbose --parents -- '/etc/greetd/wlgreet.toml.d'

      if [ "${sysmaint_autologin}" = "yes" ]; then
         write_greetd_enable_autologin_config
      else
         ## Prevent autologin in this instance, or the normal user account will
         ## end up logged in
         write_greetd_disable_autologin_config
      fi
      printf "%s\n" "INFO: Created file: '${sysmaint_greetd_conf_file}'" >&2

      write_greetd_sysmaint_default_session_config

      rebuild_greetd_config
   fi

   ## Qubes handling.
   if [ ! -f /usr/share/qubes/marker-vm ]; then
      return
   fi

   printf "%s\n" "INFO: Qubes OS detected. Installing volatile qrexec blacklist." >&2

   local rpc_config_file
   while read -r rpc_config_file; do
      cp -- '/usr/libexec/user-sysmaint-split/qubes-rpc-block' "/run/qubes-rpc/${rpc_config_file}"
   done < /usr/share/user-sysmaint-split/qubes-rpc-blocks
}

parse_user_sysmaint_split_config() {
   local config_file line
   if [ -d "${user_sysmaint_split_config_dir}" ]; then
      for config_file in "${user_sysmaint_split_config_dir}"/*; do
         if [ ! -f "${config_file}" ]; then
            continue
         fi
         while read -r line; do
            case "${line}" in
               'sysmaint-autologin=yes')
                  sysmaint_autologin='yes'
                  ;;
               'sysmaint-autologin=no')
                  sysmaint_autologin='no'
                  ;;
               'sysmaint-session-wayland=yes')
                  sysmaint_session_wayland='yes'
                  ;;
               'sysmaint-session-wayland=no')
                  sysmaint_session_wayland='no'
                  ;;
            esac
         done < "${config_file}"
      done
   fi
   printf "%s\n" "INFO: account 'sysmaint' autologin: '${sysmaint_autologin}'" >&2
   printf "%s\n" "INFO: Wayland session: '${sysmaint_session_wayland}'" >&2
}

remove_sysmaint_qubes() {
   if [ -f /run/qubes/this-is-templatevm ]; then
     ## We don't support using unrestricted admin mode in templates, because
     ## the uninstallation of user-sysmaint-split would persist across
     ## reboots.
     ##
     ## Don't make this an error, to avoid cluttering up systemcheck too much.
     ## systemcheck will already detect this condition and warn about it.
     printf '%s\n' "INFO: Refusing to remove user-sysmaint-split even though kernel parameter 'remove-sysmaint-qubes' is present, because this is a TemplateVM."
     return 0
   fi

   printf "%s\n" "INFO: Qubes unrestricted mode detected. Removing user-sysmaint-split."
   printf "%s\n" "INFO: (kernel parameter 'remove-sysmaint-qubes' is present, ok.)"
   dummy-dependency --yes --purge user-sysmaint-split
   if accountctl user is-pass-locked 2>/dev/null; then
     printf "%s\n" "INFO: Account 'user' has a locked password. Running /usr/bin/passwordless-root to enable passwordless root escalation."
     printf "%s\n" "INFO: (kernel parameter 'remove-sysmaint-qubes' is present, ok.)"
     /usr/bin/passwordless-root
   fi
}

sysmaint_boot() {
   local mode="${1-}"
   parse_user_sysmaint_split_config
   case "${mode}" in
      'handle-boot') handle_boot;;
      'cleanup-autologin') restore_pre_sysmaint_autologin_config;;
      'remove-sysmaint-qubes') remove_sysmaint_qubes;;
      'query-sysmaint-autologin')
         trap "" EXIT
         printf "%s\n" "${sysmaint_autologin}"
      ;;
      *)
         printf "%s\n" "$0: ERROR: Invalid mode provided." >&2
         exit 1
      ;;
   esac
}

sysmaint_boot "$@"
