fix(linux/xdgportal): allow unified systemd service for all capture methods (#4854)
This commit is contained in:
@@ -19,11 +19,6 @@ IF (NOT WIN32)
|
|||||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||||
OUTPUT_VARIABLE SYSTEMD_USER_UNIT_INSTALL_DIR)
|
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}
|
execute_process(COMMAND ${PKG_CONFIG_EXECUTABLE}
|
||||||
--variable=systemd_system_unit_dir systemd
|
--variable=systemd_system_unit_dir systemd
|
||||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||||
@@ -36,7 +31,6 @@ IF (NOT WIN32)
|
|||||||
|
|
||||||
mark_as_advanced(
|
mark_as_advanced(
|
||||||
SYSTEMD_USER_UNIT_INSTALL_DIR
|
SYSTEMD_USER_UNIT_INSTALL_DIR
|
||||||
SYSTEMD_USER_PRESET_INSTALL_DIR
|
|
||||||
SYSTEMD_SYSTEM_UNIT_INSTALL_DIR
|
SYSTEMD_SYSTEM_UNIT_INSTALL_DIR
|
||||||
SYSTEMD_MODULES_LOAD_DIR
|
SYSTEMD_MODULES_LOAD_DIR
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -18,10 +18,6 @@ if(${SUNSHINE_BUILD_APPIMAGE} OR ${SUNSHINE_BUILD_FLATPAK})
|
|||||||
DESTINATION "${SUNSHINE_ASSETS_DIR}/modules-load.d")
|
DESTINATION "${SUNSHINE_ASSETS_DIR}/modules-load.d")
|
||||||
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/app-${PROJECT_FQDN}.service"
|
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/app-${PROJECT_FQDN}.service"
|
||||||
DESTINATION "${SUNSHINE_ASSETS_DIR}/systemd/user")
|
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()
|
else()
|
||||||
find_package(Systemd)
|
find_package(Systemd)
|
||||||
find_package(Udev)
|
find_package(Udev)
|
||||||
@@ -33,10 +29,6 @@ else()
|
|||||||
if(SYSTEMD_FOUND)
|
if(SYSTEMD_FOUND)
|
||||||
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/app-${PROJECT_FQDN}.service"
|
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/app-${PROJECT_FQDN}.service"
|
||||||
DESTINATION "${SYSTEMD_USER_UNIT_INSTALL_DIR}")
|
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"
|
install(FILES "${SUNSHINE_SOURCE_ASSETS_DIR}/linux/misc/60-sunshine.conf"
|
||||||
DESTINATION "${SYSTEMD_MODULES_LOAD_DIR}")
|
DESTINATION "${SYSTEMD_MODULES_LOAD_DIR}")
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
@@ -25,8 +25,6 @@ elseif(UNIX)
|
|||||||
|
|
||||||
# configure service
|
# configure service
|
||||||
configure_file(packaging/linux/app-${PROJECT_FQDN}.service.in app-${PROJECT_FQDN}.service @ONLY)
|
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
|
# configure the arch linux pkgbuild
|
||||||
if(${SUNSHINE_CONFIGURE_PKGBUILD})
|
if(${SUNSHINE_CONFIGURE_PKGBUILD})
|
||||||
|
|||||||
@@ -447,21 +447,20 @@ After adding yourself to the group, log out and log back in for the changes to t
|
|||||||
|
|
||||||
#### Services
|
#### 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**
|
**Start once**
|
||||||
```bash
|
```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
|
```bash
|
||||||
systemctl --user --now disable app-dev.lizardbyte.app.Sunshine
|
systemctl --user --now enable app-dev.lizardbyte.app.Sunshine
|
||||||
systemctl --user --now enable 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
|
### macOS
|
||||||
The first time you start Sunshine, you will be asked to grant access to screen recording and your microphone.
|
The first time you start Sunshine, you will be asked to grant access to screen recording and your microphone.
|
||||||
|
|
||||||
|
|||||||
@@ -160,18 +160,21 @@ sudo usermod -aG input $USER
|
|||||||
```
|
```
|
||||||
|
|
||||||
### KMS Streaming fails
|
### KMS Streaming fails
|
||||||
If screencasting fails with KMS, you may be using the unprivileged app-dev.lizardbyte.app.Sunshine service unit.
|
KMS screencasting requires elevated privileges which are not allowed for Flatpak or AppImage packages.
|
||||||
Switch to the privileged sunshine service:
|
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
|
```bash
|
||||||
systemctl --user --now disable app-dev.lizardbyte.app.Sunshine
|
export KWIN_USE_OVERLAYS=0
|
||||||
systemctl --user --now enable sunshine
|
|
||||||
```
|
```
|
||||||
|
|
||||||
> [!NOTE]
|
> [!NOTE]
|
||||||
> The above commands will not work with the AppImage or Flatpak packages, as KMS screencasting
|
> Disabling overlays will reduce KWin's rendering efficiency. Consider using XDG Portal Capture instead.
|
||||||
> requires elevated privileges which are not allowed by their respective packaging security policies.
|
|
||||||
> As an alternative, XDG Portal capture is recommended.
|
|
||||||
|
|
||||||
### KMS streaming fails on Nvidia GPUs
|
### KMS streaming fails on Nvidia GPUs
|
||||||
If KMS screen capture results in a black screen being streamed, you may need to
|
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@
|
Description=@PROJECT_DESCRIPTION@
|
||||||
StartLimitIntervalSec=500
|
StartLimitIntervalSec=500
|
||||||
StartLimitBurst=5
|
StartLimitBurst=5
|
||||||
Conflicts=sunshine.service
|
|
||||||
After=graphical-session.target xdg-desktop-autostart.target xdg-desktop-portal.service
|
After=graphical-session.target xdg-desktop-autostart.target xdg-desktop-portal.service
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
@@ -12,7 +11,7 @@ ExecStartPre=/bin/sleep 5
|
|||||||
@SUNSHINE_SERVICE_STOP_COMMAND@
|
@SUNSHINE_SERVICE_STOP_COMMAND@
|
||||||
Restart=on-failure
|
Restart=on-failure
|
||||||
RestartSec=5s
|
RestartSec=5s
|
||||||
NoNewPrivileges=true
|
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=graphical-session.target
|
WantedBy=graphical-session.target
|
||||||
|
Alias=sunshine.service
|
||||||
|
|||||||
@@ -390,9 +390,8 @@ fi
|
|||||||
%caps(cap_sys_admin+p) %{_bindir}/sunshine
|
%caps(cap_sys_admin+p) %{_bindir}/sunshine
|
||||||
%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
|
%{_userunitdir}/*.service
|
||||||
%{_userpresetdir}/*.preset
|
|
||||||
|
|
||||||
# Udev rules
|
# Udev rules
|
||||||
%{_udevrulesdir}/*-sunshine.rules
|
%{_udevrulesdir}/*-sunshine.rules
|
||||||
|
|||||||
@@ -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
|
|
||||||
@@ -1684,17 +1684,6 @@ namespace platf {
|
|||||||
#if defined(SUNSHINE_BUILD_FLATPAK) || defined(SUNSHINE_BUILD_APPIMAGE)
|
#if defined(SUNSHINE_BUILD_FLATPAK) || defined(SUNSHINE_BUILD_APPIMAGE)
|
||||||
BOOST_LOG((config::video.capture == "kms") ? fatal : error)
|
BOOST_LOG((config::video.capture == "kms") ? fatal : error)
|
||||||
<< "AppImage and Flatpak do not support KMS capture. Use another capture method."sv;
|
<< "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
|
#endif
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,12 @@
|
|||||||
#include "vaapi.h"
|
#include "vaapi.h"
|
||||||
#include "wayland.h"
|
#include "wayland.h"
|
||||||
|
|
||||||
|
#if !defined(__FreeBSD__)
|
||||||
|
// platform includes
|
||||||
|
#include <sys/capability.h>
|
||||||
|
#include <sys/prctl.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
// Buffer and limit constants
|
// Buffer and limit constants
|
||||||
constexpr int SPA_POD_BUFFER_SIZE = 4096;
|
constexpr int SPA_POD_BUFFER_SIZE = 4096;
|
||||||
@@ -223,12 +229,43 @@ namespace portal {
|
|||||||
return 0;
|
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() {
|
int connect_to_portal() {
|
||||||
g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, FALSE);
|
g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, FALSE);
|
||||||
g_autofree gchar *session_path = nullptr;
|
g_autofree gchar *session_path = nullptr;
|
||||||
g_autofree gchar *session_token = nullptr;
|
g_autofree gchar *session_token = nullptr;
|
||||||
create_session_path(conn, nullptr, &session_token);
|
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
|
// Try combined RemoteDesktop + ScreenCast session first
|
||||||
bool use_screencast_only = !try_remote_desktop_session(loop, &session_path, session_token);
|
bool use_screencast_only = !try_remote_desktop_session(loop, &session_path, session_token);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user