-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[RFE] enforce single graphical session on systemd platforms #2491
Comments
I agree. I think your systemd RFE illustrates that this isn't going to change for systemd-based systems. The difficulty is where to put something like this. We can easily add something to A script in |
here a script that should work. I put it in /etc/X11/Xsession.d/10enforce-single-graphical-session but of course that is debian/ubuntu specific. Also this only works for X11 session. Xrdp should detect pre-existing wayland sessions, but the other way around probably does not work. Good news: it seems all you need to do is stop if systemctl --quiet --user is-active graphical-session.target; then
xmessage -button Logout:1,Terminate\ running\ graphical\ session:2 "Only a single graphical session is allowed.
If you terminate the current graphical session all unsaved data in that session will be lost"
action=$?
if [ $action -eq 2 ]; then
systemctl --user stop graphical-session.target
sleep 1
else
exit 0
fi
fi |
That's a great contribution - thanks. I like the use of I've been looking for a hook to get into Wayland session startup, and so far I've had no luck. There seems to be a lack of documentation on how exactly a Wayland session is started. I found this presentation, this link and there's a bit of info on the gnome-session manpage. A bit of a hacky workaround (for GDM) is to use this to disable Wayland whern xrdp starts:-
|
I think wayland is directly started by startplasma-wayland or gnome-session. in sddm this script is called: https://github.com/sddm/sddm/blob/develop/data/scripts/wayland-session. The way it is called stays the same: the parameter is the "desktop", in plasma startplasma-wayland, gnome probably gnome-session At least startplasma seems to check if the current dbus session has a org.freedesktop.systemd1 service and systemd startup is not explicitly disabled. If systemd is used then plasma-workspace-wayland.target is activated that in turn takes graphical-session.target up. Adding a check to see if graphical-session.target is already running should be fairly simple for them (the same is for plasma-workspace-x11.target) and would IMHO make a lot of sense. |
Good news for those who need multiple concurrent sessions for singe users on a systemd based system: prepare-multisession.sh # workarounds for enabling multiple sessions when using systemd
# mainly the issue is that a desktop session needs a dedicated dbus,
# but systemd creates a single one for every logged on user.
# using systemd-run it is possible to create a separate systemd-user instance
# with dedicated XDG_RUNTIME_DIR and DBUS
# script by mwsys.mine.bz
if [ "$XDG_RUNTIME_DIR" = "/run/user/"`id -u` ]; then
SESSION_RUNTIME_DIR=/run/user/`id -u`/xrdp-` echo $DISPLAY | sed 's/:/display-/g' `
mkdir -p $SESSION_RUNTIME_DIR
chmod 0700 $SESSION_RUNTIME_DIR
# optional: mask specific units
mkdir -p $SESSION_RUNTIME_DIR/systemd/user.control/
ln -s /dev/null $SESSION_RUNTIME_DIR/systemd/user.control/pipewire-pulseaudio.service
ln -s /dev/null $SESSION_RUNTIME_DIR/systemd/user.control/pipewire-pulseaudio.socket
#start systemd service. this must be done using systemd-run to get a proper scope. This mimics user@.service
systemd-run --user -u systemd-xrdp-`echo $DISPLAY | sed 's/:/display-/g'` -E XDG_RUNTIME_DIR=$SESSION_RUNTIME_DIR -E DBUS_SESSION_BUS_ADDRESS=unix:path="$SESSION_RUNTIME_DIR"/bus systemd --user
export DBUS_SESSION_BUS_ADDRESS="unix:path="$SESSION_RUNTIME_DIR"/bus"
export XDG_RUNTIME_DIR=$SESSION_RUNTIME_DIR
if [ "$XDG_SESSION_ID" != "" ]; then
# used by reconnect.sh
echo $XDG_SESSION_ID > $SESSION_RUNTIME_DIR/login-session-id;
fi
#wait for dbus. this will be launched by systemd
while [ ! -e $XDG_RUNTIME_DIR/bus ] ;do sleep 0.3; done
trap ". /etc/xrdp/cleanup-multisession.sh" SIGTERM SIGINT
#explicitly start pulseaudio.socket (not enable to not enable this user wide)
test -e /lib/systemd/user/pulseaudio.socket && systemctl --user start pulseaudio.socket
SESSION_RUNTIME_DIR=
fi cleanup-multisession.sh if [ "$XDG_RUNTIME_DIR" = "/run/user/"`id -u`"/xrdp-"` echo $DISPLAY | sed 's/:/display-/g' ` ]; then
XDG_RUNTIME_DIR="/run/user/"`id -u` DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/"`id -u`/bus systemctl --user stop systemd-xrdp-` echo $DISPLAY | sed 's/:/display-/g' `
rm -rf "$XDG_RUNTIME_DIR"
fi |
Looks good. When I played with this quite a while ago, I had significant problems with the GNOME lock screen, which is implemented by the display manager gdm3 rather than by a separate screen lock program. Comms for that go over the system D-Bus rather than the session one. There might be other interactions with the system as a whole (hot-plugging USB sticks?) which do interesting things. I'll have a play with your scripts when I get a moment - they're certainly a step forward. Thanks. |
The only thing I have to check is how sesman terminates sessions "externally". Maybe I need to handle that signal (2 or 4) and cleanup the session systemd. |
Thanks. The current flow is as follows, but I expect you know most of this already. sesman currently forks when it runs a session. The fork starts the X server, session manager and chansrv. It then waits for the session manager to finish, kills the X server and chansrv and exits. sesman picks up the SIGCHLD and clean up its own data. The sesman fork needs to runs as root so it can call the PAM session cleanup stuff properly. There are some significant problems with the above, mainly #1684 and #800. For what it's worth, I'm currently working on fixing these by changing the way sesman works (see #1961 and this personal wiki page), so there's scope for more flexibility if we need it. |
@matt335672 I made some tests with kubuntu: snap works, but usually the snap packager (most notably ubuntu devs themself) set PULSE_SERVER without checking if it was already set and point to the user wide daemon. screen lock and unlock works, even with |
I've had a big play today with this. The good news is it seems to be working with GNOME on Ubuntu 22.04. I've currently got the following script in # workarounds for enabling multiple sessions when using systemd
# mainly the issue is that a desktop session needs a dedicated dbus,
# but systemd creates a single one for every logged on user.
# using systemd-run it is possible to create a separate systemd-user instance
# with dedicated XDG_RUNTIME_DIR and DBUS
# script by mwsys.mine.bz
if [ -z "$DISPLAY" ]; then
echo "** Warning - no DISPLAY. Assuming test mode" >&2
display_num=test
else
display_num=${DISPLAY##*:}
display_num=${display_num%.*}
fi
unit_name=xrdp-systemd-user-$display_num
SESSION_RUNTIME_DIR=/run/user/`id -u`/$unit_name
install -dm 0700 $SESSION_RUNTIME_DIR
rm -rf $SESSION_RUNTIME_DIR/systemd/user.control/
mkdir -p $SESSION_RUNTIME_DIR/systemd/user.control/
# optional: mask specific units
ln -s /dev/null $SESSION_RUNTIME_DIR/systemd/user.control/pipewire-pulseaudio.service
ln -s /dev/null $SESSION_RUNTIME_DIR/systemd/user.control/pipewire-pulseaudio.socket
# Create a unit to wait for this PID to finish
{
echo "[Unit]"
echo "Description=Wait for XRDP session to finish"
echo "Requires=default.target"
echo
echo "[Service]"
echo "Type=simple"
echo "ExecStart=/bin/sh -c 'while /bin/kill -0 $$; do sleep 10; done'"
echo "ExecStopPost=/usr/bin/systemctl --user exit"
} >$SESSION_RUNTIME_DIR/systemd/user.control/wait-for-xrdp-session.service
# start systemd service. this must be done using systemd-run to get a
# proper scope. This mimics user@.service
#
# Within the system --user process we run wait-for-xrdp-session.service. That
# kill the systemd --user instance when this process finishes
systemd-run --user -u $unit_name \
-E XDG_RUNTIME_DIR=$SESSION_RUNTIME_DIR \
-E DBUS_SESSION_BUS_ADDRESS=unix:path="$SESSION_RUNTIME_DIR"/bus \
systemd --user --unit wait-for-xrdp-session.service
# Switch to the new systemd --user instance
export XDG_RUNTIME_DIR=$SESSION_RUNTIME_DIR
export DBUS_SESSION_BUS_ADDRESS="unix:path="$SESSION_RUNTIME_DIR"/bus"
if [ "$XDG_SESSION_ID" != "" ]; then
# used by reconnect.sh
echo $XDG_SESSION_ID > $SESSION_RUNTIME_DIR/login-session-id;
fi
#wait for dbus. this will be launched by systemd
while [ ! -e $XDG_RUNTIME_DIR/bus ] ;do sleep 0.3; done
#explicitly start pulseaudio.socket (not enable to not enable this user wide)
test -e /lib/systemd/user/pulseaudio.socket && systemctl --user start pulseaudio.socket and in # On systemd system?
if [ -x /usr/bin/systemctl -a \
"$XDG_RUNTIME_DIR" = "/run/user/"`id -u` ]; then
. /etc/xrdp/start_private_systemd_user.sh
fi It's not a big change, but it might make system integration a bit easier. As part of my day, I tried getting the startwm.sh script running as part of the I need to do a bit more testing, but that's for another day. |
@matt335672 nice refactoring 👍 did not know that kill -0 works to check if a pid is still around. Usually I do that with test -d /proc/PID, but kill -0 is probably better since it does not have to traverse the proc filesystem. I would lower the polling rate since a logout/login sequence is doable in 10 seconds. Maybe 2-5 seconds would be better? sddm does not start the x-session-manager as a systemd unit, so I think this is not the way to go. Also the process would not belong to the correct pam session (systemd units are not part of pam sessions) so there might be some issues with logind later |
I've been playing with creating a new mount namespace with a private Sadly the available system calls don't make it possible to share mountpoints in general but have a private @akarl10 - can I ask you about these sets of lines:-
and
and
Are these things you use in your own local setup? |
Just out of curiosity: the private mount you would have be created directly by sesman? What if just a private /run/user would be created (without the uid)? There should not be no need for seeing other users in /run/user
yes, this are private. If you are interrested my reconnect.sh looks like this #!/bin/sh
# unlock session when reconnecting. works in conjunction with multisession workarounds
if [ -e /run/user/`id -u`/xrdp-` echo $DISPLAY | sed 's/:/display-/g' ` ]; then
export XDG_RUNTIME_DIR=/run/user/`id -u`/xrdp-` echo $DISPLAY | sed 's/:/display-/g' `
test -e $XDG_RUNTIME_DIR/login-session-id && loginctl unlock-session `cat $XDG_RUNTIME_DIR/login-session-id`
fi This ensures that I don't have to enter my credentials twice when connecting to an idle connection and locked screen. The pipewire-pulseaudio masking I did because else I would not get remote sound since there is no pipewire audio driver yet for xrdp |
Yes, I'm doing it in sesman. The problem with the mount API, is that the namespace is by default created shared (which is what we want). You can't then create a private mount on that - you have to create a shared mount and make it private. If we do that on I remember doing something very similar for the reconnect for CentOS 7 reconnects, back in my sysadmin days. It's a good point that reconnectwm.sh needs to know what has happened to I'll rework the above (again) to make it a bit more robust, generic and easy to test, with the intention of supporting your reconnectwm.sh functionality. We can then work out a way to get the other stuff in. |
Here's the updated file. It's getting bigger, so it's an attachment. It's now an executable script that takes a number of sub-commands. Calling has changed. Some commands generate environment variables settings, and you parse these with Top of startwm.sh looks like this, plus a stanza for your reconnectwm.sh # On systemd system?
#
# If so, start a private "systemd --user" instance
if [ -x /usr/bin/systemctl -a "$XDG_RUNTIME_DIR" = "/run/user/"`id -u` ]
then
eval "`${0%/*}/systemd_user_context.sh init -p $$`"
# used by reconnect.sh
if [ -n "$XDG_SESSION_ID" ]; then
echo $XDG_SESSION_ID > $XDG_RUNTIME_DIR/login-session-id
fi
fi I've not tried this, but from your reconnectwm.sh, you should be able to do this:- # Connect to systemd --user instance
eval "`${0%/*}/systemd_user_context.sh get`"
if [ -f $XDG_RUNTIME_DIR/login-session-id ]; then
loginctl unlock-session `cat $XDG_RUNTIME_DIR/login-session-id`
fi I've also found a potential problem. If I start an XRDP session with no console session active, and look at
If you've got any ideas about preventing this, let me know - I'm done for today. |
wow, you made a whole tool out of it. Maybe if this gets adopted for other usecases too systemd-user nesting may get proper support from others like snap or default apparmor rules. hmm, that's unfortunate.. with
you could create a parameter list to unset every "main" systemd-user environment variable, or at least empty it. after changing to the new systemd-user with (IFS=$'\n'; for a in `env`; do [ ${a%%=*} != "SYSTEMD_EXEC_PID" ] && systemctl --user set-environment ${a%%=*}; done) you could import the current environment. systemd-run --user -E SYSTEMD_EXEC_PID= --wait /bin/sh -c 'printenv;' |
I think we need something packaged up, so that people can retro-fit this to existing v0.9.x installations. I can see that for enterprise working where a developer may have a single account they use on a machine console, and also remotely from home this would be very useful indeed. I like what you've done there - you've certainly got a better grasp of systemd than I have. I'll have a play and see what else I can find out. |
Tool above added to https://github.com/matt335672/nest-systemd-user. |
…ests to use systemd to start its session, instead of through dbus-run-session Detect if a systemd user enabled session set graphical-session.target to active if trying to start a new session While there is a clever solution in neutrinolabs/xrdp#2491 to workaround the limitation, it doesn't quite work right with Gnome's usage of systemd --user to start the session, and while with KDE's when enabled it does work, but the startup git-svn-id: https://svn.code.sf.net/p/rebeccablackos/code@8049 b52b6941-3400-464c-9f42-43200397181c
Hi there, By configuring the initialization of XRDP, I successfully avoided creating new (black-screen) sessions when a user changes the size and/or color depth from the RDP client - the previous session always gets updated automatically.
Anyway, this practice is very easy to implement, and solves the black screen problem without configuring sesman. No extra script is needed. Hope it helps. Thanks! |
…don't limit to one systemd --user initilized session neutrinolabs/xrdp#2491 (comment) The kwin issue seems to be a recent issue that impacts non systemd initiated instances, and the gnome-shell issue appears when systemd --user is running as normal Two instance of systemd initilized kwin start in parallel. reactivation does work on older versions of kwin The nested instance has its own dbus socket in its own XDG_RUNTIME_DIR (one under the real XDG_RUNTIME_DIR) the guest systemd user service runs as a transient service started by the user's main session. This allows one user to start multiple sessions as themselves, a good use case for users testing multiple desktops and switching between them on a live cd git-svn-id: https://svn.code.sf.net/p/rebeccablackos/code@8052 b52b6941-3400-464c-9f42-43200397181c
Lines 232 to 258 in d71ec3f
Apparently it’s configuration under the |
I use Xvnc as the backend. Specifically, tigervnc-server. @matt335672 |
since systemd/systemd#24932 got rejected by @poettering himself I think the only longterm solution is that every display session manager implements a safeguard that prevents "launching" multiple graphical sessions and display a message to the user that he must log off the offending session (maybe also providing a option to force terminate the offending session)
Multiple graphical sessions per user are simply not possible with systemd. This is by design
if this is not intentional then the broken by design (
graphical-session.target
being a user unit, in addition the assumption that the DBUS_SESSION_BUS_ADDRESS is session exclusive while systemd make this the same for every user session), but still it is not something we can fixIf someone has a better idea how to treat this issue, just propose that, but I think something has to be done given the amount of bug reports that pop up that the display keeps black
some findings:
displays the active sessions. Locally created graphical sessions fill the
DISPLAY
property (xrdp does not do that, but maybe that is only possible if the session is local)A similar thing should be done in sddm/gdm/... (out of scope for this report)
The text was updated successfully, but these errors were encountered: