mirror of
https://github.com/outbackdingo/hyprcosmic-session.git
synced 2026-08-25 14:53:23 +00:00
PAM starts the daemon at login but brings up only its `control` socket. Nothing else brought up the rest, so a HyprCosmic session had no agent at all: SSH_AUTH_SOCK unset, every SSH operation asking for the passphrase again, and callers of the secrets API finding nothing listening. Under the stock COSMIC session the same machine works, which makes it look like a key problem rather than a session-script omission. `--start` attaches to the daemon that is already running and brings up the missing components; on this machine that created `pkcs11` and `ssh` alongside the existing `control`. Where the socket path comes from is the part worth writing down. start-cosmic evals the daemon's stdout, but the eval is `eval "$(... > /dev/null 2>&1)"` -- the redirect is inside the substitution, so it always evaluates the empty string, and the block works only because of the socket checks after it. Rather than repair that by evaluating whatever a daemon writes to stdout, this reads out the single variable we want and keeps the fixed paths as the fallback. The daemon also writes chatter to stderr, so the 2>/dev/null is load-bearing for the parse rather than tidiness. start-cosmic's rule is kept intact: set the correct socket or set none at all, never a wrong one. A plain file sitting where the socket belongs is rejected by the `-S` test, and a machine with no keyring directory skips the block entirely. SSH_AUTH_SOCK then joins the systemd and D-Bus activation environments for the same reason XDG_CURRENT_DESKTOP did: a user unit or an activated app looks there, not in this script's environment. `import-environment` ignores an unset name and exits 0, so a machine without a keyring still logs in.
181 lines
8.5 KiB
Bash
Executable File
181 lines
8.5 KiB
Bash
Executable File
#!/usr/bin/bash
|
|
#
|
|
# Launch a HyprCosmic session: cosmic-comp for window management, HyDE's shell
|
|
# on top.
|
|
#
|
|
# Deliberately separate from /usr/bin/start-cosmic rather than patching it.
|
|
# The stock COSMIC session entry keeps working untouched, so a broken
|
|
# HyprCosmic config is always one logout away from being escaped — which
|
|
# matters when the thing you are replacing is your desktop.
|
|
|
|
set -e
|
|
|
|
# The forked binaries, installed alongside rather than over the stock ones.
|
|
#
|
|
# Pointing at /usr/bin/cosmic-session here would be a silent no-op: the system
|
|
# binary has no profile module, so the session would come up as ordinary COSMIC
|
|
# and the gating would look broken. Installing to a private prefix instead of
|
|
# replacing /usr/bin also means `dnf update cosmic-session` cannot clobber the
|
|
# fork, and the stock session entry keeps working as the escape hatch.
|
|
HYPRCOSMIC_SESSION_BIN="${HYPRCOSMIC_SESSION_BIN:-/usr/libexec/hyprcosmic/cosmic-session}"
|
|
HYPRCOSMIC_COMP_BIN="${HYPRCOSMIC_COMP_BIN:-/usr/libexec/hyprcosmic/cosmic-comp}"
|
|
|
|
if [[ ! -x "$HYPRCOSMIC_SESSION_BIN" ]]; then
|
|
echo "start-hyprcosmic: $HYPRCOSMIC_SESSION_BIN is missing or not executable" >&2
|
|
echo "start-hyprcosmic: log out and choose the stock COSMIC session" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# Capture this session's output to a file that outlives it.
|
|
#
|
|
# A session that fails drops you straight back to the greeter, taking its
|
|
# stderr with it -- and by then you cannot read the journal from inside a
|
|
# session that no longer exists. A plain file in the cache directory is still
|
|
# there afterwards, whatever happened.
|
|
#
|
|
# A plain redirect, not `tee` into a process substitution: tee would be a child
|
|
# process the session writes through, so if it ever died the session would take
|
|
# a SIGPIPE. Nothing about diagnostics should be able to kill the desktop.
|
|
#
|
|
# Every step is guarded. If any of it fails the session starts anyway with no
|
|
# log -- losing diagnostics is an inconvenience, refusing to log in is not.
|
|
#
|
|
# The previous log is kept as .1, so a second attempt does not erase the
|
|
# evidence from the first.
|
|
HYPRCOSMIC_LOG="${XDG_CACHE_HOME:-$HOME/.cache}/hyprcosmic/session.log"
|
|
if mkdir -p "$(dirname "$HYPRCOSMIC_LOG")" 2>/dev/null; then
|
|
if [[ -f "$HYPRCOSMIC_LOG" ]]; then
|
|
mv -f "$HYPRCOSMIC_LOG" "${HYPRCOSMIC_LOG}.1" 2>/dev/null || true
|
|
fi
|
|
if : >>"$HYPRCOSMIC_LOG" 2>/dev/null; then
|
|
exec >>"$HYPRCOSMIC_LOG" 2>&1
|
|
echo "=== hyprcosmic session starting $(date -Is) ==="
|
|
echo " session bin: $HYPRCOSMIC_SESSION_BIN"
|
|
echo " comp bin: $HYPRCOSMIC_COMP_BIN"
|
|
echo " autostart: ${XDG_CONFIG_HOME:-$HOME/.config}/hyprcosmic/autostart"
|
|
fi
|
|
fi
|
|
|
|
# Selects the component set in cosmic-session's profile module: cosmic-panel,
|
|
# cosmic-launcher, cosmic-app-library, cosmic-workspaces, cosmic-bg and
|
|
# cosmic-files-applet are skipped; waybar and friends come from
|
|
# ~/.config/hyprcosmic/autostart.
|
|
export HYPRCOSMIC_PROFILE=hyprcosmic
|
|
|
|
# Keep COSMIC's identity: portals, cosmic-settings and any COSMIC app still
|
|
# key off this, and the compositor underneath really is COSMIC.
|
|
export XDG_CURRENT_DESKTOP="${XDG_CURRENT_DESKTOP:-COSMIC}"
|
|
export XDG_SESSION_DESKTOP="${XDG_SESSION_DESKTOP:-hyprcosmic}"
|
|
export XDG_SESSION_TYPE="${XDG_SESSION_TYPE:-wayland}"
|
|
|
|
# gnome-keyring, and the SSH agent it provides.
|
|
#
|
|
# PAM starts the daemon at login but brings up only its `control` socket, so
|
|
# without this a HyprCosmic session has no ssh-agent at all: SSH_AUTH_SOCK is
|
|
# unset, anything over SSH asks for the passphrase every time, and callers of
|
|
# the secrets API find nothing listening. `--start` attaches to the daemon that
|
|
# is already running and brings up whichever components are missing.
|
|
#
|
|
# The daemon writes `NAME=value` to stdout and chatter like
|
|
# "discover_other_daemon: 1" to stderr, so the 2>/dev/null is load-bearing for
|
|
# the parse, not just for tidiness.
|
|
#
|
|
# start-cosmic evals that stdout, but its eval is
|
|
# `eval "$(... > /dev/null 2>&1)"` -- the redirect is *inside* the substitution,
|
|
# so the eval always runs on an empty string and the block works only because
|
|
# of the socket checks that follow it. Rather than repair that by evaluating
|
|
# whatever a daemon chooses to write to stdout, read out the one variable we
|
|
# actually want.
|
|
if [[ -d "/run/user/$(id -u)/keyring" ]]; then
|
|
keyring_ssh_sock=""
|
|
if command -v gnome-keyring-daemon >/dev/null 2>&1; then
|
|
keyring_ssh_sock="$(
|
|
gnome-keyring-daemon --start --components=pkcs11,secrets,ssh 2>/dev/null |
|
|
sed -n 's/^SSH_AUTH_SOCK=//p' | tail -n1
|
|
)"
|
|
else
|
|
echo "start-hyprcosmic: gnome-keyring-daemon not found in PATH" >&2
|
|
fi
|
|
|
|
# start-cosmic's rule, kept: set the correct socket or set none at all,
|
|
# never a wrong one. The daemon's own answer goes first because it is
|
|
# authoritative about where it just put the socket; the two fixed paths are
|
|
# the fallback for when it told us nothing.
|
|
for candidate in \
|
|
"${keyring_ssh_sock}" \
|
|
"/run/user/$(id -u)/gcr/ssh" \
|
|
"/run/user/$(id -u)/keyring/ssh"
|
|
do
|
|
if [[ -n "${candidate}" && -S "${candidate}" ]]; then
|
|
export SSH_AUTH_SOCK="${candidate}"
|
|
break
|
|
fi
|
|
done
|
|
unset keyring_ssh_sock candidate
|
|
fi
|
|
|
|
# Same hygiene as start-cosmic: a failed unit left by a previous graphical
|
|
# session will otherwise block this one from starting.
|
|
if command -v systemctl >/dev/null; then
|
|
for unit in $(systemctl --user --no-legend --state=failed --plain list-units | cut -f1 -d' '); do
|
|
partof="$(systemctl --user show -p PartOf --value "$unit")"
|
|
for target in cosmic-session.target graphical-session.target; do
|
|
if [ "$partof" = "$target" ]; then
|
|
systemctl --user reset-failed "$unit"
|
|
break
|
|
fi
|
|
done
|
|
done
|
|
|
|
# Hand the session's identity to `systemd --user`, which is what actually
|
|
# launches the portal.
|
|
#
|
|
# Exporting XDG_CURRENT_DESKTOP above only reaches processes started *by
|
|
# this script*. The user manager is older than the session and has its own
|
|
# environment block, so anything it activates -- xdg-desktop-portal, and
|
|
# through it xdg-desktop-portal-cosmic, which is `SystemdService=` in its
|
|
# D-Bus service file -- never saw the variable.
|
|
#
|
|
# xdg-desktop-portal picks a backend by matching the `UseIn=` line of
|
|
# /usr/share/xdg-desktop-portal/portals/*.portal against
|
|
# XDG_CURRENT_DESKTOP. With the variable unset, `UseIn=COSMIC` did not
|
|
# match, no backend loaded, and org.freedesktop.portal.Screenshot was never
|
|
# exported -- so pressing PrtScr made cosmic-screenshot unwrap an error and
|
|
# abort with "COSMIC screenshot crashed". Nothing logged a missing portal;
|
|
# the only symptom was the crash.
|
|
#
|
|
# Ordering is fine: xdg-desktop-portal.service is `After=` and
|
|
# `Requisite=graphical-session.target`, which cosmic-session brings up
|
|
# later, so the portal cannot start before this import.
|
|
#
|
|
# SSH_AUTH_SOCK rides along for the same reason: a user unit or a
|
|
# systemd-activated app that wants the agent has to find it in the manager's
|
|
# environment, not in this script's. It is skipped harmlessly when the
|
|
# keyring block above declined to set it.
|
|
#
|
|
# `||:` throughout -- losing a portal is bad, refusing to log in is worse.
|
|
systemctl --user import-environment \
|
|
XDG_CURRENT_DESKTOP XDG_SESSION_DESKTOP XDG_SESSION_TYPE SSH_AUTH_SOCK ||:
|
|
fi
|
|
|
|
# Same variables again for dbus-daemon's own activation environment, which
|
|
# covers any backend whose .service file has no `SystemdService=` line.
|
|
#
|
|
# Only when a bus already exists. In the dbus-run-session branch below the bus
|
|
# is created *after* this point, so calling it here would talk to the wrong bus
|
|
# or none at all. That branch is the fallback path anyway; a normal graphical
|
|
# login arrives with DBUS_SESSION_BUS_ADDRESS already set.
|
|
if [[ -n "${DBUS_SESSION_BUS_ADDRESS}" ]] && command -v dbus-update-activation-environment >/dev/null; then
|
|
dbus-update-activation-environment \
|
|
XDG_CURRENT_DESKTOP XDG_SESSION_DESKTOP XDG_SESSION_TYPE SSH_AUTH_SOCK ||:
|
|
fi
|
|
|
|
# cosmic-session takes the compositor to launch as its first argument
|
|
# (main.rs: `args.next().unwrap_or_else(|| String::from("cosmic-comp"))`),
|
|
# which is how a forked cosmic-comp gets used without replacing the system one.
|
|
if [[ -z "${DBUS_SESSION_BUS_ADDRESS}" ]]; then
|
|
exec /usr/bin/dbus-run-session -- "${HYPRCOSMIC_SESSION_BIN}" "${HYPRCOSMIC_COMP_BIN}"
|
|
else
|
|
exec "${HYPRCOSMIC_SESSION_BIN}" "${HYPRCOSMIC_COMP_BIN}"
|
|
fi
|