#!/bin/sh
# Start the Windows service control manager in the machine-level wineserver.
#
# Runs after sg-wineserver, as root, so services.exe and everything it starts
# carry the SYSTEM SID. Clients reach it over RPC inside the server's object
# namespace, which is shared by every process attached to that server -- so one
# SCM serves every logged-in user, as on Windows.
set -eu

SG_LIB="${SG_LIB:-/usr/lib/stained-glass}"
# shellcheck source=lib/sg-common.sh
. "$SG_LIB/sg-common.sh"
# The machine's Windows system never uses a display. Whatever screen this was
# started from -- a build host's, a developer's desktop -- would otherwise be
# recorded in the shared prefix as the machine's monitor, and every user's
# desktop would be laid out for it.
unset DISPLAY WAYLAND_DISPLAY XAUTHORITY
sg_wine_env

HOME="${HOME:-$SG_ROOT}"
XDG_RUNTIME_DIR="${XDG_RUNTIME_DIR:-/run/stained-glass}"
export HOME XDG_RUNTIME_DIR

# Session 0; see the note in sg-wineserver. services.exe already puts the
# services it launches on this station -- it just never put itself there,
# because on Windows its parent does that for it.
SG_WINSTATION='__wineservice_winstation\Default'
export SG_WINSTATION

# Protect the machine registry branches on the running machine server, before
# any user logs in. See sg_protect_machine_registry.
sg_log "protecting machine registry branches"
sg_protect_machine_registry

# Machine Group Policy, applied after the branches are protected so it lands in
# administrator-owned keys.
sg_log "applying machine policy"
sg_apply_policy

# The event logs are read only by the Event Log service (SYSTEM), which answers
# everyone else through its pipe and decides who may read what -- Security
# only for administrators. At the Linux level the prefix's files are all open
# to its group (D13), so their directory is made the SYSTEM account's alone:
# a user reading the prefix through Z: must not get the Security log. Wine
# cannot make it so itself (a DACL naming SYSTEM maps to user and group bits).
sg_protect_event_logs() {
    logs="$SG_PREFIX/drive_c/windows/system32/winevt/Logs"
    [ -d "$SG_PREFIX/drive_c/windows/system32" ] || return 0
    mkdir -p "$logs" || return 0
    chmod 0700 "$logs" || :
    find "$logs" -mindepth 1 -maxdepth 1 -type f -exec chmod go-rwx {} + 2>/dev/null || :
}
sg_log "protecting the event logs"
sg_protect_event_logs

# The SYSTEM account's temporary folder. Its profile is made afresh on a new
# machine (the image carries none), and Wine made the profile but not its
# Temp: whatever runs as SYSTEM then had no TEMP -- msiexec, run as an
# administrator, could not open its copy of a package (transacted storage
# needs a temporary file) and stopped with 91, or 2 for Edge's installer.
# All of the standard folders, as a user's profile has them (sg-profile-create):
# Steam started as an administrator crashed on its first download without
# AppData\LocalLow (cryptnet keeps its revocation cache there).
sg_system_temp() {
    profile="$SG_PREFIX/drive_c/users/$(id -un)"
    [ -d "$SG_PREFIX/drive_c/users" ] || return 0
    (umask 022; cd "$SG_PREFIX/drive_c/users" && mkdir -p "$profile" && cd "$profile" && mkdir -p \
        AppData/Local/Temp AppData/LocalLow AppData/Roaming \
        Desktop Documents Downloads Music Pictures Videos Favorites Links \
        Contacts Searches "Saved Games") 2>/dev/null || sg_log "could not make the folders of $profile"
}
sg_system_temp

# Windows' code signing and time stamping roots (David 2026-10-28: "like
# Windows"): the roots Microsoft's root program trusts for those uses, as CCADB
# publishes them, that the host's store lacks -- VeriSign Class 3 G5, which GOM
# Player's signatures chain to, DigiCert's and Actalis' code signing roots.
# sg-shell's sg-roots puts them into the machine's Root store, each marked with
# its uses only (wine-sg 7602 honours that: never a web server's), removes the
# ones the bundle dropped, and leaves alone what it did not add. In the
# background: the login screen does not wait for it.
_roots_tool="${SG_LIBEXEC:-/usr/libexec/stained-glass}/shell/sg-roots64.exe"
_roots_pem="${SG_CODESIGN_ROOTS:-/usr/share/stained-glass/codesign-roots.pem}"
if [ -f "$_roots_tool" ] && [ -s "$_roots_pem" ]; then
    sg_log "the machine's code signing roots"
    ( timeout 300 wine "$_roots_tool" --sync "Z:$_roots_pem" 2>/dev/null | while IFS= read -r _l; do sg_log "$_l"; done ) &
fi

# The first-run setup's region and keyboards for the accounts that draw the
# sign-in screen (sggreet) and the lock screen (the SYSTEM account), as
# Windows' setup copies them to the welcome screen: the lock screen's clock
# was in US English on an en-GB machine. Once per setup (sg-oobe-user's stamp).
_oobe_user="$SG_LIB/sg-oobe-user"
if [ -x "$_oobe_user" ] && [ -r "${SG_OOBE_CONF:-/etc/stained-glass/oobe.conf}" ]; then
    for _acct in "$SG_SYSTEM_USER" sggreet; do
        getent passwd "$_acct" >/dev/null 2>&1 || continue
        timeout 120 runuser -u "$_acct" -- env -u DISPLAY -u WAYLAND_DISPLAY -u XDG_RUNTIME_DIR \
            SG_WINSTATION='__wineservice_winstation\Default' "$_oobe_user" --welcome >/dev/null 2>&1 ||
            sg_log "WARNING: the first-run setup's region was not applied to $_acct"
    done
fi

sg_log "starting services.exe as SYSTEM"

# Launched detached: services.exe is a long-running Windows process, and this
# script's job is only to get it going inside the already-running server.
wine services.exe &

# Wait for the SCM to answer rather than assuming it is up. A session that
# starts before the SCM is ready sees no services and looks broken.
# Bounded by the clock, not by counting tries: on a slow machine (or an
# emulated one) each sc query itself takes seconds, and the unit's start
# timeout must never be reached -- systemd would kill the whole server.
start=$(date +%s)
while :; do
    waited=$(( $(date +%s) - start ))
    [ "$waited" -lt "${SG_SERVICES_TIMEOUT:-60}" ] || break
    # a named service: Wine's "sc query" alone prints its usage and exits
    # 103, so asking that waited out the whole timeout on every boot -- a
    # minute before the login screen
    if timeout 30 wine sc query PlugPlay >/dev/null 2>&1; then
        sg_log "SCM is answering after ${waited}s"
        exit 0
    fi
    sleep 2
done

sg_log "WARNING: the SCM did not answer within ${SG_SERVICES_TIMEOUT:-60}s"
exit 0
