Add starting headless sessions with C tool
Resolves: RHEL-126603
This commit is contained in:
parent
8ff2823f59
commit
3fef77ad21
@ -1,219 +0,0 @@
|
||||
From a8a0d952293337544da4681f0c896052eafd9d0f Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
|
||||
Date: Fri, 5 Apr 2024 16:44:07 +0200
|
||||
Subject: [PATCH] Add headless session files
|
||||
|
||||
It consists of a python script for running the session, and a systemd
|
||||
system service template.
|
||||
---
|
||||
data/gnome-headless-session@.service | 6 +
|
||||
data/meson.build | 4 +
|
||||
utils/gdm-headless-login-session | 157 +++++++++++++++++++++++++++
|
||||
utils/meson.build | 5 +
|
||||
4 files changed, 172 insertions(+)
|
||||
create mode 100644 data/gnome-headless-session@.service
|
||||
create mode 100644 utils/gdm-headless-login-session
|
||||
|
||||
diff --git a/data/gnome-headless-session@.service b/data/gnome-headless-session@.service
|
||||
new file mode 100644
|
||||
index 000000000..269d16288
|
||||
--- /dev/null
|
||||
+++ b/data/gnome-headless-session@.service
|
||||
@@ -0,0 +1,6 @@
|
||||
+[Unit]
|
||||
+Description=Headless desktop session
|
||||
+
|
||||
+[Service]
|
||||
+ExecStart=/usr/libexec/gdm-headless-login-session --user=%i
|
||||
+Restart=on-failure
|
||||
diff --git a/data/meson.build b/data/meson.build
|
||||
index 2211e98b5..2df07cd32 100644
|
||||
--- a/data/meson.build
|
||||
+++ b/data/meson.build
|
||||
@@ -221,3 +221,7 @@ if get_option('gdm-xsession')
|
||||
install_dir: gdmconfdir,
|
||||
)
|
||||
endif
|
||||
+
|
||||
+headless_session_service = install_data('gnome-headless-session@.service',
|
||||
+ install_dir: systemd_systemunitdir,
|
||||
+ )
|
||||
diff --git a/utils/gdm-headless-login-session b/utils/gdm-headless-login-session
|
||||
new file mode 100644
|
||||
index 000000000..e108be523
|
||||
--- /dev/null
|
||||
+++ b/utils/gdm-headless-login-session
|
||||
@@ -0,0 +1,157 @@
|
||||
+#!/usr/bin/env python3
|
||||
+
|
||||
+import argparse
|
||||
+import pam
|
||||
+import pwd
|
||||
+import os
|
||||
+import signal
|
||||
+import sys
|
||||
+
|
||||
+import gi
|
||||
+gi.require_version('AccountsService', '1.0')
|
||||
+from gi.repository import AccountsService, GLib
|
||||
+
|
||||
+def run_desktop_in_new_session(pam_environment, user, session_desktop, tty_input, tty_output):
|
||||
+ keyfile = GLib.KeyFile()
|
||||
+ keyfile.load_from_data_dirs(f'wayland-sessions/{session_desktop}.desktop',
|
||||
+ GLib.KeyFileFlags.NONE)
|
||||
+
|
||||
+ try:
|
||||
+ can_run_headless = keyfile.get_boolean(GLib.KEY_FILE_DESKTOP_GROUP,
|
||||
+ 'X-GDM-CanRunHeadless')
|
||||
+ except GLib.GError:
|
||||
+ raise Exception(f"Session {session_desktop} can't run headlessly")
|
||||
+
|
||||
+ if not can_run_headless:
|
||||
+ raise Exception(f"Session {session_desktop} can't run headlessly")
|
||||
+
|
||||
+ executable = keyfile.get_string(GLib.KEY_FILE_DESKTOP_GROUP,
|
||||
+ GLib.KEY_FILE_DESKTOP_KEY_TRY_EXEC)
|
||||
+ if GLib.find_program_in_path(executable) is None:
|
||||
+ raise Exception(f"Invalid session {session_desktop}")
|
||||
+
|
||||
+ command = keyfile.get_string(GLib.KEY_FILE_DESKTOP_GROUP,
|
||||
+ GLib.KEY_FILE_DESKTOP_KEY_EXEC)
|
||||
+ [success, args] = GLib.shell_parse_argv(command)
|
||||
+
|
||||
+ pam_handle = pam.pam()
|
||||
+
|
||||
+ for key, value in pam_environment.items():
|
||||
+ pam_handle.putenv(f'{key}={value}')
|
||||
+
|
||||
+ if not pam_handle.authenticate(user, '', service='gdm-autologin', call_end=False):
|
||||
+ raise Exception("Authentication failed")
|
||||
+
|
||||
+ for key, value in pam_environment.items():
|
||||
+ pam_handle.putenv(f'{key}={value}')
|
||||
+
|
||||
+ if pam_handle.open_session() != pam.PAM_SUCCESS:
|
||||
+ raise Exception("Failed to open PAM session")
|
||||
+
|
||||
+ session_environment = os.environ.copy()
|
||||
+ session_environment.update(pam_handle.getenvlist())
|
||||
+
|
||||
+ user_info = pwd.getpwnam(user)
|
||||
+ uid = user_info.pw_uid
|
||||
+ gid = user_info.pw_gid
|
||||
+
|
||||
+ old_tty_output = os.fdopen(os.dup(2), 'w')
|
||||
+
|
||||
+ pid = os.fork()
|
||||
+ if pid == 0:
|
||||
+ try:
|
||||
+ os.setsid()
|
||||
+ except OSError as e:
|
||||
+ print(f"Could not create new pid session: {e}", file=old_tty_output)
|
||||
+
|
||||
+ try:
|
||||
+ os.dup2(tty_input.fileno(), 0)
|
||||
+ os.dup2(tty_output.fileno(), 1)
|
||||
+ os.dup2(tty_output.fileno(), 2)
|
||||
+ except OSError as e:
|
||||
+ print(f"Could not set up standard i/o: {e}", file=old_tty_output)
|
||||
+
|
||||
+ try:
|
||||
+ os.initgroups(user, gid)
|
||||
+ os.setgid(gid)
|
||||
+ os.setuid(uid);
|
||||
+ except OSError as e:
|
||||
+ print(f"Could not become user {user} (uid={uid}): {e}", file=old_tty_output)
|
||||
+
|
||||
+ try:
|
||||
+ os.execvpe(args[0], args, session_environment)
|
||||
+ except OSError as e:
|
||||
+ print(f"Could not run program \"{' '.join(arguments)}\": {e}", file=old_tty_output)
|
||||
+ os._exit(1)
|
||||
+
|
||||
+
|
||||
+ def signal_handler(sig, frame):
|
||||
+ os.kill(pid, sig)
|
||||
+
|
||||
+ signal.signal(signal.SIGTERM, signal_handler)
|
||||
+
|
||||
+ try:
|
||||
+ (_, exit_code) = os.waitpid(pid, 0);
|
||||
+ except KeyboardInterrupt:
|
||||
+ os.kill(pid, signal.SIGTERM)
|
||||
+ except OSError as e:
|
||||
+ print(f"Could not wait for program to finish: {e}", file=old_tty_output)
|
||||
+
|
||||
+ if os.WIFEXITED(exit_code):
|
||||
+ exit_code = os.WEXITSTATUS(exit_code)
|
||||
+ else:
|
||||
+ os.kill(os.getpid(), os.WTERMSIG(exit_code))
|
||||
+ old_tty_output.close()
|
||||
+
|
||||
+ if pam_handle.close_session() != pam.PAM_SUCCESS:
|
||||
+ raise Exception("Failed to close PAM session")
|
||||
+
|
||||
+ pam_handle.end()
|
||||
+
|
||||
+ return exit_code
|
||||
+
|
||||
+def wait_for_user_data(user):
|
||||
+ main_context = GLib.MainContext.default()
|
||||
+ while not user.is_loaded():
|
||||
+ main_context.iteration(True)
|
||||
+
|
||||
+def main():
|
||||
+ parser = argparse.ArgumentParser(description='Run a desktop session in a PAM session as a specified user.')
|
||||
+ parser.add_argument('--user', help='Username for which to run the session')
|
||||
+
|
||||
+ args = parser.parse_args()
|
||||
+
|
||||
+ if args.user is None:
|
||||
+ parser.print_usage()
|
||||
+ sys.exit(1)
|
||||
+
|
||||
+ try:
|
||||
+ tty_path = '/dev/null'
|
||||
+
|
||||
+ tty_input = open(tty_path, 'r')
|
||||
+ tty_output = open(tty_path, 'w')
|
||||
+ except OSError as e:
|
||||
+ raise Exception(f"Error opening /dev/null as tty associated with VT {vt}: {e}")
|
||||
+
|
||||
+ user_manager = AccountsService.UserManager().get_default()
|
||||
+ user = user_manager.get_user(args.user)
|
||||
+ wait_for_user_data(user)
|
||||
+ session_desktop = user.get_session()
|
||||
+ if not session_desktop:
|
||||
+ session_desktop = 'gnome'
|
||||
+
|
||||
+ pam_environment = {}
|
||||
+ pam_environment['XDG_SESSION_TYPE'] = 'wayland'
|
||||
+ pam_environment['XDG_SESSION_CLASS'] = 'user'
|
||||
+ pam_environment['XDG_SESSION_DESKTOP'] = session_desktop
|
||||
+
|
||||
+ try:
|
||||
+ result = run_desktop_in_new_session(pam_environment, args.user, session_desktop, tty_input, tty_output)
|
||||
+ except Exception as e:
|
||||
+ raise Exception(f"Error running desktop session \"{session_desktop}\": {e}")
|
||||
+ tty_input.close()
|
||||
+ tty_output.close()
|
||||
+ sys.exit(result)
|
||||
+
|
||||
+if __name__ == '__main__':
|
||||
+ main()
|
||||
diff --git a/utils/meson.build b/utils/meson.build
|
||||
index e4141fb13..57dd6519f 100644
|
||||
--- a/utils/meson.build
|
||||
+++ b/utils/meson.build
|
||||
@@ -65,3 +65,8 @@ if distro != 'none'
|
||||
install_dir: get_option('libexecdir'),
|
||||
)
|
||||
endif
|
||||
+
|
||||
+gdm_headless_login_session = install_data('gdm-headless-login-session',
|
||||
+ install_mode: 'rwxr-xr-x',
|
||||
+ install_dir: get_option('libexecdir'),
|
||||
+ )
|
||||
--
|
||||
2.44.0
|
||||
|
||||
1937
0001-Introduce-gdm-new-session-tool.patch
Normal file
1937
0001-Introduce-gdm-new-session-tool.patch
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,28 +0,0 @@
|
||||
From e938a72b8ee65b7db2ad76f63dc3f77713871a82 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
|
||||
Date: Thu, 3 Jul 2025 12:09:40 +0200
|
||||
Subject: [PATCH] headless-session: Fix autostarting on boot
|
||||
|
||||
Make it wanted by graphical.target, and make sure it launches after gdm.
|
||||
---
|
||||
data/gnome-headless-session@.service | 4 ++++
|
||||
1 file changed, 4 insertions(+)
|
||||
|
||||
diff --git a/data/gnome-headless-session@.service b/data/gnome-headless-session@.service
|
||||
index 269d16288..cba7526f8 100644
|
||||
--- a/data/gnome-headless-session@.service
|
||||
+++ b/data/gnome-headless-session@.service
|
||||
@@ -1,6 +1,10 @@
|
||||
[Unit]
|
||||
Description=Headless desktop session
|
||||
+After=multi-user.target rescue.service rescue.target display-manager.service
|
||||
|
||||
[Service]
|
||||
ExecStart=/usr/libexec/gdm-headless-login-session --user=%i
|
||||
Restart=on-failure
|
||||
+
|
||||
+[Install]
|
||||
+WantedBy=graphical.target
|
||||
--
|
||||
2.49.0
|
||||
|
||||
15
gdm.spec
15
gdm.spec
@ -23,7 +23,6 @@ Source6: gdm.sysusers
|
||||
# Downstream patches
|
||||
Patch: 0001-Honor-initial-setup-being-disabled-by-distro-install.patch
|
||||
Patch: 0001-data-add-system-dconf-databases-to-gdm-profile.patch
|
||||
Patch: 0001-Add-headless-session-files.patch
|
||||
Patch: 0002-data-Drop-X11-fallback-rules.patch
|
||||
|
||||
# RHEL-4104
|
||||
@ -38,12 +37,14 @@ Patch: 0001-meson-Define-missing-HAVE_LIBAUDIT.patch
|
||||
# https://gitlab.gnome.org/GNOME/gdm/-/merge_requests/298
|
||||
Patch: 0001-session-Fix-memory-leak-on-new-outside-connection.patch
|
||||
|
||||
# RHEL-69319
|
||||
Patch: 0001-headless-session-Fix-autostarting-on-boot.patch
|
||||
|
||||
#RHEL-62663
|
||||
# RHEL-62663
|
||||
# https://gitlab.gnome.org/GNOME/gdm/-/merge_requests/303
|
||||
Patch: 0001-manager-Quit-plymouth-when-no-local-display-is-avail.patch
|
||||
|
||||
# RHEL-126603
|
||||
# https://gitlab.gnome.org/GNOME/gdm/-/merge_requests/288
|
||||
Patch: 0001-Introduce-gdm-new-session-tool.patch
|
||||
|
||||
BuildRequires: dconf
|
||||
BuildRequires: desktop-file-utils
|
||||
BuildRequires: gettext-devel
|
||||
@ -255,7 +256,7 @@ fi
|
||||
%{_libexecdir}/gdm-runtime-config
|
||||
%{_libexecdir}/gdm-session-worker
|
||||
%{_libexecdir}/gdm-wayland-session
|
||||
%{_libexecdir}/gdm-headless-login-session
|
||||
%{_libexecdir}/gdm-new-session
|
||||
%{_sbindir}/gdm
|
||||
%{_bindir}/gdmflexiserver
|
||||
%{_bindir}/gdm-config
|
||||
@ -271,6 +272,8 @@ fi
|
||||
%{_datadir}/gdm/locale.alias
|
||||
%{_datadir}/gdm/gdb-cmd
|
||||
%{_datadir}/gnome-session/sessions/gnome-login.session
|
||||
%{_datadir}/polkit-1/rules.d/20-gdm.rules
|
||||
%{_datadir}/polkit-1/actions/org.gnome.displaymanager.policy
|
||||
%{_libdir}/girepository-1.0/Gdm-1.0.typelib
|
||||
%{_libdir}/security/pam_gdm.so
|
||||
%{_libdir}/libgdm*.so*
|
||||
|
||||
Loading…
Reference in New Issue
Block a user