diff --git a/cmake/FindSystemd.cmake b/cmake/FindSystemd.cmake index 3edc04195..b8d9fb7ec 100644 --- a/cmake/FindSystemd.cmake +++ b/cmake/FindSystemd.cmake @@ -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 ) diff --git a/cmake/packaging/linux.cmake b/cmake/packaging/linux.cmake index 7b605d066..ad2b8a4af 100644 --- a/cmake/packaging/linux.cmake +++ b/cmake/packaging/linux.cmake @@ -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() diff --git a/cmake/prep/special_package_configuration.cmake b/cmake/prep/special_package_configuration.cmake index a038cef25..211ffe14b 100644 --- a/cmake/prep/special_package_configuration.cmake +++ b/cmake/prep/special_package_configuration.cmake @@ -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}) diff --git a/docs/getting_started.md b/docs/getting_started.md index 110aca728..a6596ce5f 100644 --- a/docs/getting_started.md +++ b/docs/getting_started.md @@ -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. diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index 6be4bb6c0..eae5692b9 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -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 diff --git a/packaging/linux/00-app-dev.lizardbyte.app.Sunshine.preset.in b/packaging/linux/00-app-dev.lizardbyte.app.Sunshine.preset.in deleted file mode 100644 index cd8394752..000000000 --- a/packaging/linux/00-app-dev.lizardbyte.app.Sunshine.preset.in +++ /dev/null @@ -1,4 +0,0 @@ -# @PROJECT_DESCRIPTION@ -# The unprivileged service should preset to disabled - -disable app-@PROJECT_FQDN@.service diff --git a/packaging/linux/app-dev.lizardbyte.app.Sunshine.service.in b/packaging/linux/app-dev.lizardbyte.app.Sunshine.service.in index 7ae9487e5..f3b26184b 100644 --- a/packaging/linux/app-dev.lizardbyte.app.Sunshine.service.in +++ b/packaging/linux/app-dev.lizardbyte.app.Sunshine.service.in @@ -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 diff --git a/packaging/linux/copr/Sunshine.spec b/packaging/linux/copr/Sunshine.spec index 11f3c890b..ece778aaa 100644 --- a/packaging/linux/copr/Sunshine.spec +++ b/packaging/linux/copr/Sunshine.spec @@ -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 diff --git a/packaging/linux/sunshine.service.in b/packaging/linux/sunshine.service.in deleted file mode 100644 index ea8feb314..000000000 --- a/packaging/linux/sunshine.service.in +++ /dev/null @@ -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 diff --git a/src/platform/linux/kmsgrab.cpp b/src/platform/linux/kmsgrab.cpp index a64563b82..c395791ce 100644 --- a/src/platform/linux/kmsgrab.cpp +++ b/src/platform/linux/kmsgrab.cpp @@ -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; } diff --git a/src/platform/linux/portalgrab.cpp b/src/platform/linux/portalgrab.cpp index fb1da4f9f..6e2ab0e7b 100644 --- a/src/platform/linux/portalgrab.cpp +++ b/src/platform/linux/portalgrab.cpp @@ -31,6 +31,12 @@ #include "vaapi.h" #include "wayland.h" +#if !defined(__FreeBSD__) + // platform includes + #include + #include +#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 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);