fix(linux/xdgportal): allow unified systemd service for all capture methods (#4854)

This commit is contained in:
Conn O'Griofa
2026-03-16 18:25:30 +00:00
committed by GitHub
parent ff33670c84
commit 253e624ee8
11 changed files with 56 additions and 67 deletions
-6
View File
@@ -19,11 +19,6 @@ IF (NOT WIN32)
OUTPUT_STRIP_TRAILING_WHITESPACE
OUTPUT_VARIABLE SYSTEMD_USER_UNIT_INSTALL_DIR)
execute_process(COMMAND ${PKG_CONFIG_EXECUTABLE}
--variable=systemd_user_preset_dir systemd
OUTPUT_STRIP_TRAILING_WHITESPACE
OUTPUT_VARIABLE SYSTEMD_USER_PRESET_INSTALL_DIR)
execute_process(COMMAND ${PKG_CONFIG_EXECUTABLE}
--variable=systemd_system_unit_dir systemd
OUTPUT_STRIP_TRAILING_WHITESPACE
@@ -36,7 +31,6 @@ IF (NOT WIN32)
mark_as_advanced(
SYSTEMD_USER_UNIT_INSTALL_DIR
SYSTEMD_USER_PRESET_INSTALL_DIR
SYSTEMD_SYSTEM_UNIT_INSTALL_DIR
SYSTEMD_MODULES_LOAD_DIR
)
-8
View File
@@ -18,10 +18,6 @@ if(${SUNSHINE_BUILD_APPIMAGE} OR ${SUNSHINE_BUILD_FLATPAK})
DESTINATION "${SUNSHINE_ASSETS_DIR}/modules-load.d")
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/app-${PROJECT_FQDN}.service"
DESTINATION "${SUNSHINE_ASSETS_DIR}/systemd/user")
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/sunshine.service"
DESTINATION "${SUNSHINE_ASSETS_DIR}/systemd/user")
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/00-app-${PROJECT_FQDN}.preset"
DESTINATION "${SUNSHINE_ASSETS_DIR}/systemd/user-preset")
else()
find_package(Systemd)
find_package(Udev)
@@ -33,10 +29,6 @@ else()
if(SYSTEMD_FOUND)
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/app-${PROJECT_FQDN}.service"
DESTINATION "${SYSTEMD_USER_UNIT_INSTALL_DIR}")
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/sunshine.service"
DESTINATION "${SYSTEMD_USER_UNIT_INSTALL_DIR}")
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/00-app-${PROJECT_FQDN}.preset"
DESTINATION "${SYSTEMD_USER_PRESET_INSTALL_DIR}")
install(FILES "${SUNSHINE_SOURCE_ASSETS_DIR}/linux/misc/60-sunshine.conf"
DESTINATION "${SYSTEMD_MODULES_LOAD_DIR}")
endif()
@@ -25,8 +25,6 @@ elseif(UNIX)
# configure service
configure_file(packaging/linux/app-${PROJECT_FQDN}.service.in app-${PROJECT_FQDN}.service @ONLY)
configure_file(packaging/linux/sunshine.service.in sunshine.service @ONLY)
configure_file(packaging/linux/00-app-${PROJECT_FQDN}.preset.in 00-app-${PROJECT_FQDN}.preset @ONLY)
# configure the arch linux pkgbuild
if(${SUNSHINE_CONFIGURE_PKGBUILD})
+7 -8
View File
@@ -447,21 +447,20 @@ After adding yourself to the group, log out and log back in for the changes to t
#### Services
> [!NOTE]
> Two service unit files are available. Pick "app-dev.lizardbyte.app.Sunshine" for unprivileged XDG Portal
> or X11 capture, otherwise pick "sunshine" for privileged KMS capture.
**Start once**
```bash
systemctl --user start sunshine
systemctl --user start app-dev.lizardbyte.app.Sunshine
```
**Start on boot (privileged; swap names for unprivileged XDG capture)**
**Start on boot**
```bash
systemctl --user --now disable app-dev.lizardbyte.app.Sunshine
systemctl --user --now enable sunshine
systemctl --user --now enable app-dev.lizardbyte.app.Sunshine
```
> [!NOTE]
> The service has been renamed to "app-dev.lizardbyte.app.Sunshine" in order to increase compatibility with
> XDG Desktop Portal, but it is also aliased to "sunshine.service" for convenience.
### macOS
The first time you start Sunshine, you will be asked to grant access to screen recording and your microphone.
+10 -7
View File
@@ -160,18 +160,21 @@ sudo usermod -aG input $USER
```
### KMS Streaming fails
If screencasting fails with KMS, you may be using the unprivileged app-dev.lizardbyte.app.Sunshine service unit.
Switch to the privileged sunshine service:
KMS screencasting requires elevated privileges which are not allowed for Flatpak or AppImage packages.
This means that you must install Sunshine using the native package format of your distribution, if available.
KMS capture will soon be phased out in favour of XDG Portal Capture (which works with all package types).
### KMS Streaming; some windows flicker/disappear on KDE Plasma 6.5+
KWin's overlay support interferes with KMS capture. As of KWin 6.5 this is not yet set by default, but
for future versions that enables this by default, you may be able to disable again via a special
[environment variable](https://invent.kde.org/plasma/kwin/-/wikis/Environment-Variables#kwin_use_overlays):
```bash
systemctl --user --now disable app-dev.lizardbyte.app.Sunshine
systemctl --user --now enable sunshine
export KWIN_USE_OVERLAYS=0
```
> [!NOTE]
> The above commands will not work with the AppImage or Flatpak packages, as KMS screencasting
> requires elevated privileges which are not allowed by their respective packaging security policies.
> As an alternative, XDG Portal capture is recommended.
> Disabling overlays will reduce KWin's rendering efficiency. Consider using XDG Portal Capture instead.
### KMS streaming fails on Nvidia GPUs
If KMS screen capture results in a black screen being streamed, you may need to
@@ -1,4 +0,0 @@
# @PROJECT_DESCRIPTION@
# The unprivileged service should preset to disabled
disable app-@PROJECT_FQDN@.service
@@ -2,7 +2,6 @@
Description=@PROJECT_DESCRIPTION@
StartLimitIntervalSec=500
StartLimitBurst=5
Conflicts=sunshine.service
After=graphical-session.target xdg-desktop-autostart.target xdg-desktop-portal.service
[Service]
@@ -12,7 +11,7 @@ ExecStartPre=/bin/sleep 5
@SUNSHINE_SERVICE_STOP_COMMAND@
Restart=on-failure
RestartSec=5s
NoNewPrivileges=true
[Install]
WantedBy=graphical-session.target
Alias=sunshine.service
+1 -2
View File
@@ -390,9 +390,8 @@ fi
%caps(cap_sys_admin+p) %{_bindir}/sunshine
%caps(cap_sys_admin+p) %{_bindir}/sunshine-*
# Systemd unit/preset files for user services
# Systemd unit files for user services
%{_userunitdir}/*.service
%{_userpresetdir}/*.preset
# Udev rules
%{_udevrulesdir}/*-sunshine.rules
-17
View File
@@ -1,17 +0,0 @@
[Unit]
Description=@PROJECT_DESCRIPTION@
StartLimitIntervalSec=500
StartLimitBurst=5
Conflicts=app-@PROJECT_FQDN@.service
After=graphical-session.target xdg-desktop-autostart.target
[Service]
# Avoid starting Sunshine before the desktop is fully initialized.
ExecStartPre=/bin/sleep 5
@SUNSHINE_SERVICE_START_COMMAND@
@SUNSHINE_SERVICE_STOP_COMMAND@
Restart=on-failure
RestartSec=5s
[Install]
WantedBy=graphical-session.target
-11
View File
@@ -1684,17 +1684,6 @@ namespace platf {
#if defined(SUNSHINE_BUILD_FLATPAK) || defined(SUNSHINE_BUILD_APPIMAGE)
BOOST_LOG((config::video.capture == "kms") ? fatal : error)
<< "AppImage and Flatpak do not support KMS capture. Use another capture method."sv;
#else
{
const std::string kms_msg =
"You must use the 'sunshine' service instead of the 'app-" +
std::string(PROJECT_FQDN) +
"' service for KMS capture.\n"
"Please refer to the official documentation:\n"
" stable: https://docs.lizardbyte.dev/projects/sunshine/latest/md_docs_2getting__started.html#linux-1\n"
" beta: https://docs.lizardbyte.dev/projects/sunshine/master/md_docs_2getting__started.html#linux-1";
BOOST_LOG((config::video.capture == "kms") ? fatal : error) << kms_msg;
}
#endif
break;
}
+37
View File
@@ -31,6 +31,12 @@
#include "vaapi.h"
#include "wayland.h"
#if !defined(__FreeBSD__)
// platform includes
#include <sys/capability.h>
#include <sys/prctl.h>
#endif
namespace {
// Buffer and limit constants
constexpr int SPA_POD_BUFFER_SIZE = 4096;
@@ -223,12 +229,43 @@ namespace portal {
return 0;
}
void finalize_portal_security() {
#if !defined(__FreeBSD__)
BOOST_LOG(debug) << "Finalizing Portal security: dropping CAP_SYS_ADMIN and resetting dumpable"sv;
cap_t caps = cap_get_proc();
if (!caps) {
BOOST_LOG(error) << "Failed to get process capabilities"sv;
return;
}
std::array<cap_value_t, 1> remove_list {CAP_SYS_ADMIN};
cap_set_flag(caps, CAP_PERMITTED, remove_list.size(), remove_list.data(), CAP_CLEAR);
cap_set_flag(caps, CAP_EFFECTIVE, remove_list.size(), remove_list.data(), CAP_CLEAR);
if (cap_set_proc(caps) != 0) {
BOOST_LOG(error) << "Failed to prune capabilities: "sv << std::strerror(errno);
}
cap_free(caps);
// Reset dumpable AFTER the caps have been pruned to ensure the Portal can
// access /proc/pid/root.
if (prctl(PR_SET_DUMPABLE, 1) != 0) {
BOOST_LOG(error) << "Failed to set PR_SET_DUMPABLE: "sv << std::strerror(errno);
}
#endif
}
int connect_to_portal() {
g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, FALSE);
g_autofree gchar *session_path = nullptr;
g_autofree gchar *session_token = nullptr;
create_session_path(conn, nullptr, &session_token);
// Drop CAP_SYS_ADMIN and set DUMPABLE flag to allow XDG /root access
finalize_portal_security();
// Try combined RemoteDesktop + ScreenCast session first
bool use_screencast_only = !try_remote_desktop_session(loop, &session_path, session_token);