diff -Nru sddm-0.19.0/ChangeLog sddm-0.20.0/ChangeLog --- sddm-0.19.0/ChangeLog 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/ChangeLog 2023-06-23 12:28:38.000000000 +0000 @@ -4,6 +4,33 @@ - Bug fixes * Others +## 0.20.0 - 2023-06-23 +---------------------- + + Initial Qt6 support (Will break themes which rely on Qt 5) + + **Experimental** support for running the greeter with Wayland + + Enable HiDPI scaling by default + + Support for running X11 display server without root privileges + + Greeter: Support setting environment variables + + Allow additional env vars to be defined in session files (#1370) + + Make accountsservice data directory overridable via CMake + + Add support for X11 cursor size configuration + + Search XDG Base Directories for session files + + Display information and errors from PAM in the greeter (#1486) + * Remove the Passwd backend, make PAM mandatory + * Bump minimum CMake version to 3.4 + * Introduce SDDM_INITIAL_VT as the TTY to reach out to + * Set XCURSOR_SIZE in XorgDisplayServer::start + * Make it possible to start ConsoleKit D-Bus service during SDDM startup + * pam: Do not use tally2 if faillock is present + * Bump to Qt 5.15, port away from deprecated APIs + * remove `-logfile` arg that causes server to fail + - Set RUNTIME_DIR to /run/sddm when using systemd to follow FHS 3.0 + - Use avatars in FacesDir first and if not found search other locations + - Switch to using libxau with `FamilyWild` (#1230) + - New interface to access config values from themes (#1097) + - Session names are translated now (#1645) + - Many more bugfixes + ## 0.19.0 - 2020-11-02 ---------------------- * Don't disable authentication in --test-mode @@ -33,9 +60,9 @@ * Use modern connects * Update translations -## 0.18.1 - 2019-03-31 +## 0.18.1 - 2019-03-11 ---------------------- - * Use C++ scoping for handling buffer deletion + - Fix crashes when creating a user session ## 0.18.0 - 2018-07-17 ---------------------- diff -Nru sddm-0.19.0/cmake/FindPAM.cmake sddm-0.20.0/cmake/FindPAM.cmake --- sddm-0.19.0/cmake/FindPAM.cmake 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/cmake/FindPAM.cmake 2023-06-23 12:28:38.000000000 +0000 @@ -13,6 +13,7 @@ find_path(PAM_INCLUDE_DIR NAMES security/pam_appl.h pam/pam_appl.h) find_library(PAM_LIBRARY pam) find_library(DL_LIBRARY dl) +find_library(HAVE_PAM_FAILLOCK NAME pam_faillock.so PATH_SUFFIXES security) if (PAM_INCLUDE_DIR AND PAM_LIBRARY) set(PAM_FOUND TRUE) diff -Nru sddm-0.19.0/CMakeLists.txt sddm-0.20.0/CMakeLists.txt --- sddm-0.19.0/CMakeLists.txt 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/CMakeLists.txt 2023-06-23 12:28:38.000000000 +0000 @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.8) +cmake_minimum_required(VERSION 3.4) project(SDDM) @@ -6,16 +6,12 @@ set(CMAKE_AUTOMOC ON) SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) -# Silence CMake 3.0 warnings -if(POLICY CMP0043) - cmake_policy(SET CMP0043 OLD) -endif() - # Set version set(SDDM_VERSION_MAJOR 0) -set(SDDM_VERSION_MINOR 19) +set(SDDM_VERSION_MINOR 20) set(SDDM_VERSION_PATCH 0) set(SDDM_VERSION_STRING "${SDDM_VERSION_MAJOR}.${SDDM_VERSION_MINOR}.${SDDM_VERSION_PATCH}") +add_compile_definitions("SDDM_VERSION=\"${SDDM_VERSION_STRING}\"") # Set up packaging set(CPACK_PACKAGE_NAME "sddm") @@ -30,16 +26,19 @@ # Options option(BUILD_MAN_PAGES "Build man pages" OFF) option(ENABLE_JOURNALD "Enable logging to journald" ON) -option(ENABLE_PAM "Enable PAM support" ON) option(NO_SYSTEMD "Disable systemd support" OFF) option(USE_ELOGIND "Use elogind instead of logind" OFF) +option(BUILD_WITH_QT6 "Build with Qt 6" OFF) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) # ECM find_package(ECM 1.4.0 REQUIRED NO_MODULE) set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake;${CMAKE_MODULE_PATH};${ECM_MODULE_PATH}") # Definitions -add_definitions(-Wall -std=c++11 -DQT_NO_CAST_FROM_ASCII -DQT_NO_CAST_FROM_BYTEARRAY -DQT_NO_FOREACH) +add_definitions(-Wall -Wextra -DQT_NO_CAST_FROM_ASCII -DQT_NO_CAST_FROM_BYTEARRAY -DQT_NO_FOREACH) # Default build type if(NOT CMAKE_BUILD_TYPE) @@ -75,18 +74,10 @@ find_package(PkgConfig) # PAM -if(ENABLE_PAM) - find_package(PAM REQUIRED) - - if(PAM_FOUND) - add_definitions(-DUSE_PAM) - endif() -endif() -add_feature_info("PAM" PAM_FOUND "PAM support") +find_package(PAM REQUIRED) -# getspnam and shadow(5) support -include(CheckFunctionExists) -check_function_exists(getspnam HAVE_GETSPNAM) +# XAU +pkg_check_modules(LIBXAU REQUIRED "xau") # XCB find_package(XCB REQUIRED) @@ -94,13 +85,21 @@ # XKB find_package(XKB REQUIRED) -# Qt 5 -find_package(Qt5 5.8.0 CONFIG REQUIRED Core DBus Gui Qml Quick LinguistTools Test) +# Qt +if(BUILD_WITH_QT6) + set(QT_MAJOR_VERSION 6) + message(STATUS "Building Qt 6 version") +else() + set(QT_MAJOR_VERSION 5) + message(STATUS "Building Qt 5 version") +endif() + +find_package(Qt${QT_MAJOR_VERSION} 5.15.0 CONFIG REQUIRED Core DBus Gui Qml Quick LinguistTools Test QuickTest) # find qt5 imports dir -get_target_property(QMAKE_EXECUTABLE Qt5::qmake LOCATION) +get_target_property(QMAKE_EXECUTABLE Qt${QT_MAJOR_VERSION}::qmake LOCATION) if(NOT QT_IMPORTS_DIR) - exec_program(${QMAKE_EXECUTABLE} ARGS "-query QT_INSTALL_QML" RETURN_VALUE return_code OUTPUT_VARIABLE QT_IMPORTS_DIR) + execute_process(COMMAND ${QMAKE_EXECUTABLE} -query QT_INSTALL_QML OUTPUT_VARIABLE QT_IMPORTS_DIR OUTPUT_STRIP_TRAILING_WHITESPACE) endif() # Uninstall target @@ -137,11 +136,17 @@ endif() if (NOT DEFINED SYSTEMD_SYSTEM_UNIT_DIR) - execute_process(COMMAND ${PKG_CONFIG_EXECUTABLE} --variable=systemdsystemunitdir systemd OUTPUT_VARIABLE SYSTEMD_SYSTEM_UNIT_DIR) - string(REGEX REPLACE "[ \t\n]+" "" SYSTEMD_SYSTEM_UNIT_DIR ${SYSTEMD_SYSTEM_UNIT_DIR}) + pkg_get_variable(SYSTEMD_SYSTEM_UNIT_DIR systemd systemdsystemunitdir) + endif() + + if (NOT DEFINED SYSTEMD_SYSUSERS_DIR) + pkg_get_variable(SYSTEMD_SYSUSERS_DIR systemd sysusersdir) + endif() + + if (NOT DEFINED SYSTEMD_TMPFILES_DIR) + pkg_get_variable(SYSTEMD_TMPFILES_DIR systemd tmpfilesdir) endif() - set(MINIMUM_VT 1) set(HALT_COMMAND "/usr/bin/systemctl poweroff") set(REBOOT_COMMAND "/usr/bin/systemctl reboot") else() @@ -159,7 +164,6 @@ add_definitions(-DHAVE_ELOGIND) set(CMAKE_AUTOMOC_MOC_OPTIONS -DHAVE_ELOGIND) - set(MINIMUM_VT 7) set(HALT_COMMAND "/usr/bin/loginctl poweroff") set(REBOOT_COMMAND "/usr/bin/loginctl reboot") endif() @@ -171,21 +175,25 @@ # commands for shutdown and reboot. On FreeBSD, there are # normally more getty's running than on Linux. if("${CMAKE_SYSTEM_NAME}" STREQUAL "FreeBSD") - set(MINIMUM_VT 9) set(HALT_COMMAND "/sbin/shutdown -p now") else() - set(MINIMUM_VT 7) set(HALT_COMMAND "/sbin/shutdown -h -P now") endif() set(REBOOT_COMMAND "/sbin/shutdown -r now") endif() +if (SYSTEMD_FOUND) + set(RUNTIME_DIR_DEFAULT "/run/sddm") +else() + set(RUNTIME_DIR_DEFAULT "${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/run/sddm") +endif() + # Set constants set(DATA_INSTALL_DIR "${CMAKE_INSTALL_FULL_DATADIR}/sddm" CACHE PATH "System application data install directory") set(DBUS_CONFIG_DIR "${CMAKE_INSTALL_SYSCONFDIR}/dbus-1/system.d" CACHE PATH "DBus config files directory") set(STATE_DIR "${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/lib/sddm" CACHE PATH "State directory") -set(RUNTIME_DIR "${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/run/sddm" CACHE PATH "Runtime data storage directory") +set(RUNTIME_DIR "${RUNTIME_DIR_DEFAULT}" CACHE PATH "Runtime data storage directory") set(QML_INSTALL_DIR "${QT_IMPORTS_DIR}" CACHE PATH "QML component installation directory") set(SESSION_COMMAND "${DATA_INSTALL_DIR}/scripts/Xsession" CACHE PATH "Script to execute when starting the X11 desktop session") @@ -193,10 +201,12 @@ set(CONFIG_FILE "${CMAKE_INSTALL_FULL_SYSCONFDIR}/sddm.conf" CACHE PATH "Path of the sddm config file") set(CONFIG_DIR "${CMAKE_INSTALL_FULL_SYSCONFDIR}/sddm.conf.d" CACHE PATH "Path of the sddm config directory") +set(ACCOUNTSSERVICE_DATA_DIR "/var/lib/AccountsService" CACHE PATH "Path of the accountsservice data directory") set(SYSTEM_CONFIG_DIR "${CMAKE_INSTALL_PREFIX}/lib/sddm/sddm.conf.d" CACHE PATH "Path of the system sddm config directory") set(LOG_FILE "${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/log/sddm.log" CACHE PATH "Path of the sddm log file") set(DBUS_CONFIG_FILENAME "org.freedesktop.DisplayManager.conf" CACHE STRING "Name of the sddm config file") set(COMPONENTS_TRANSLATION_DIR "${DATA_INSTALL_DIR}/translations" CACHE PATH "Components translations directory") +set(SDDM_INITIAL_VT "1" CACHE STRING "Initial tty to use") # Autodetect UID_MIN and UID_MAX from /etc/login.defs diff -Nru sddm-0.19.0/components/CMakeLists.txt sddm-0.20.0/components/CMakeLists.txt --- sddm-0.19.0/components/CMakeLists.txt 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/components/CMakeLists.txt 2023-06-23 12:28:38.000000000 +0000 @@ -1,5 +1,5 @@ configure_file("2.0/LayoutBox.qml" "2.0/LayoutBox.qml") -install(DIRECTORY "2.0/" DESTINATION "${QML_INSTALL_DIR}/SddmComponents") +install(DIRECTORY "2.0/" DESTINATION "${QML_INSTALL_DIR}/SddmComponents" PATTERN "LayoutBox.qml" EXCLUDE) install(DIRECTORY "common/" DESTINATION "${QML_INSTALL_DIR}/SddmComponents") install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/2.0/" DESTINATION "${QML_INSTALL_DIR}/SddmComponents") Binary files /tmp/tmpwut7c2c6/zlKBzbu1hI/sddm-0.19.0/data/flags/bd.png and /tmp/tmpwut7c2c6/rRrnxiK7Hp/sddm-0.20.0/data/flags/bd.png differ diff -Nru sddm-0.19.0/data/man/sddm.conf.rst.in sddm-0.20.0/data/man/sddm.conf.rst.in --- sddm-0.19.0/data/man/sddm.conf.rst.in 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/data/man/sddm.conf.rst.in 2023-06-23 12:28:38.000000000 +0000 @@ -6,7 +6,7 @@ sddm display manager configuration ---------------------------------- -:Date: August 2015 +:Date: March 2021 :Version: sddm @SDDM_VERSION_STRING@ :Manual section: 5 :Manual group: sddm @@ -36,6 +36,17 @@ [General] section: +`DisplayServer=` + Select the display server to use for the greeter. + Valid values are: + + * `x11`: X server running as root. + * `x11-user`: X server running as unprivileged user. + * `wayland`: Wayland compositor as unprivileged user. (Experimental) + + Default value is "x11". + For `x11-user` you might need to configure Xorg.wrap(1). + `HaltCommand=` Halt command. Default value is "@HALT_COMMAND@". @@ -85,6 +96,9 @@ Name of the cursor theme to be set before starting the display server. +`CursorSize=` + Cursor size to be set before starting the display server. + `Font=` Name of the font to be set before starting the display server. Please note that the theme can still override this option. @@ -110,13 +124,9 @@ Path of the Xephyr. Default value is "/usr/bin/Xephyr". -`XauthPath=` - Path of the Xauth. - Default value is "/usr/bin/xauth". - `SessionDir=` - Path of the directory containing session files. - Default value is "/usr/share/xsessions". + Comma-separated list of directories containing session files. + Default value is "/usr/local/share/xsessions,/usr/share/xsessions". `SessionCommand=` Path of script to execute when starting the user session. This script @@ -128,34 +138,43 @@ Path to the user session log file, relative to the home directory. Default value is ".local/share/sddm/xorg-session.log". -`UserAuthFile=` - Path to the Xauthority file, relative to the home directory. - Default value is ".Xauthority". - `DisplayCommand=` Path of script to execute when starting the display server. + The script will be executed as root when General.DisplayServer + is "x11", otherwise as sddm user. Default value is "@DATA_INSTALL_DIR@/scripts/Xsetup". `DisplayStopCommand=` Path of script to execute when stopping the display server. + The script will be executed as root when General.DisplayServer + is "x11", otherwise as sddm user. Default value is "@DATA_INSTALL_DIR@/scripts/Xstop". `MinimumVT=` Minimum virtual terminal number that will be used by the first display. Virtual terminal number will increase as new displays added. - Default value is @MINIMUM_VT@. + This setting is no longer available since SDDM v0.20. `EnableHiDPI=` Enables Qt's automatic HiDPI scaling. Can be either "true" or "false". - Default value is "false". + Default value is "true". + +The `XauthPath=` option is no longer necessary, libxau is used instead. + +The `UserAuthFile=` option was removed, the file is always created as +`/tmp/xauth_XXXXX`. This is necessary for to the use of `FamilyWild` entries. [Wayland] section: +`CompositorCommand=` + Path of the compositor to execute when starting the greeter. + Default value is "weston --shell=fullscreen-shell.so". + `SessionDir=` - Path of the directory containing session files. - Default value is "/usr/share/wayland-sessions". + Comma-separated list of directories containing session files. + Default value is "/usr/local/share/wayland-sessions,/usr/share/wayland-sessions". `SessionCommand=` Path of script to execute when starting the user session. This script @@ -170,7 +189,7 @@ `EnableHiDPI=` Enables Qt's automatic HiDPI scaling. Can be either "true" or "false". - Default value is "false". + Default value is "true". [Users] section: diff -Nru sddm-0.19.0/data/man/sddm.rst.in sddm-0.20.0/data/man/sddm.rst.in --- sddm-0.19.0/data/man/sddm.rst.in 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/data/man/sddm.rst.in 2023-06-23 12:28:38.000000000 +0000 @@ -34,6 +34,10 @@ into the **video** group, otherwise errors regarding GL and drm devices might be experienced. +For X11 sessions, the cookie for X authorization is written into a +temporary file `/tmp/xauth_XXXXXX`, owned and only accessible by the +user. + OPTIONS ======= diff -Nru sddm-0.19.0/data/scripts/wayland-session sddm-0.20.0/data/scripts/wayland-session --- sddm-0.19.0/data/scripts/wayland-session 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/data/scripts/wayland-session 2023-06-23 12:28:38.000000000 +0000 @@ -6,28 +6,19 @@ # Copyright (C) 2001-2005 Oswald Buddenhagen # Note that the respective logout scripts are not sourced. + +# Backup the user shell setting into SDDM specific variable +SDDM_USER_SHELL=$SHELL + case $SHELL in */bash) - [ -z "$BASH" ] && exec $SHELL $0 "$@" + [ -z "$BASH" ] && exec $SHELL --login $0 "$@" + shopt -q login_shell || exec $SHELL --login $0 "$@" set +o posix - [ -f /etc/profile ] && . /etc/profile - if [ -f $HOME/.bash_profile ]; then - . $HOME/.bash_profile - elif [ -f $HOME/.bash_login ]; then - . $HOME/.bash_login - elif [ -f $HOME/.profile ]; then - . $HOME/.profile - fi ;; -*/zsh) - [ -z "$ZSH_NAME" ] && exec $SHELL $0 "$@" - [ -d /etc/zsh ] && zdir=/etc/zsh || zdir=/etc - zhome=${ZDOTDIR:-$HOME} - # zshenv is always sourced automatically. - [ -f $zdir/zprofile ] && . $zdir/zprofile - [ -f $zhome/.zprofile ] && . $zhome/.zprofile - [ -f $zdir/zlogin ] && . $zdir/zlogin - [ -f $zhome/.zlogin ] && . $zhome/.zlogin + */zsh) + [ -z "$ZSH_NAME" ] && exec $SHELL --login $0 "$@" + [[ -o login ]] || exec $SHELL --login $0 "$@" emulate -R sh ;; */csh|*/tcsh) @@ -39,6 +30,8 @@ rm -f $wlsess_tmp ;; */fish) + [ -f /etc/profile ] && . /etc/profile + [ -f $HOME/.profile ] && . $HOME/.profile xsess_tmp=`mktemp /tmp/xsess-env-XXXXXX` $SHELL --login -c "/bin/sh -c 'export -p' > $xsess_tmp" . $xsess_tmp @@ -50,4 +43,7 @@ ;; esac +# Restore user shell setting that may have been clobbered by setting environment +export SHELL=$SDDM_USER_SHELL + exec $@ diff -Nru sddm-0.19.0/data/scripts/Xsession sddm-0.20.0/data/scripts/Xsession --- sddm-0.19.0/data/scripts/Xsession 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/data/scripts/Xsession 2023-06-23 12:28:38.000000000 +0000 @@ -8,26 +8,13 @@ # Note that the respective logout scripts are not sourced. case $SHELL in */bash) - [ -z "$BASH" ] && exec $SHELL $0 "$@" + [ -z "$BASH" ] && exec $SHELL --login $0 "$@" + shopt -q login_shell || exec $SHELL --login $0 "$@" set +o posix - [ -f /etc/profile ] && . /etc/profile - if [ -f $HOME/.bash_profile ]; then - . $HOME/.bash_profile - elif [ -f $HOME/.bash_login ]; then - . $HOME/.bash_login - elif [ -f $HOME/.profile ]; then - . $HOME/.profile - fi ;; -*/zsh) - [ -z "$ZSH_NAME" ] && exec $SHELL $0 "$@" - [ -d /etc/zsh ] && zdir=/etc/zsh || zdir=/etc - zhome=${ZDOTDIR:-$HOME} - # zshenv is always sourced automatically. - [ -f $zdir/zprofile ] && . $zdir/zprofile - [ -f $zhome/.zprofile ] && . $zhome/.zprofile - [ -f $zdir/zlogin ] && . $zdir/zlogin - [ -f $zhome/.zlogin ] && . $zhome/.zlogin + */zsh) + [ -z "$ZSH_NAME" ] && exec $SHELL --login $0 "$@" + [[ -o login ]] || exec $SHELL --login $0 "$@" emulate -R sh ;; */csh|*/tcsh) @@ -39,6 +26,8 @@ rm -f $xsess_tmp ;; */fish) + [ -f /etc/profile ] && . /etc/profile + [ -f $HOME/.profile ] && . $HOME/.profile xsess_tmp=`mktemp /tmp/xsess-env-XXXXXX` $SHELL --login -c "/bin/sh -c 'export -p' > $xsess_tmp" . $xsess_tmp diff -Nru sddm-0.19.0/data/themes/CMakeLists.txt sddm-0.20.0/data/themes/CMakeLists.txt --- sddm-0.19.0/data/themes/CMakeLists.txt 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/data/themes/CMakeLists.txt 2023-06-23 12:28:38.000000000 +0000 @@ -8,7 +8,7 @@ set_source_files_properties(${TRANSLATION_SOURCES} PROPERTIES OUTPUT_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/${TRANSLATIONS_DIR}") - qt5_add_translation(QM_FILES "${TRANSLATION_SOURCES}") + qt_add_translation(QM_FILES "${TRANSLATION_SOURCES}") install(DIRECTORY "${THEME}" DESTINATION "${DATA_INSTALL_DIR}/themes" PATTERN "${THEME}/*.ts" EXCLUDE PATTERN "${THEME}/.gitattributes" diff -Nru sddm-0.19.0/data/themes/elarun/Main.qml sddm-0.20.0/data/themes/elarun/Main.qml --- sddm-0.19.0/data/themes/elarun/Main.qml 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/data/themes/elarun/Main.qml 2023-06-23 12:28:38.000000000 +0000 @@ -41,6 +41,8 @@ target: sddm onLoginSucceeded: { } + onInformationMessage: { + } onLoginFailed: { pw_entry.text = "" } @@ -152,7 +154,7 @@ onClicked: sddm.login(user_entry.text, pw_entry.text, sessionIndex) - KeyNavigation.backtab: pw_entry; KeyNavigation.tab: session_button + KeyNavigation.backtab: pw_entry; KeyNavigation.tab: session } Item { @@ -172,7 +174,7 @@ source: "images/system_shutdown.png" onClicked: sddm.powerOff() - KeyNavigation.backtab: session_button; KeyNavigation.tab: reboot_button + KeyNavigation.backtab: session; KeyNavigation.tab: reboot_button } ImageButton { diff -Nru sddm-0.19.0/data/themes/maldives/Main.qml sddm-0.20.0/data/themes/maldives/Main.qml --- sddm-0.19.0/data/themes/maldives/Main.qml 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/data/themes/maldives/Main.qml 2023-06-23 12:28:38.000000000 +0000 @@ -50,6 +50,10 @@ errorMessage.color = "red" errorMessage.text = textConstants.loginFailed } + onInformationMessage: { + errorMessage.color = "red" + errorMessage.text = message + } } Background { diff -Nru sddm-0.19.0/data/themes/maya/Main.qml sddm-0.20.0/data/themes/maya/Main.qml --- sddm-0.19.0/data/themes/maya/Main.qml 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/data/themes/maya/Main.qml 2023-06-23 12:28:38.000000000 +0000 @@ -93,6 +93,15 @@ anim_failure.start() } + onInformationMessage: { + prompt_bg.color = failureText + prompt_txt.text = message + + maya_busy.visible = false; + maya_busy_anim.stop() + + anim_failure.start() + } } diff -Nru sddm-0.19.0/data/translations/ar.ts sddm-0.20.0/data/translations/ar.ts --- sddm-0.19.0/data/translations/ar.ts 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/data/translations/ar.ts 2023-06-23 12:28:38.000000000 +0000 @@ -62,7 +62,7 @@ أطفئ - User name + Username اسم المستخدم diff -Nru sddm-0.19.0/data/translations/bn.ts sddm-0.20.0/data/translations/bn.ts --- sddm-0.19.0/data/translations/bn.ts 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/data/translations/bn.ts 2023-06-23 12:28:38.000000000 +0000 @@ -65,7 +65,7 @@ শাটডাউন - User name + Username ব্যবহারকারীর নাম diff -Nru sddm-0.19.0/data/translations/ca.ts sddm-0.20.0/data/translations/ca.ts --- sddm-0.19.0/data/translations/ca.ts 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/data/translations/ca.ts 2023-06-23 12:28:38.000000000 +0000 @@ -62,7 +62,7 @@ Apaga - User name + Username Usuari diff -Nru sddm-0.19.0/data/translations/CMakeLists.txt sddm-0.20.0/data/translations/CMakeLists.txt --- sddm-0.19.0/data/translations/CMakeLists.txt 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/data/translations/CMakeLists.txt 2023-06-23 12:28:38.000000000 +0000 @@ -10,6 +10,7 @@ eu.ts fi.ts fr.ts + gl.ts hi_IN.ts he.ts hu.ts @@ -17,6 +18,7 @@ is.ts it.ts ja.ts + ka.ts kk.ts ko.ts lt.ts @@ -42,7 +44,7 @@ zh_TW.ts ) -qt5_add_translation(QM_FILES ${TRANSLATION_FILES}) +qt_add_translation(QM_FILES ${TRANSLATION_FILES}) install(FILES ${QM_FILES} DESTINATION "${COMPONENTS_TRANSLATION_DIR}") diff -Nru sddm-0.19.0/data/translations/cs.ts sddm-0.20.0/data/translations/cs.ts --- sddm-0.19.0/data/translations/cs.ts 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/data/translations/cs.ts 2023-06-23 12:28:38.000000000 +0000 @@ -62,7 +62,7 @@ Vypnout - User name + Username Uživatelské jméno diff -Nru sddm-0.19.0/data/translations/da.ts sddm-0.20.0/data/translations/da.ts --- sddm-0.19.0/data/translations/da.ts 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/data/translations/da.ts 2023-06-23 12:28:38.000000000 +0000 @@ -70,7 +70,7 @@ Luk ned - User name + Username Brugernavn diff -Nru sddm-0.19.0/data/translations/es.ts sddm-0.20.0/data/translations/es.ts --- sddm-0.19.0/data/translations/es.ts 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/data/translations/es.ts 2023-06-23 12:28:38.000000000 +0000 @@ -62,7 +62,7 @@ Apagar - User name + Username Usuario diff -Nru sddm-0.19.0/data/translations/et.ts sddm-0.20.0/data/translations/et.ts --- sddm-0.19.0/data/translations/et.ts 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/data/translations/et.ts 2023-06-23 12:28:38.000000000 +0000 @@ -62,7 +62,7 @@ Seiska - User name + Username Kasutajanimi diff -Nru sddm-0.19.0/data/translations/eu.ts sddm-0.20.0/data/translations/eu.ts --- sddm-0.19.0/data/translations/eu.ts 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/data/translations/eu.ts 2023-06-23 12:28:38.000000000 +0000 @@ -62,7 +62,7 @@ Itzali - User name + Username Erabiltzaile izena diff -Nru sddm-0.19.0/data/translations/fi.ts sddm-0.20.0/data/translations/fi.ts --- sddm-0.19.0/data/translations/fi.ts 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/data/translations/fi.ts 2023-06-23 12:28:38.000000000 +0000 @@ -62,7 +62,7 @@ Sammuta - User name + Username Käyttäjätunnus diff -Nru sddm-0.19.0/data/translations/fr.ts sddm-0.20.0/data/translations/fr.ts --- sddm-0.19.0/data/translations/fr.ts 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/data/translations/fr.ts 2023-06-23 12:28:38.000000000 +0000 @@ -70,7 +70,7 @@ Éteindre - User name + Username Identifiant diff -Nru sddm-0.19.0/data/translations/gl.ts sddm-0.20.0/data/translations/gl.ts --- sddm-0.19.0/data/translations/gl.ts 1970-01-01 00:00:00.000000000 +0000 +++ sddm-0.20.0/data/translations/gl.ts 2023-06-23 12:28:38.000000000 +0000 @@ -0,0 +1,73 @@ + + + + + PictureBox + + Press to login + Prema para acceder + + + + QObject + + %1 (Wayland) + %1 (Wayland) + + + + TextConstants + + Welcome to %1 + Dámoslle a benvida a %1 + + + Warning, Caps Lock is ON! + Aviso, as maiúsculas están BLOQUEADAS! + + + Layout + Disposición + + + Login + Acceder + + + Login failed + O acceso fallou + + + Login succeeded + Accedeuse + + + Password + Contrasinal + + + Enter your username and password + Escriba o seu nome de persoa usuaria e contrasinal + + + Reboot + Reiniciar + + + Session + Sesión + + + Shutdown + Apagar + + + Username + Usuaria + + + Select your user and enter password + Seleccione a súa conta e escriba o contrasinal + + + diff -Nru sddm-0.19.0/data/translations/he.ts sddm-0.20.0/data/translations/he.ts --- sddm-0.19.0/data/translations/he.ts 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/data/translations/he.ts 2023-06-23 12:28:38.000000000 +0000 @@ -62,7 +62,7 @@ כיבוי - User name + Username שם משתמש diff -Nru sddm-0.19.0/data/translations/hi_IN.ts sddm-0.20.0/data/translations/hi_IN.ts --- sddm-0.19.0/data/translations/hi_IN.ts 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/data/translations/hi_IN.ts 2023-06-23 12:28:38.000000000 +0000 @@ -62,7 +62,7 @@ बंद कीजिए - User name + Username आपकी पहचान diff -Nru sddm-0.19.0/data/translations/hsb.ts sddm-0.20.0/data/translations/hsb.ts --- sddm-0.19.0/data/translations/hsb.ts 1970-01-01 00:00:00.000000000 +0000 +++ sddm-0.20.0/data/translations/hsb.ts 2023-06-23 12:28:38.000000000 +0000 @@ -0,0 +1,117 @@ + + + + + PictureBox + + Press to login + Klikńće tu, zo byšće so přizjewili + + + + QObject + + %1 (Wayland) + %1 (Wayland) + + + + TextConstants + + Welcome to %1 + Witajće na %1 + + + Warning, Caps Lock is ON! + Kedźbu, wulkopisanje je zaswěćene! + + + Layout + Layout + + + Login + So přizjewić + + + Login failed + Přizjewjenje njeje so poradźiło + + + Login succeeded + Přizjewjenje bě wuspěšne + + + Password + Hesło + + + Enter your username and password + Prošu zapodajće wužiwarske mjeno a hesło + + + Reboot + Znowa startować + + + Suspend + Spinkać + + + Hibernate + Zymski spar + + + Session + Posedźenje + + + Shutdown + Hasnyć + + + Username + Wužiwarske mjeno + + + Enter your username + Zapodajće swoje wužiwarske mjeno + + + Enter your password + Zapodajće swoje hesło + + + Select your user and enter password + Wužiwarja wubrać a hesło zapodać + + + Show password + Hesło pokazać + + + Hide password + Hesło chować + + + Please enter a password! + Prošu zapodajće hesło! + + + Change password + Hesło změnić + + + Password: + Hesło: + + + Password change aborted because maximum tries reached + Změna hesła přetorhnjena (přewjele pospytow) + + + New password change round! Please input current password again! + Přichodny pospyt změny hesła! Prošu zapodajće tuchwilne hesło znowa! + + + diff -Nru sddm-0.19.0/data/translations/hu.ts sddm-0.20.0/data/translations/hu.ts --- sddm-0.19.0/data/translations/hu.ts 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/data/translations/hu.ts 2023-06-23 12:28:38.000000000 +0000 @@ -62,7 +62,7 @@ Leállítás - User name + Username Felhasználónév diff -Nru sddm-0.19.0/data/translations/ie.ts sddm-0.20.0/data/translations/ie.ts --- sddm-0.19.0/data/translations/ie.ts 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/data/translations/ie.ts 2023-06-23 12:28:38.000000000 +0000 @@ -60,7 +60,7 @@ - User name + Username Usator diff -Nru sddm-0.19.0/data/translations/is.ts sddm-0.20.0/data/translations/is.ts --- sddm-0.19.0/data/translations/is.ts 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/data/translations/is.ts 2023-06-23 12:28:38.000000000 +0000 @@ -62,7 +62,7 @@ Slökkva - User name + Username Notandanafn diff -Nru sddm-0.19.0/data/translations/it.ts sddm-0.20.0/data/translations/it.ts --- sddm-0.19.0/data/translations/it.ts 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/data/translations/it.ts 2023-06-23 12:28:38.000000000 +0000 @@ -62,7 +62,7 @@ Spegni - User name + Username Nome utente diff -Nru sddm-0.19.0/data/translations/ja.ts sddm-0.20.0/data/translations/ja.ts --- sddm-0.19.0/data/translations/ja.ts 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/data/translations/ja.ts 2023-06-23 12:28:38.000000000 +0000 @@ -62,7 +62,7 @@ シャットダウン - User name + Username ユーザ名 diff -Nru sddm-0.19.0/data/translations/ka.ts sddm-0.20.0/data/translations/ka.ts --- sddm-0.19.0/data/translations/ka.ts 1970-01-01 00:00:00.000000000 +0000 +++ sddm-0.20.0/data/translations/ka.ts 2023-06-23 12:28:38.000000000 +0000 @@ -0,0 +1,125 @@ + + + + ka + Georgian <(nothing)> + Temuri Doghonadze <temuri.doghonadze@gmail.com> + + + SDDM + Poedit 3.1.1 + Project-Id-Version,POT-Creation-Date,PO-Revision-Date,Last-Translator,Language-Team,Language,MIME-Version,Content-Type,Content-Transfer-Encoding,Plural-Forms,X-Language,X-Qt-Contexts,X-Generator + + PictureBox + + Press to login + დააწექით შესასვლელად + + + + QObject + + %1 (Wayland) + %1 (Wayland) + + + + TextConstants + + Welcome to %1 + მოგესალმებით %1-ში + + + Warning, Caps Lock is ON! + გაფრთხილება: CapsLock ჩართულია! + + + Layout + განლაგება + + + Login + შესვლა + + + Login failed + შესვლის შეცდომა + + + Login succeeded + შესვლა წარმატებულია + + + Password + პაროლი + + + Enter your username and password + შეიყვანეთ თქვენი მომხარებლის სახელი და პაროლი + + + Reboot + გადატვირთვა + + + Suspend + ძილი + + + Hibernate + პროგრამული ძილი + + + Session + სესია + + + Shutdown + გამორთვა + + + Username + მოხმარებელი + + + Enter your username + შეიყვანეთ თქვენი მომხარებლის სახელი + + + Enter your password + შეიყვანეთ თქვენი პაროლი + + + Select your user and enter password + აირჩიეთ თქვენი მომხმარებელი და შეიყვანეთ პაროლი + + + Show password + პაროლის ჩვენება + + + Hide password + პაროლის დამალვა + + + Please enter a password! + შეიყვანეთ პაროლი! + + + Change password + პაროლის შეცვლა + + + Password: + პაროლი: + + + Password change aborted because maximum tries reached + პაროლის შეცვლა გაუქმდა ცდების მაქსიმალური რაოდენობის გადაჭარბების გამო + + + New password change round! Please input current password again! + დროა თავიდან შეცვალოთ პაროლი! თავიდან შეიყვანეთ თქვენი მიმდინარე პაროლი! + + + diff -Nru sddm-0.19.0/data/translations/kk.ts sddm-0.20.0/data/translations/kk.ts --- sddm-0.19.0/data/translations/kk.ts 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/data/translations/kk.ts 2023-06-23 12:28:38.000000000 +0000 @@ -62,7 +62,7 @@ Сөндіру - User name + Username Пайдаланушы аты diff -Nru sddm-0.19.0/data/translations/ko.ts sddm-0.20.0/data/translations/ko.ts --- sddm-0.19.0/data/translations/ko.ts 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/data/translations/ko.ts 2023-06-23 12:28:38.000000000 +0000 @@ -66,10 +66,6 @@ 컴퓨터 끄기 - User name - 사용자 이름 - - Welcome to %1 %1에 오신 것을 환영합니다 diff -Nru sddm-0.19.0/data/translations/lt.ts sddm-0.20.0/data/translations/lt.ts --- sddm-0.19.0/data/translations/lt.ts 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/data/translations/lt.ts 2023-06-23 12:28:38.000000000 +0000 @@ -54,6 +54,14 @@ Paleisti iš naujo + Suspend + Pristabdyti + + + Hibernate + Užmigdyti + + Session Seansas @@ -62,12 +70,48 @@ Išjungti - User name + Username Naudotojo vardas + Enter your username + Įveskite savo naudotojo vardą + + + Enter your password + Įveskite savo slaptažodį + + Select your user and enter password Pasirinkite savo naudotoją ir įveskite slaptažodį + + Show password + Rodyti slaptažodį + + + Hide password + Slėpti slaptažodį + + + Please enter a password! + Įveskite slaptažodį! + + + Change password + Keisti slaptažodį + + + Password: + Slaptažodis: + + + Password change aborted because maximum tries reached + Slaptažodžio keitimas nutrauktas, nes pasiektas didžiausias bandymų skaičius + + + New password change round! Please input current password again! + Dar kartą įveskite dabartinį slaptažodį! + diff -Nru sddm-0.19.0/data/translations/lv.ts sddm-0.20.0/data/translations/lv.ts --- sddm-0.19.0/data/translations/lv.ts 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/data/translations/lv.ts 2023-06-23 12:28:38.000000000 +0000 @@ -62,7 +62,7 @@ Izslēgt - User name + Username Lietotāja vārds diff -Nru sddm-0.19.0/data/translations/nb.ts sddm-0.20.0/data/translations/nb.ts --- sddm-0.19.0/data/translations/nb.ts 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/data/translations/nb.ts 2023-06-23 12:28:38.000000000 +0000 @@ -4,7 +4,6 @@ PictureBox - Press to login Trykk for å logge inn @@ -12,7 +11,6 @@ QObject - %1 (Wayland) %1 (Wayland) @@ -20,69 +18,100 @@ TextConstants - + Welcome to %1 + Velkommen til %1 + + Warning, Caps Lock is ON! Advarsel: «Caps Lock» står på! - Layout Tastatur - Login Logg inn - Login failed - Klarte ikke logge inn + Innlogging feilet - Login succeeded - Innlogget + Innlogging lyktes - Password Passord - Enter your username and password - Skriv inn brukernavn og passord + Skriv inn ditt brukernavn og passord - - Select your user and enter password - Velg bruker og skriv inn passord + Reboot + Omstart - - Reboot - Start på nytt + Suspend + Hvile + + + Hibernate + Dvale - Session Økt - Shutdown Slå av - - User name + Username Brukernavn - - Welcome to %1 - Velkommen til %1 + Enter your username + Skriv inn ditt brukernavn + + + Enter your password + Skriv inn ditt passord + + + Select your user and enter password + Velg bruker og skriv inn passord + + + Show password + Vis passord + + + Hide password + Skjul passord + + + Please enter a password! + Vennligst skriv inn et passord! + + + Change password + Endre passord + + + Unregistered user + Ugyldig bruker + + + Password change aborted because maximum tries reached + Endring av passord ble avbrutt da høyeste antall forsøk er nådd + + + New password change round! Please input current password again! + Ny runde for ending av passord! Vennligst skriv inn det nåværende passordet igjen! diff -Nru sddm-0.19.0/data/translations/nl.ts sddm-0.20.0/data/translations/nl.ts --- sddm-0.19.0/data/translations/nl.ts 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/data/translations/nl.ts 2023-06-23 12:28:38.000000000 +0000 @@ -62,7 +62,7 @@ Uitschakelen - User name + Username Gebruikersnaam diff -Nru sddm-0.19.0/data/translations/nn.ts sddm-0.20.0/data/translations/nn.ts --- sddm-0.19.0/data/translations/nn.ts 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/data/translations/nn.ts 2023-06-23 12:28:38.000000000 +0000 @@ -4,7 +4,6 @@ PictureBox - Press to login Trykk for å logga inn @@ -12,7 +11,6 @@ QObject - %1 (Wayland) %1 (Wayland) @@ -20,69 +18,100 @@ TextConstants - + Welcome to %1 + Velkommen til %1 + + Warning, Caps Lock is ON! Åtvaring: «Caps Lock» står på! - Layout Tastatur - Login Logg inn - Login failed Klarte ikkje logga inn - Login succeeded Logga inn - Password Passord - - Enter your username and password - Skriv inn brukarnamn og passord + Enter your username + Skriv inn brukarnamn + + + Enter your password + Skriv inn passord - Select your user and enter password Vel brukar og skriv inn passord - Reboot Start på nytt - Session Økt - Shutdown Slå av - - User name + Username Brukarnamn - - Welcome to %1 - Velkommen til %1 + Enter your username + Skriv inn brukarnamn + + + Enter your password + Skriv inn passord + + + Select your user and enter password + Vel brukar og skriv inn passord + + + Show password + Vis passord + + + Hide password + Skjul passord + + + Please enter a password! + Ver vennleg og skriv inn eit passord! + + + Change password + Endre passord + + + Unregistered user + Ugyldig brukar + + + Password change aborted because maximum tries reached + Endring av passord vart avbrotne då høgaste talet på forsøk er nådd + + + New password change round! Please input current password again! + Ny runde for ending av passord! Ver vennleg og skriv inn det noverande passordet igjen! diff -Nru sddm-0.19.0/data/translations/oc.ts sddm-0.20.0/data/translations/oc.ts --- sddm-0.19.0/data/translations/oc.ts 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/data/translations/oc.ts 2023-06-23 12:28:38.000000000 +0000 @@ -62,7 +62,7 @@ Atudar - User name + Username Nom d’utilizaire diff -Nru sddm-0.19.0/data/translations/pl.ts sddm-0.20.0/data/translations/pl.ts --- sddm-0.19.0/data/translations/pl.ts 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/data/translations/pl.ts 2023-06-23 12:28:38.000000000 +0000 @@ -78,7 +78,7 @@ Zahibernuj - User name + Username Nazwa użytkownika diff -Nru sddm-0.19.0/data/translations/pt_BR.ts sddm-0.20.0/data/translations/pt_BR.ts --- sddm-0.19.0/data/translations/pt_BR.ts 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/data/translations/pt_BR.ts 2023-06-23 12:28:38.000000000 +0000 @@ -62,7 +62,7 @@ Desligar - User name + Username Usuário diff -Nru sddm-0.19.0/data/translations/pt_PT.ts sddm-0.20.0/data/translations/pt_PT.ts --- sddm-0.19.0/data/translations/pt_PT.ts 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/data/translations/pt_PT.ts 2023-06-23 12:28:38.000000000 +0000 @@ -62,7 +62,7 @@ Desligar - User name + Username Utilizador diff -Nru sddm-0.19.0/data/translations/ro.ts sddm-0.20.0/data/translations/ro.ts --- sddm-0.19.0/data/translations/ro.ts 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/data/translations/ro.ts 2023-06-23 12:28:38.000000000 +0000 @@ -62,7 +62,7 @@ Oprește - User name + Username Nume de utilizator diff -Nru sddm-0.19.0/data/translations/sk.ts sddm-0.20.0/data/translations/sk.ts --- sddm-0.19.0/data/translations/sk.ts 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/data/translations/sk.ts 2023-06-23 12:28:38.000000000 +0000 @@ -62,7 +62,7 @@ Vypnúť - User name + Username Užívateľské meno diff -Nru sddm-0.19.0/data/translations/sr@ijekavianlatin.ts sddm-0.20.0/data/translations/sr@ijekavianlatin.ts --- sddm-0.19.0/data/translations/sr@ijekavianlatin.ts 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/data/translations/sr@ijekavianlatin.ts 2023-06-23 12:28:38.000000000 +0000 @@ -62,7 +62,7 @@ Ugasi - User name + Username Korisničko ime diff -Nru sddm-0.19.0/data/translations/sr@ijekavian.ts sddm-0.20.0/data/translations/sr@ijekavian.ts --- sddm-0.19.0/data/translations/sr@ijekavian.ts 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/data/translations/sr@ijekavian.ts 2023-06-23 12:28:38.000000000 +0000 @@ -62,7 +62,7 @@ Угаси - User name + Username Корисничко име diff -Nru sddm-0.19.0/data/translations/sr@latin.ts sddm-0.20.0/data/translations/sr@latin.ts --- sddm-0.19.0/data/translations/sr@latin.ts 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/data/translations/sr@latin.ts 2023-06-23 12:28:38.000000000 +0000 @@ -62,7 +62,7 @@ Ugasi - User name + Username Korisničko ime diff -Nru sddm-0.19.0/data/translations/sr.ts sddm-0.20.0/data/translations/sr.ts --- sddm-0.19.0/data/translations/sr.ts 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/data/translations/sr.ts 2023-06-23 12:28:38.000000000 +0000 @@ -62,7 +62,7 @@ Угаси - User name + Username Корисничко име diff -Nru sddm-0.19.0/data/translations/sv.ts sddm-0.20.0/data/translations/sv.ts --- sddm-0.19.0/data/translations/sv.ts 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/data/translations/sv.ts 2023-06-23 12:28:38.000000000 +0000 @@ -1,10 +1,9 @@ - + PictureBox - Press to login Tryck för att logga in @@ -12,7 +11,6 @@ QObject - %1 (Wayland) %1 (Wayland) @@ -20,69 +18,101 @@ TextConstants - + Welcome to %1 + Välkommen till %1 + + Warning, Caps Lock is ON! Varning, «Caps Lock» är på! - Layout Tangentbordslayout - Login Logga in - Login failed Inloggningen misslyckades - Login succeeded Inloggningen lyckades - Password Lösenord - Enter your username and password Fyll i ditt användarnamn och lösenord - - Select your user and enter password - Välj din användare och ange lösenord - - - Reboot Starta om - + Suspend + Vänteläge + + + Hibernate + Viloläge + + Session Session - Shutdown Stäng av - User name + Username Användarnamn - - Welcome to %1 - Välkommen till %1 + Enter your username + Fyll i ditt användarnamn + + + Enter your password + Fyll i ditt lösenord + + + Select your user and enter password + Välj användarnamn och skriv in lösenord + + + Show password + Visa lösenord + + + Hide password + Dölj lösenord + + + Please enter a password! + Fyll i lösenord! + + + Change password + Ändra lösenord + + + Unregistered user + Ogiltig användarnamn + + + Password change aborted because maximum tries reached + Lösenordsbyte avbröts eftersom maximalt antal försök har uppnåtts + + + New password change round! Please input current password again! + Nytt varv för lösenordsbyte! Skriv in nuvarande lösenord igen! diff -Nru sddm-0.19.0/data/translations/tr.ts sddm-0.20.0/data/translations/tr.ts --- sddm-0.19.0/data/translations/tr.ts 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/data/translations/tr.ts 2023-06-23 12:28:38.000000000 +0000 @@ -5,14 +5,14 @@ PictureBox Press to login - + Giriş yapmak için tıklayın QObject %1 (Wayland) - + %1 (Wayland) @@ -62,7 +62,7 @@ Kapat - User name + Username Kullanıcı adı diff -Nru sddm-0.19.0/data/translations/uk.ts sddm-0.20.0/data/translations/uk.ts --- sddm-0.19.0/data/translations/uk.ts 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/data/translations/uk.ts 2023-06-23 12:28:38.000000000 +0000 @@ -48,7 +48,7 @@ Вимкнути - User name + Username Ім'я користувача diff -Nru sddm-0.19.0/debian/changelog sddm-0.20.0/debian/changelog --- sddm-0.19.0/debian/changelog 2023-06-26 09:09:31.000000000 +0000 +++ sddm-0.20.0/debian/changelog 2023-06-29 13:30:54.000000000 +0000 @@ -1,3 +1,34 @@ +sddm (0.20.0-1ubuntu1) mantic; urgency=medium + + * Merge from Debian unstable. Remaining changes: + - Keep the default-display-manager file logic in sddm.postinst. + - Update Vcs-* to point to Launchpad instead of Salsa. + - Make sure sddm.service starts at the correct time. + - Instead of hardcoding the Breeze theme in, allow ubuntu-theme to be + used in the same fashion Debian's is. + - Revert upstream change prefering wayland sessions. + + debian/patches: revert_prefer-wayland-sessions-over-X11.patch + + Refresh the above patch. + - Patch to avoid screen overlap or inset. + - Patch Xsetup script to use bash. Allows previous patch to actually + run as it requires arrays, + + -- Rik Mills Thu, 29 Jun 2023 14:30:54 +0100 + +sddm (0.20.0-1) unstable; urgency=medium + + [ Aurélien COUDERC ] + * New upstream release (0.20.0). + * Update build-deps and deps with the info from cmake. + * Bump Standards-Version to 4.6.2, no change required. + * Refresh patches. + * Drop patch 03_vt7-minimum-vt.diff and switch back to upstream default of + spawning greeter on VT1. + * Drop patches applied upstream. + * Refresh the list of installed files. + + -- Aurélien COUDERC Sat, 24 Jun 2023 08:38:52 +0200 + sddm (0.19.0-5ubuntu1) mantic; urgency=medium * Merge from Debian unstable. Remaining changes: diff -Nru sddm-0.19.0/debian/control sddm-0.20.0/debian/control --- sddm-0.19.0/debian/control 2023-06-26 09:07:42.000000000 +0000 +++ sddm-0.20.0/debian/control 2023-06-29 13:30:54.000000000 +0000 @@ -4,23 +4,25 @@ Maintainer: Ubuntu Developers XSBC-Original-Maintainer: Debian Qt/KDE Maintainers Uploaders: Aurélien COUDERC , -Build-Depends: cmake (>= 2.8.8), +Build-Depends: cmake (>= 3.4~), debhelper-compat (= 13), extra-cmake-modules (>= 1.4.0~), libpam0g-dev, libsystemd-dev [linux-any], libupower-glib-dev, + libxau-dev, libxcb-xkb-dev, libxcb1-dev, libxkbcommon-dev, pkg-config, python3-docutils, - qtbase5-dev (>= 5.8.0~), - qtdeclarative5-dev (>= 5.8.0~), + qml-module-qttest , + qtbase5-dev (>= 5.15.0~), + qtdeclarative5-dev (>= 5.15.0~), qttools5-dev, - qttools5-dev-tools (>= 5.8.0~), + qttools5-dev-tools (>= 5.15.0~), systemd [linux-any], -Standards-Version: 4.6.1 +Standards-Version: 4.6.2 Homepage: https://github.com/sddm/sddm Vcs-Browser: https://git.launchpad.net/~ubuntu-qt-code/+git/sddm Vcs-Git: https://git.launchpad.net/~ubuntu-qt-code/+git/sddm @@ -30,8 +32,8 @@ Architecture: any Depends: adduser, qml-module-qtquick2, - xauth, x11-common, + xauth, xserver-xorg | xserver, ${misc:Depends}, ${shlibs:Depends}, diff -Nru sddm-0.19.0/debian/patches/01_sddm_systemctl.diff sddm-0.20.0/debian/patches/01_sddm_systemctl.diff --- sddm-0.19.0/debian/patches/01_sddm_systemctl.diff 2021-02-18 17:31:22.000000000 +0000 +++ sddm-0.20.0/debian/patches/01_sddm_systemctl.diff 2023-06-29 13:30:54.000000000 +0000 @@ -8,10 +8,10 @@ --- a/CMakeLists.txt +++ b/CMakeLists.txt -@@ -142,8 +142,8 @@ if(SYSTEMD_FOUND) +@@ -147,8 +147,8 @@ if(SYSTEMD_FOUND) + pkg_get_variable(SYSTEMD_TMPFILES_DIR systemd tmpfilesdir) endif() - set(MINIMUM_VT 1) - set(HALT_COMMAND "/usr/bin/systemctl poweroff") - set(REBOOT_COMMAND "/usr/bin/systemctl reboot") + set(HALT_COMMAND "/bin/systemctl poweroff") diff -Nru sddm-0.19.0/debian/patches/02_use_debian_theme.diff sddm-0.20.0/debian/patches/02_use_debian_theme.diff --- sddm-0.19.0/debian/patches/02_use_debian_theme.diff 2021-02-18 17:31:22.000000000 +0000 +++ sddm-0.20.0/debian/patches/02_use_debian_theme.diff 2023-06-29 13:30:54.000000000 +0000 @@ -9,7 +9,7 @@ --- a/src/common/Configuration.h +++ b/src/common/Configuration.h -@@ -47,7 +47,7 @@ namespace SDDM { +@@ -51,7 +51,7 @@ // Name Entries (but it's a regular class again) Section(Theme, Entry(ThemeDir, QString, _S(DATA_INSTALL_DIR "/themes"), _S("Theme directory path")); diff -Nru sddm-0.19.0/debian/patches/04_set_default_path.diff sddm-0.20.0/debian/patches/04_set_default_path.diff --- sddm-0.19.0/debian/patches/04_set_default_path.diff 2021-02-18 17:31:22.000000000 +0000 +++ sddm-0.20.0/debian/patches/04_set_default_path.diff 2023-06-29 13:30:54.000000000 +0000 @@ -18,7 +18,7 @@ --- a/data/man/sddm.conf.rst.in +++ b/data/man/sddm.conf.rst.in -@@ -173,7 +173,7 @@ OPTIONS +@@ -196,7 +196,7 @@ `DefaultPath=` Default path to set after successfully logging in. This is also where SDDM looks for programs. @@ -29,7 +29,7 @@ Minimum user id of the users to be listed in the --- a/src/common/Configuration.h +++ b/src/common/Configuration.h -@@ -81,7 +81,7 @@ namespace SDDM { +@@ -87,7 +87,7 @@ ); Section(Users, diff -Nru sddm-0.19.0/debian/patches/kubuntu_nvidia-prime.diff sddm-0.20.0/debian/patches/kubuntu_nvidia-prime.diff --- sddm-0.19.0/debian/patches/kubuntu_nvidia-prime.diff 2021-02-18 17:31:22.000000000 +0000 +++ sddm-0.20.0/debian/patches/kubuntu_nvidia-prime.diff 1970-01-01 00:00:00.000000000 +0000 @@ -1,29 +0,0 @@ -Description: run nvidia-prime setup on login -Author: Jonathan Riddell -Origin: me -Bug: https://bugs.launchpad.net/ubuntu/+source/nvidia-prime/+bug/1428328 -Bug-SDDM: https://github.com/sddm/sddm/issues/393 -Forwarded: yes, although it's not very elegant so may not be accepted -Last-Update: 2015-04-02 ---- -This patch header follows DEP-3: http://dep.debian.net/deps/dep3/ ---- a/data/scripts/Xsetup -+++ b/data/scripts/Xsetup -@@ -1,3 +1,7 @@ - #!/bin/sh - # Xsetup - run as root before the login dialog appears - -+if [ -e /sbin/prime-offload ]; then -+ echo running NVIDIA Prime setup /sbin/prime-offload -+ /sbin/prime-offload -+fi ---- a/data/scripts/Xstop -+++ b/data/scripts/Xstop -@@ -1,2 +1,7 @@ - #!/bin/sh - # Xstop - run as root after stopping X -+ -+if [ -e /sbin/prime-switch ]; then -+ echo running NVIDIA Prime switch /sbin/prime-switch -+ /sbin/prime-switch -+fi diff -Nru sddm-0.19.0/debian/patches/revert_prefer-wayland-sessions-over-X11.patch sddm-0.20.0/debian/patches/revert_prefer-wayland-sessions-over-X11.patch --- sddm-0.19.0/debian/patches/revert_prefer-wayland-sessions-over-X11.patch 2021-02-18 17:31:52.000000000 +0000 +++ sddm-0.20.0/debian/patches/revert_prefer-wayland-sessions-over-X11.patch 2023-06-29 13:30:54.000000000 +0000 @@ -6,34 +6,30 @@ This reverts commit 994fa67b01ccfac1aaac08572302bbbea7842dc3. -diff --git a/src/common/Session.cpp b/src/common/Session.cpp -index 2d7b04f..40f7fb7 100644 --- a/src/common/Session.cpp +++ b/src/common/Session.cpp -@@ -120,14 +120,14 @@ namespace SDDM { - m_desktopNames.clear(); +@@ -142,14 +142,14 @@ + QStringList sessionDirs; switch (type) { - case WaylandSession: -- m_dir = QDir(mainConfig.Wayland.SessionDir.get()); +- sessionDirs = mainConfig.Wayland.SessionDir.get(); - m_xdgSessionType = QStringLiteral("wayland"); - break; case X11Session: - m_dir = QDir(mainConfig.X11.SessionDir.get()); + sessionDirs = mainConfig.X11.SessionDir.get(); m_xdgSessionType = QStringLiteral("x11"); break; + case WaylandSession: -+ m_dir = QDir(mainConfig.Wayland.SessionDir.get()); ++ sessionDirs = mainConfig.Wayland.SessionDir.get(); + m_xdgSessionType = QStringLiteral("wayland"); + break; default: m_xdgSessionType.clear(); break; -diff --git a/src/daemon/Display.cpp b/src/daemon/Display.cpp -index b95f6e5..515c4b9 100644 --- a/src/daemon/Display.cpp +++ b/src/daemon/Display.cpp -@@ -114,10 +114,10 @@ namespace SDDM { +@@ -211,10 +211,10 @@ if (autologinSession.isEmpty()) { autologinSession = stateConfig.Last.Session.get(); } @@ -47,32 +43,32 @@ } else { qCritical() << "Unable to find autologin session entry" << autologinSession; return false; -diff --git a/src/greeter/SessionModel.cpp b/src/greeter/SessionModel.cpp -index 1953c76..655f793 100644 --- a/src/greeter/SessionModel.cpp +++ b/src/greeter/SessionModel.cpp -@@ -41,8 +41,8 @@ namespace SDDM { - SessionModel::SessionModel(QObject *parent) : QAbstractListModel(parent), d(new SessionModelPrivate()) { +@@ -46,9 +46,9 @@ + // initial population beginResetModel(); -- populate(Session::WaylandSession, mainConfig.Wayland.SessionDir.get()); - populate(Session::X11Session, mainConfig.X11.SessionDir.get()); -+ populate(Session::WaylandSession, mainConfig.Wayland.SessionDir.get()); ++ populate(Session::X11Session, mainConfig.X11.SessionDir.get()); + if (dri_active) + populate(Session::WaylandSession, mainConfig.Wayland.SessionDir.get()); +- populate(Session::X11Session, mainConfig.X11.SessionDir.get()); endResetModel(); // refresh everytime a file is changed, added or removed -@@ -50,12 +50,12 @@ namespace SDDM { - connect(watcher, &QFileSystemWatcher::directoryChanged, [this](const QString &path) { +@@ -59,13 +59,13 @@ beginResetModel(); d->sessions.clear(); -- populate(Session::WaylandSession, mainConfig.Wayland.SessionDir.get()); - populate(Session::X11Session, mainConfig.X11.SessionDir.get()); -+ populate(Session::WaylandSession, mainConfig.Wayland.SessionDir.get()); + d->displayNames.clear(); ++ populate(Session::X11Session, mainConfig.X11.SessionDir.get()); + if (dri_active) + populate(Session::WaylandSession, mainConfig.Wayland.SessionDir.get()); +- populate(Session::X11Session, mainConfig.X11.SessionDir.get()); endResetModel(); }); -- watcher->addPath(mainConfig.Wayland.SessionDir.get()); - watcher->addPath(mainConfig.X11.SessionDir.get()); -+ watcher->addPath(mainConfig.Wayland.SessionDir.get()); +- watcher->addPaths(mainConfig.Wayland.SessionDir.get()); + watcher->addPaths(mainConfig.X11.SessionDir.get()); ++ watcher->addPaths(mainConfig.Wayland.SessionDir.get()); } SessionModel::~SessionModel() { diff -Nru sddm-0.19.0/debian/patches/series sddm-0.20.0/debian/patches/series --- sddm-0.19.0/debian/patches/series 2023-06-26 09:06:04.000000000 +0000 +++ sddm-0.20.0/debian/patches/series 2023-06-29 13:30:54.000000000 +0000 @@ -2,10 +2,7 @@ 02_use_debian_theme.diff 04_set_default_path.diff 05_add_debian_themes.diff -kubuntu_nvidia-prime.diff -upstream-2f034906d-use-only-basename-of-session.patch +06_nvidia_prime_setup.diff revert_prefer-wayland-sessions-over-X11.patch -upstream-8ad3c5afd-re-add-fish-profile-sourcing.diff -upstream_Fix-compilation-once-QTBUG-88431-gets-fixed.patch organize-screens-avoid-inset.patch use-bash-for-xsetup.patch diff -Nru sddm-0.19.0/debian/patches/upstream-2f034906d-use-only-basename-of-session.patch sddm-0.20.0/debian/patches/upstream-2f034906d-use-only-basename-of-session.patch --- sddm-0.19.0/debian/patches/upstream-2f034906d-use-only-basename-of-session.patch 2021-02-18 17:31:22.000000000 +0000 +++ sddm-0.20.0/debian/patches/upstream-2f034906d-use-only-basename-of-session.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,25 +0,0 @@ -From 2f034906ded7df6542a350bcc914f4ec808acbb0 Mon Sep 17 00:00:00 2001 -From: Fabian Vogt -Date: Mon, 9 Nov 2020 11:22:15 +0100 -Subject: [PATCH] Only use the base name for $DESKTOP_SESSION - -Other DMs don't use the path. - -Fixes #852 ---- - src/common/Session.cpp | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/src/common/Session.cpp b/src/common/Session.cpp -index 2d7b04f1..3de28ef1 100644 ---- a/src/common/Session.cpp -+++ b/src/common/Session.cpp -@@ -89,7 +89,7 @@ namespace SDDM { - - QString Session::desktopSession() const - { -- return fileName().replace(s_entryExtention, QString()); -+ return QFileInfo(m_fileName).completeBaseName(); - } - - QString Session::desktopNames() const diff -Nru sddm-0.19.0/debian/patches/upstream-8ad3c5afd-re-add-fish-profile-sourcing.diff sddm-0.20.0/debian/patches/upstream-8ad3c5afd-re-add-fish-profile-sourcing.diff --- sddm-0.19.0/debian/patches/upstream-8ad3c5afd-re-add-fish-profile-sourcing.diff 2022-07-12 11:19:39.000000000 +0000 +++ sddm-0.20.0/debian/patches/upstream-8ad3c5afd-re-add-fish-profile-sourcing.diff 1970-01-01 00:00:00.000000000 +0000 @@ -1,31 +0,0 @@ -Origin: https://github.com/sddm/sddm/commit/cf65e99eb8abfe2ee1ef7e2f7dc43862e83bf0ab -From: soredake -Date: Tue, 9 Mar 2021 07:28:11 +0000 -Subject: Add fish /etc/profile and $HOME/.profile sourcing ---- -diff --git a/data/scripts/Xsession b/data/scripts/Xsession -index 54498101..d0c2605f 100755 ---- a/data/scripts/Xsession -+++ b/data/scripts/Xsession -@@ -39,6 +39,8 @@ case $SHELL in - rm -f $xsess_tmp - ;; - */fish) -+ [ -f /etc/profile ] && . /etc/profile -+ [ -f $HOME/.profile ] && . $HOME/.profile - xsess_tmp=`mktemp /tmp/xsess-env-XXXXXX` - $SHELL --login -c "/bin/sh -c 'export -p' > $xsess_tmp" - . $xsess_tmp -diff --git a/data/scripts/wayland-session b/data/scripts/wayland-session -index 1f905543..552a39f5 100755 ---- a/data/scripts/wayland-session -+++ b/data/scripts/wayland-session -@@ -43,6 +43,8 @@ case $SHELL in - rm -f $wlsess_tmp - ;; - */fish) -+ [ -f /etc/profile ] && . /etc/profile -+ [ -f $HOME/.profile ] && . $HOME/.profile - xsess_tmp=`mktemp /tmp/xsess-env-XXXXXX` - $SHELL --login -c "/bin/sh -c 'export -p' > $xsess_tmp" - . $xsess_tmp diff -Nru sddm-0.19.0/debian/patches/upstream_Fix-compilation-once-QTBUG-88431-gets-fixed.patch sddm-0.20.0/debian/patches/upstream_Fix-compilation-once-QTBUG-88431-gets-fixed.patch --- sddm-0.19.0/debian/patches/upstream_Fix-compilation-once-QTBUG-88431-gets-fixed.patch 2023-06-26 09:05:07.000000000 +0000 +++ sddm-0.20.0/debian/patches/upstream_Fix-compilation-once-QTBUG-88431-gets-fixed.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,26 +0,0 @@ -From e93bf95c54ad8c2a1604f8d7be05339164b19308 Mon Sep 17 00:00:00 2001 -From: "aacid@kde.org" -Date: Thu, 12 Nov 2020 23:42:48 +0100 -Subject: [PATCH] Fix compilation once QTBUG-88431 gets fixed - -And also comes closes to Qt6 compatibility ---- - src/daemon/XorgDisplayServer.cpp | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/src/daemon/XorgDisplayServer.cpp b/src/daemon/XorgDisplayServer.cpp -index 5f93a1b..d5f29a9 100644 ---- a/src/daemon/XorgDisplayServer.cpp -+++ b/src/daemon/XorgDisplayServer.cpp -@@ -65,7 +65,7 @@ namespace SDDM { - // create a random hexadecimal number - const char *digits = "0123456789abcdef"; - for (int i = 0; i < 32; ++i) -- m_cookie[i] = digits[dis(gen)]; -+ m_cookie[i] = QLatin1Char(digits[dis(gen)]); - } - - XorgDisplayServer::~XorgDisplayServer() { --- -2.35.1 - diff -Nru sddm-0.19.0/debian/sddm.install sddm-0.20.0/debian/sddm.install --- sddm-0.19.0/debian/sddm.install 2021-02-18 17:31:22.000000000 +0000 +++ sddm-0.20.0/debian/sddm.install 2023-06-29 13:30:54.000000000 +0000 @@ -5,6 +5,8 @@ usr/bin/sddm* usr/lib/*/qt5/qml/SddmComponents/ usr/lib/*/sddm/ +usr/lib/sysusers.d/sddm.conf +usr/lib/tmpfiles.d/sddm.conf usr/share/man/man1/sddm-greeter.1 usr/share/man/man1/sddm.1 usr/share/man/man5/sddm-state.conf.5 diff -Nru sddm-0.19.0/.github/workflows/checks.yml sddm-0.20.0/.github/workflows/checks.yml --- sddm-0.19.0/.github/workflows/checks.yml 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/.github/workflows/checks.yml 2023-06-23 12:28:38.000000000 +0000 @@ -29,7 +29,7 @@ if: "!contains(github.event.head_commit.message, 'ci skip')" runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: fetch-depth: 1 - name: Validate XDG files @@ -40,7 +40,7 @@ if: "!contains(github.event.head_commit.message, 'ci skip')" runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: fetch-depth: 1 - name: Validate QML and JavaScript files diff -Nru sddm-0.19.0/README.md sddm-0.20.0/README.md --- sddm-0.19.0/README.md 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/README.md 2023-06-23 12:28:38.000000000 +0000 @@ -1,9 +1,9 @@ ## INTRODUCTION -[![Build Status](https://travis-ci.org/sddm/sddm.svg?branch=master)](https://travis-ci.org/sddm/sddm) [![IRC Network](https://img.shields.io/badge/irc-freenode-blue.svg "IRC Freenode")](https://webchat.freenode.net/?channels=sddm) -SDDM is a modern display manager for X11 and Wayland aiming to be fast, simple and beautiful. +SDDM is a modern display manager for X11 and Wayland sessions aiming to +be fast, simple and beautiful. It uses modern technologies like QtQuick, which in turn gives the designer the ability to create smooth, animated user interfaces. @@ -17,9 +17,6 @@ There are a few sample themes distributed with SDDM. They can be used as a starting point for new themes. -SDDM is developed by volunteers, please consider [donating](https://www.bountysource.com/teams/sddm) -money that can be used to support the features that you most desire. - ## SCREENSHOTS ![sample screenshot](https://raw.github.com/sddm/sddm/master/src/greeter/theme/maui.jpg) @@ -39,7 +36,7 @@ ## INSTALLATION -Qt >= 5.8.0 is required to use SDDM. +Qt >= 5.15.0 is required to use SDDM. SDDM runs the greeter as a system user named "sddm" whose home directory needs to be set to `/var/lib/sddm`. @@ -51,6 +48,17 @@ into the "video" group, otherwise errors regarding GL and drm devices might be experienced. +## VIRTUAL TERMINALS + +SDDM is assumed to start at the tty specified by the cmake variable +SDDM_INITIAL_VT which is an integer and defaults to 1. + +If SDDM_INITIAL_VT wasn't available, SDDM will use the next available one +instead. + +You can override SDDM_INITIAL_VT if you want to have a different one if, +for example, you were planning on using tty1 for something else. + ## LICENSE Source code of SDDM is licensed under GNU GPL version 2 or later (at your choosing). diff -Nru sddm-0.19.0/services/CMakeLists.txt sddm-0.20.0/services/CMakeLists.txt --- sddm-0.19.0/services/CMakeLists.txt 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/services/CMakeLists.txt 2023-06-23 12:28:38.000000000 +0000 @@ -1,6 +1,15 @@ if(SYSTEMD_FOUND) - configure_file("${CMAKE_CURRENT_SOURCE_DIR}/sddm.service.in" "${CMAKE_CURRENT_BINARY_DIR}/sddm.service") + configure_file(sddm.service.in sddm.service) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/sddm.service" DESTINATION "${SYSTEMD_SYSTEM_UNIT_DIR}") + + configure_file(sddm-sysuser.conf.in sddm-sysuser.conf) + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/sddm-sysuser.conf" DESTINATION "${SYSTEMD_SYSUSERS_DIR}" RENAME sddm.conf) +endif() + +# systemd-tmpfiles can be used standalone without other systemd parts +if(DEFINED SYSTEMD_TMPFILES_DIR) + configure_file(sddm-tmpfiles.conf.in sddm-tmpfiles.conf) + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/sddm-tmpfiles.conf" DESTINATION "${SYSTEMD_TMPFILES_DIR}" RENAME sddm.conf) endif() if(USE_ELOGIND) @@ -10,6 +19,17 @@ endif() configure_file("${CMAKE_CURRENT_SOURCE_DIR}/sddm-greeter.pam.in" "${CMAKE_CURRENT_BINARY_DIR}/sddm-greeter.pam") -install(FILES sddm.pam DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/pam.d RENAME sddm) -install(FILES sddm-autologin.pam DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/pam.d RENAME sddm-autologin) -install(FILES "${CMAKE_CURRENT_BINARY_DIR}/sddm-greeter.pam" DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/pam.d RENAME sddm-greeter) +if(EXISTS "/etc/debian_version") + install(FILES debian.sddm-autologin.pam DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/pam.d RENAME sddm-autologin) + install(FILES debian.sddm-greeter.pam DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/pam.d RENAME sddm-greeter) + install(FILES debian.sddm.pam DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/pam.d RENAME sddm) +else() + if(HAVE_PAM_FAILLOCK) + install(FILES sddm-autologin.pam DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/pam.d RENAME sddm-autologin) + else() + install(FILES sddm-autologin-tally2.pam DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/pam.d RENAME sddm-autologin) + endif() + + install(FILES sddm.pam DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/pam.d RENAME sddm) + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/sddm-greeter.pam" DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/pam.d RENAME sddm-greeter) +endif() diff -Nru sddm-0.19.0/services/debian.sddm-autologin.pam sddm-0.20.0/services/debian.sddm-autologin.pam --- sddm-0.19.0/services/debian.sddm-autologin.pam 1970-01-01 00:00:00.000000000 +0000 +++ sddm-0.20.0/services/debian.sddm-autologin.pam 2023-06-23 12:28:38.000000000 +0000 @@ -0,0 +1,32 @@ +#%PAM-1.0 + +# Block login if they are globally disabled +auth requisite pam_nologin.so +auth required pam_permit.so + +@include common-account + +# SELinux needs to be the first session rule. This ensures that any +# lingering context has been cleared. Without this it is possible that a +# module could execute code in the wrong domain. +session [success=ok ignore=ignore module_unknown=ignore default=bad] pam_selinux.so close +# Create a new session keyring. +session optional pam_keyinit.so force revoke +session required pam_limits.so +session required pam_loginuid.so +@include common-session +# SELinux needs to intervene at login time to ensure that the process starts +# in the proper default security context. Only sessions which are intended +# to run in the user's context should be run after this. +session [success=ok ignore=ignore module_unknown=ignore default=bad] pam_selinux.so open + +@include common-password + +# From the pam_env man page +# Since setting of PAM environment variables can have side effects to other modules, this module should be the last one on the stack. + +# Load environment from /etc/environment +session required pam_env.so + +# Load environment from /etc/default/locale +session required pam_env.so envfile=/etc/default/locale diff -Nru sddm-0.19.0/services/debian.sddm-greeter.pam sddm-0.20.0/services/debian.sddm-greeter.pam --- sddm-0.19.0/services/debian.sddm-greeter.pam 1970-01-01 00:00:00.000000000 +0000 +++ sddm-0.20.0/services/debian.sddm-greeter.pam 2023-06-23 12:28:38.000000000 +0000 @@ -0,0 +1,31 @@ +#%PAM-1.0 + +auth required pam_permit.so + +@include common-account + +# SELinux needs to be the first session rule. This ensures that any +# lingering context has been cleared. Without this it is possible that a +# module could execute code in the wrong domain. +session [success=ok ignore=ignore module_unknown=ignore default=bad] pam_selinux.so close +# Create a new session keyring. +session optional pam_keyinit.so force revoke +session required pam_limits.so +session required pam_loginuid.so +@include common-session +# SELinux needs to intervene at login time to ensure that the process starts +# in the proper default security context. Only sessions which are intended +# to run in the user's context should be run after this. +session [success=ok ignore=ignore module_unknown=ignore default=bad] pam_selinux.so open + +# Can't change password +password required pam_deny.so + +# From the pam_env man page +# Since setting of PAM environment variables can have side effects to other modules, this module should be the last one on the stack. + +# Load environment from /etc/environment +session required pam_env.so + +# Load environment from /etc/default/locale +session required pam_env.so envfile=/etc/default/locale diff -Nru sddm-0.19.0/services/debian.sddm.pam sddm-0.20.0/services/debian.sddm.pam --- sddm-0.19.0/services/debian.sddm.pam 1970-01-01 00:00:00.000000000 +0000 +++ sddm-0.20.0/services/debian.sddm.pam 2023-06-23 12:28:38.000000000 +0000 @@ -0,0 +1,40 @@ +#%PAM-1.0 + +# Block login if they are globally disabled +auth requisite pam_nologin.so +auth required pam_succeed_if.so user != root quiet_success + +# auth sufficient pam_succeed_if.so user ingroup nopasswdlogin +@include common-auth +# gnome_keyring breaks QProcess +-auth optional pam_gnome_keyring.so +-auth optional pam_kwallet5.so + +@include common-account + +# SELinux needs to be the first session rule. This ensures that any +# lingering context has been cleared. Without this it is possible that a +# module could execute code in the wrong domain. +session [success=ok ignore=ignore module_unknown=ignore default=bad] pam_selinux.so close +# Create a new session keyring. +session optional pam_keyinit.so force revoke +session required pam_limits.so +session required pam_loginuid.so +@include common-session +# SELinux needs to intervene at login time to ensure that the process starts +# in the proper default security context. Only sessions which are intended +# to run in the user's context should be run after this. +session [success=ok ignore=ignore module_unknown=ignore default=bad] pam_selinux.so open +-session optional pam_gnome_keyring.so auto_start +-session optional pam_kwallet5.so auto_start + +@include common-password + +# From the pam_env man page +# Since setting of PAM environment variables can have side effects to other modules, this module should be the last one on the stack. + +# Load environment from /etc/environment +session required pam_env.so + +# Load environment from /etc/default/locale and ~/.pam_environment +session required pam_env.so envfile=/etc/default/locale user_readenv=1 diff -Nru sddm-0.19.0/services/sddm-autologin.pam sddm-0.20.0/services/sddm-autologin.pam --- sddm-0.19.0/services/sddm-autologin.pam 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/services/sddm-autologin.pam 2023-06-23 12:28:38.000000000 +0000 @@ -1,6 +1,6 @@ #%PAM-1.0 auth required pam_env.so -auth required pam_tally2.so file=/var/log/tallylog onerr=succeed +auth required pam_faillock.so preauth auth required pam_shells.so auth required pam_nologin.so auth required pam_permit.so diff -Nru sddm-0.19.0/services/sddm-autologin-tally2.pam sddm-0.20.0/services/sddm-autologin-tally2.pam --- sddm-0.19.0/services/sddm-autologin-tally2.pam 1970-01-01 00:00:00.000000000 +0000 +++ sddm-0.20.0/services/sddm-autologin-tally2.pam 2023-06-23 12:28:38.000000000 +0000 @@ -0,0 +1,13 @@ +#%PAM-1.0 +auth required pam_env.so +auth required pam_tally2.so file=/var/log/tallylog onerr=succeed +auth required pam_shells.so +auth required pam_nologin.so +auth required pam_permit.so +-auth optional pam_gnome_keyring.so +-auth optional pam_kwallet5.so +account include system-local-login +password include system-local-login +session include system-local-login +-session optional pam_gnome_keyring.so auto_start +-session optional pam_kwallet5.so auto_start diff -Nru sddm-0.19.0/services/sddm.pam sddm-0.20.0/services/sddm.pam --- sddm-0.19.0/services/sddm.pam 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/services/sddm.pam 2023-06-23 12:28:38.000000000 +0000 @@ -1,15 +1,15 @@ #%PAM-1.0 -auth include system-login --auth optional pam_gnome_keyring.so --auth optional pam_kwallet5.so +auth include system-login +-auth optional pam_gnome_keyring.so +-auth optional pam_kwallet5.so -account include system-login +account include system-login -password include system-login --password optional pam_gnome_keyring.so use_authtok +password include system-login +-password optional pam_gnome_keyring.so use_authtok -session optional pam_keyinit.so force revoke -session include system-login --session optional pam_gnome_keyring.so auto_start --session optional pam_kwallet5.so auto_start +session optional pam_keyinit.so force revoke +session include system-login +-session optional pam_gnome_keyring.so auto_start +-session optional pam_kwallet5.so auto_start diff -Nru sddm-0.19.0/services/sddm.service.in sddm-0.20.0/services/sddm.service.in --- sddm-0.19.0/services/sddm.service.in 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/services/sddm.service.in 2023-06-23 12:28:38.000000000 +0000 @@ -1,8 +1,9 @@ [Unit] Description=Simple Desktop Display Manager Documentation=man:sddm(1) man:sddm.conf(5) -Conflicts=getty@tty1.service -After=systemd-user-sessions.service getty@tty1.service plymouth-quit.service systemd-logind.service +Conflicts=getty@tty${SDDM_INITIAL_VT}.service +After=systemd-user-sessions.service getty@tty${SDDM_INITIAL_VT}.service plymouth-quit.service systemd-logind.service +PartOf=graphical.target StartLimitIntervalSec=30 StartLimitBurst=2 diff -Nru sddm-0.19.0/services/sddm-sysuser.conf.in sddm-0.20.0/services/sddm-sysuser.conf.in --- sddm-0.19.0/services/sddm-sysuser.conf.in 1970-01-01 00:00:00.000000000 +0000 +++ sddm-0.20.0/services/sddm-sysuser.conf.in 2023-06-23 12:28:38.000000000 +0000 @@ -0,0 +1,2 @@ +#Type Name ID GECOS Home directory Shell +u sddm - "SDDM Greeter Account" ${STATE_DIR} - diff -Nru sddm-0.19.0/services/sddm-tmpfiles.conf.in sddm-0.20.0/services/sddm-tmpfiles.conf.in --- sddm-0.19.0/services/sddm-tmpfiles.conf.in 1970-01-01 00:00:00.000000000 +0000 +++ sddm-0.20.0/services/sddm-tmpfiles.conf.in 2023-06-23 12:28:38.000000000 +0000 @@ -0,0 +1,8 @@ +# Home dir of the sddm user, also contains state.conf +d ${STATE_DIR} 0750 sddm sddm +# This contains X11 auth files passed to Xorg and the greeter +d ${RUNTIME_DIR} 0711 root root +# Sockets for IPC +r! /tmp/sddm-auth* +# xauth files passed to user sessions +r! /tmp/xauth_* diff -Nru sddm-0.19.0/src/auth/Auth.cpp sddm-0.20.0/src/auth/Auth.cpp --- sddm-0.19.0/src/auth/Auth.cpp 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/src/auth/Auth.cpp 2023-06-23 12:28:38.000000000 +0000 @@ -62,9 +62,10 @@ AuthRequest *request { nullptr }; QProcess *child { nullptr }; QLocalSocket *socket { nullptr }; + QString displayServerCmd; QString sessionPath { }; QString user { }; - QString cookie { }; + QByteArray cookie { }; bool autologin { false }; bool greeter { false }; QProcessEnvironment environment { }; @@ -101,7 +102,7 @@ static std::unique_ptr self; if (!self) { self.reset(new SocketServer()); - self->listen(QStringLiteral("sddm-auth%1").arg(QUuid::createUuid().toString().replace(QRegExp(QStringLiteral("[{}]")), QString()))); + self->listen(QStringLiteral("sddm-auth-%1").arg(QUuid::createUuid().toString(QUuid::WithoutBraces))); } return self.get(); } @@ -132,7 +133,7 @@ env.insert(QStringLiteral("LANG"), QStringLiteral("C")); child->setProcessEnvironment(env); connect(child, QOverload::of(&QProcess::finished), this, &Auth::Private::childExited); - connect(child, QOverload::of(&QProcess::error), this, &Auth::Private::childError); + connect(child, &QProcess::errorOccurred, this, &Auth::Private::childError); connect(request, &AuthRequest::finished, this, &Auth::Private::requestFinished); connect(request, &AuthRequest::promptsChanged, parent, &Auth::requestChanged); } @@ -152,62 +153,75 @@ Auth *auth = qobject_cast(parent()); Msg m = MSG_UNKNOWN; SafeDataStream str(socket); - str.receive(); - str >> m; - switch (m) { - case ERROR: { - QString message; - Error type = ERROR_NONE; - str >> message >> type; - Q_EMIT auth->error(message, type); - break; - } - case INFO: { - QString message; - Info type = INFO_NONE; - str >> message >> type; - Q_EMIT auth->info(message, type); - break; - } - case REQUEST: { - Request r; - str >> r; - request->setRequest(&r); - break; - } - case AUTHENTICATED: { - QString user; - str >> user; - if (!user.isEmpty()) { - auth->setUser(user); - Q_EMIT auth->authentication(user, true); + while (socket->bytesAvailable() > 0) { + str.receive(); + str >> m; + switch (m) { + case ERROR: { + QString message; + Error type = ERROR_NONE; + str >> message >> type; + Q_EMIT auth->error(message, type); + break; + } + case INFO: { + QString message; + Info type = INFO_NONE; + str >> message >> type; + Q_EMIT auth->info(message, type); + break; + } + case REQUEST: { + Request r; + str >> r; + request->setRequest(&r); + break; + } + case AUTHENTICATED: { + QString user; + str >> user; + if (!user.isEmpty()) { + auth->setUser(user); + Q_EMIT auth->authentication(user, true); + str.reset(); + str << AUTHENTICATED << environment << cookie; + str.send(); + } + else { + Q_EMIT auth->authentication(user, false); + } + break; + } + case SESSION_STATUS: { + bool status; + str >> status; + Q_EMIT auth->sessionStarted(status); str.reset(); - str << AUTHENTICATED << environment << cookie; + str << SESSION_STATUS; str.send(); + break; } - else { - Q_EMIT auth->authentication(user, false); + case DISPLAY_SERVER_STARTED: { + QString displayName; + str >> displayName; + Q_EMIT auth->displayServerReady(displayName); + str.reset(); + str << DISPLAY_SERVER_STARTED; + str.send(); + break; + } + default: { + Q_EMIT auth->error(QStringLiteral("Auth: Unexpected value received: %1").arg(m), ERROR_INTERNAL); } - break; - } - case SESSION_STATUS: { - bool status; - str >> status; - Q_EMIT auth->sessionStarted(status); - str.reset(); - str << SESSION_STATUS; - str.send(); - break; - } - default: { - Q_EMIT auth->error(QStringLiteral("Auth: Unexpected value received: %1").arg(m), ERROR_INTERNAL); } } } void Auth::Private::childExited(int exitCode, QProcess::ExitStatus exitStatus) { if (exitStatus != QProcess::NormalExit) { - qWarning("Auth: sddm-helper crashed (exit code %d)", exitCode); + qWarning("Auth: sddm-helper (%s) crashed (exit code %d)", + qPrintable(child->arguments().join(QLatin1Char(' '))), + HelperExitStatus(exitStatus)); Q_EMIT qobject_cast(parent())->error(child->errorString(), ERROR_INTERNAL); } @@ -248,12 +262,13 @@ } Auth::~Auth() { + stop(); delete d; } void Auth::registerTypes() { - qmlRegisterType(); - qmlRegisterType(); + qmlRegisterAnonymousType("Auth", 1); + qmlRegisterAnonymousType("Auth", 1); qmlRegisterType("Auth", 1, 0, "Auth"); } @@ -266,7 +281,7 @@ return d->greeter; } - const QString& Auth::cookie() const { + const QByteArray& Auth::cookie() const { return d->cookie; } @@ -298,7 +313,7 @@ d->environment.insert(key, value); } - void Auth::setCookie(const QString& cookie) { + void Auth::setCookie(const QByteArray& cookie) { if (cookie != d->cookie) { d->cookie = cookie; Q_EMIT cookieChanged(); @@ -327,6 +342,14 @@ } } + void Auth::setDisplayServerCommand(const QString &command) + { + if (d->displayServerCmd != command) { + d->displayServerCmd = command; + Q_EMIT displayServerCommandChanged(); + } + } + void Auth::setSession(const QString& path) { if (path != d->sessionPath) { d->sessionPath = path; @@ -347,17 +370,31 @@ void Auth::start() { QStringList args; args << QStringLiteral("--socket") << SocketServer::instance()->fullServerName(); - args << QStringLiteral("--id") << QStringLiteral("%1").arg(d->id); + args << QStringLiteral("--id") << QString::number(d->id); if (!d->sessionPath.isEmpty()) args << QStringLiteral("--start") << d->sessionPath; if (!d->user.isEmpty()) args << QStringLiteral("--user") << d->user; if (d->autologin) args << QStringLiteral("--autologin"); + if (!d->displayServerCmd.isEmpty()) + args << QStringLiteral("--display-server") << d->displayServerCmd; if (d->greeter) args << QStringLiteral("--greeter"); d->child->start(QStringLiteral("%1/sddm-helper").arg(QStringLiteral(LIBEXEC_INSTALL_DIR)), args); } + + void Auth::stop() { + if (d->child->state() == QProcess::NotRunning) { + return; + } + + d->child->terminate(); + + // wait for finished + if (!d->child->waitForFinished(5000)) + d->child->kill(); + } } #include "Auth.moc" diff -Nru sddm-0.19.0/src/auth/Auth.h sddm-0.20.0/src/auth/Auth.h --- sddm-0.19.0/src/auth/Auth.h 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/src/auth/Auth.h 2023-06-23 12:28:38.000000000 +0000 @@ -54,7 +54,7 @@ Q_PROPERTY(bool autologin READ autologin WRITE setAutologin NOTIFY autologinChanged) Q_PROPERTY(bool greeter READ isGreeter WRITE setGreeter NOTIFY greeterChanged) Q_PROPERTY(bool verbose READ verbose WRITE setVerbose NOTIFY verboseChanged) - Q_PROPERTY(QString cookie READ cookie WRITE setCookie NOTIFY cookieChanged) + Q_PROPERTY(QByteArray cookie READ cookie WRITE setCookie NOTIFY cookieChanged) Q_PROPERTY(QString user READ user WRITE setUser NOTIFY userChanged) Q_PROPERTY(QString session READ session WRITE setSession NOTIFY sessionChanged) Q_PROPERTY(AuthRequest* request READ request NOTIFY requestChanged) @@ -69,6 +69,7 @@ INFO_PASS_CHANGE_REQUIRED, _INFO_LAST }; + Q_ENUM(Info) enum Error { ERROR_NONE = 0, @@ -77,20 +78,24 @@ ERROR_INTERNAL, _ERROR_LAST }; + Q_ENUM(Error) enum HelperExitStatus { HELPER_SUCCESS = 0, HELPER_AUTH_ERROR, HELPER_SESSION_ERROR, - HELPER_OTHER_ERROR + HELPER_OTHER_ERROR, + HELPER_DISPLAYSERVER_ERROR, + HELPER_TTY_ERROR, }; + Q_ENUM(HelperExitStatus) static void registerTypes(); bool autologin() const; bool isGreeter() const; bool verbose() const; - const QString &cookie() const; + const QByteArray &cookie() const; const QString &user() const; const QString &session() const; AuthRequest *request(); @@ -140,6 +145,12 @@ void setUser(const QString &user); /** + * Set the display server command to be started before the greeter. + * @param command Command of the display server to be started + */ + void setDisplayServerCommand(const QString &command); + + /** * Set the session to be started after authenticating. * @param path Path of the session executable to be started */ @@ -149,7 +160,7 @@ * Set the display server cookie, to be inserted into the user's $XAUTHORITY * @param cookie cookie data */ - void setCookie(const QString &cookie); + void setCookie(const QByteArray &cookie); public Q_SLOTS: /** @@ -157,12 +168,18 @@ */ void start(); + /** + * Indicates that we do not need the process anymore. + */ + void stop(); + Q_SIGNALS: void autologinChanged(); void greeterChanged(); void verboseChanged(); void cookieChanged(); void userChanged(); + void displayServerCommandChanged(); void sessionChanged(); void requestChanged(); @@ -185,6 +202,13 @@ void sessionStarted(bool success); /** + * Emitted when the display server is ready. + * + * @param displayName display name + */ + void displayServerReady(const QString &displayName); + + /** * Emitted when the helper quits, either after authentication or when the session ends. * Or, when something goes wrong. * diff -Nru sddm-0.19.0/src/auth/AuthMessages.h sddm-0.20.0/src/auth/AuthMessages.h --- sddm-0.19.0/src/auth/AuthMessages.h 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/src/auth/AuthMessages.h 2023-06-23 12:28:38.000000000 +0000 @@ -97,6 +97,7 @@ REQUEST, AUTHENTICATED, SESSION_STATUS, + DISPLAY_SERVER_STARTED, MSG_LAST, }; diff -Nru sddm-0.19.0/src/auth/AuthPrompt.h sddm-0.20.0/src/auth/AuthPrompt.h --- sddm-0.19.0/src/auth/AuthPrompt.h 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/src/auth/AuthPrompt.h 2023-06-23 12:28:38.000000000 +0000 @@ -40,7 +40,6 @@ */ class AuthPrompt : public QObject { Q_OBJECT - Q_ENUMS(Type) Q_PROPERTY(Type type READ type CONSTANT) Q_PROPERTY(QString message READ message CONSTANT) Q_PROPERTY(bool hidden READ hidden CONSTANT) @@ -59,6 +58,7 @@ LOGIN_USER = 0x0080, ///< On logging in: The username LOGIN_PASSWORD ///< On logging in: The password }; + Q_ENUM(Type) /** * @return the type of the prompt */ @@ -99,4 +99,4 @@ }; } -#endif //PROMPT_H \ No newline at end of file +#endif //PROMPT_H diff -Nru sddm-0.19.0/src/auth/AuthRequest.cpp sddm-0.20.0/src/auth/AuthRequest.cpp --- sddm-0.19.0/src/auth/AuthRequest.cpp 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/src/auth/AuthRequest.cpp 2023-06-23 12:28:38.000000000 +0000 @@ -73,7 +73,7 @@ } QQmlListProperty AuthRequest::promptsDecl() { - return QQmlListProperty(this, d->prompts); + return QQmlListProperty(this, &d->prompts); } void AuthRequest::done() { diff -Nru sddm-0.19.0/src/common/ConfigReader.cpp sddm-0.20.0/src/common/ConfigReader.cpp --- sddm-0.19.0/src/common/ConfigReader.cpp 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/src/common/ConfigReader.cpp 2023-06-23 12:28:38.000000000 +0000 @@ -27,19 +27,19 @@ #include #include #include +#include +#include QTextStream &operator>>(QTextStream &str, QStringList &list) { list.clear(); QString line = str.readLine(); - - const auto strings = line.splitRef(QLatin1Char(',')); - for (const QStringRef &s : strings) { - QStringRef trimmed = s.trimmed(); + const auto strings = QStringView{line}.split(u','); + for (const QStringView &s : strings) { + QStringView trimmed = s.trimmed(); if (!trimmed.isEmpty()) list.append(trimmed.toString()); } - return str; } @@ -50,7 +50,7 @@ QTextStream &operator>>(QTextStream &str, bool &val) { QString line = str.readLine(); - val = (0 == QStringRef(&line).trimmed().compare(QLatin1String("true"), Qt::CaseInsensitive)); + val = (0 == QStringView(line).trimmed().compare(QLatin1String("true"), Qt::CaseInsensitive)); return str; } @@ -197,7 +197,7 @@ return; while (!in.atEnd()) { QString line = QString::fromUtf8(in.readLine()); - QStringRef lineRef = QStringRef(&line).trimmed(); + QStringView lineRef = QStringView(line).trimmed(); // get rid of comments first lineRef = lineRef.left(lineRef.indexOf(QLatin1Char('#'))).trimmed(); @@ -211,7 +211,7 @@ int separatorPosition = lineRef.indexOf(QLatin1Char('=')); if (separatorPosition >= 0) { QString name = lineRef.left(separatorPosition).trimmed().toString(); - QStringRef value = lineRef.mid(separatorPosition + 1).trimmed(); + QStringView value = lineRef.mid(separatorPosition + 1).trimmed(); auto sectionIterator = m_sections.constFind(currentSection); if (sectionIterator != m_sections.constEnd() && sectionIterator.value()->entry(name)) @@ -265,7 +265,7 @@ QByteArray junk; // stores the junk to the temporary storage auto collectJunk = [&junk](const QString &data) { - junk.append(data); + junk.append(data.toUtf8()); }; // a short function to assign the current junk and current line to the right section, eventually create a new one @@ -275,7 +275,7 @@ sectionData[currentSection] = QByteArray(); } sectionData[currentSection].append(junk); - sectionData[currentSection].append(data); + sectionData[currentSection].append(data.toUtf8()); junk.clear(); }; @@ -285,16 +285,16 @@ while (!file.atEnd()) { const QString line = QString::fromUtf8(file.readLine()); // get rid of comments first - QStringRef trimmedLine = line.leftRef(line.indexOf(QLatin1Char('#'))).trimmed(); - QStringRef comment; + QStringView trimmedLine = QStringView{line}.left(line.indexOf(QLatin1Char('#'))).trimmed(); + QStringView comment; if (line.indexOf(QLatin1Char('#')) >= 0) - comment = line.midRef(line.indexOf(QLatin1Char('#'))).trimmed(); + comment = QStringView{line}.mid(line.indexOf(QLatin1Char('#'))).trimmed(); // value assignment int separatorPosition = trimmedLine.indexOf(QLatin1Char('=')); if (separatorPosition >= 0) { QString name = trimmedLine.left(separatorPosition).trimmed().toString(); - QStringRef value = trimmedLine.mid(separatorPosition + 1).trimmed(); + QStringView value = trimmedLine.mid(separatorPosition + 1).trimmed(); if (currentSection && currentSection->entry(name)) { // this monstrous condition checks the parameters if only one entry/section should be saved diff -Nru sddm-0.19.0/src/common/ConfigReader.h sddm-0.20.0/src/common/ConfigReader.h --- sddm-0.19.0/src/common/ConfigReader.h 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/src/common/ConfigReader.h 2023-06-23 12:28:38.000000000 +0000 @@ -51,8 +51,8 @@ __VA_ARGS__ \ } // entry wrapper -#define Entry(name, type, default, description) \ - SDDM::ConfigEntry name { this, QStringLiteral(#name), (default), (description) } +#define Entry(name, type, default, description, ...) \ + SDDM::ConfigEntry name { this, QStringLiteral(#name), default, description, __VA_ARGS__ } // section wrapper #define Section(name, ...) \ class name : public SDDM::ConfigSection { \ diff -Nru sddm-0.19.0/src/common/Configuration.h sddm-0.20.0/src/common/Configuration.h --- sddm-0.19.0/src/common/Configuration.h 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/src/common/Configuration.h 2023-06-23 12:28:38.000000000 +0000 @@ -37,6 +37,9 @@ enum NumState { NUM_NONE, NUM_SET_ON, NUM_SET_OFF }; // Name Type Default value Description + // TODO: Change default to x11-user in a future release + Entry(DisplayServer, QString, _S("x11"), _S("Which display server should be used.\n" + "Valid values are: x11, x11-user, wayland. Wayland support is experimental")); Entry(HaltCommand, QString, _S(HALT_COMMAND), _S("Halt command")); Entry(RebootCommand, QString, _S(REBOOT_COMMAND), _S("Reboot command")); Entry(Numlock, NumState, NUM_NONE, _S("Initial NumLock state. Can be on, off or none.\n" @@ -44,6 +47,7 @@ "NOTE: Currently ignored if autologin is enabled.")); Entry(InputMethod, QString, QStringLiteral("qtvirtualkeyboard"), _S("Input method module")); Entry(Namespaces, QStringList, QStringList(), _S("Comma-separated list of Linux namespaces for user session to enter")); + Entry(GreeterEnvironment, QStringList, QStringList(), _S("Comma-separated list of environment variables to be set")); // Name Entries (but it's a regular class again) Section(Theme, Entry(ThemeDir, QString, _S(DATA_INSTALL_DIR "/themes"), _S("Theme directory path")); @@ -51,6 +55,7 @@ Entry(FacesDir, QString, _S(DATA_INSTALL_DIR "/faces"), _S("Global directory for user avatars\n" "The files should be named .face.icon")); Entry(CursorTheme, QString, QString(), _S("Cursor theme used in the greeter")); + Entry(CursorSize, QString, QString(), _S("Cursor size used in the greeter")); Entry(Font, QString, QString(), _S("Font used in the greeter")); Entry(EnableAvatars, bool, true, _S("Enable display of custom user avatars")); Entry(DisableAvatarsThreshold,int, 7, _S("Number of users to use as threshold\n" @@ -63,22 +68,22 @@ Entry(ServerPath, QString, _S("/usr/bin/X"), _S("Path to X server binary")); Entry(ServerArguments, QString, _S("-nolisten tcp"), _S("Arguments passed to the X server invocation")); Entry(XephyrPath, QString, _S("/usr/bin/Xephyr"), _S("Path to Xephyr binary")); - Entry(XauthPath, QString, _S("/usr/bin/xauth"), _S("Path to xauth binary")); - Entry(SessionDir, QString, _S("/usr/share/xsessions"), _S("Directory containing available X sessions")); + Entry(SessionDir, QStringList, {_S("/usr/local/share/xsessions"), + _S("/usr/share/xsessions")}, _S("Comma-separated list of directories containing available X sessions")); Entry(SessionCommand, QString, _S(SESSION_COMMAND), _S("Path to a script to execute when starting the desktop session")); - Entry(SessionLogFile, QString, _S(".local/share/sddm/xorg-session.log"), _S("Path to the user session log file")); - Entry(UserAuthFile, QString, _S(".Xauthority"), _S("Path to the Xauthority file")); + Entry(SessionLogFile, QString, _S(".local/share/sddm/xorg-session.log"), _S("Path to the user session log file")); Entry(DisplayCommand, QString, _S(DATA_INSTALL_DIR "/scripts/Xsetup"), _S("Path to a script to execute when starting the display server")); Entry(DisplayStopCommand, QString, _S(DATA_INSTALL_DIR "/scripts/Xstop"), _S("Path to a script to execute when stopping the display server")); - Entry(MinimumVT, int, MINIMUM_VT, _S("The lowest virtual terminal number that will be used.")); - Entry(EnableHiDPI, bool, false, _S("Enable Qt's automatic high-DPI scaling")); + Entry(EnableHiDPI, bool, true, _S("Enable Qt's automatic high-DPI scaling")); ); Section(Wayland, - Entry(SessionDir, QString, _S("/usr/share/wayland-sessions"), _S("Directory containing available Wayland sessions")); + Entry(CompositorCommand, QString, _S("weston --shell=fullscreen-shell.so"), _S("Path of the Wayland compositor to execute when starting the greeter")); + Entry(SessionDir, QStringList, {_S("/usr/local/share/wayland-sessions"), + _S("/usr/share/wayland-sessions")}, _S("Comma-separated list of directories containing available Wayland sessions")); Entry(SessionCommand, QString, _S(WAYLAND_SESSION_COMMAND), _S("Path to a script to execute when starting the desktop session")); - Entry(SessionLogFile, QString, _S(".local/share/sddm/wayland-session.log"),_S("Path to the user session log file")); - Entry(EnableHiDPI, bool, false, _S("Enable Qt's automatic high-DPI scaling")); + Entry(SessionLogFile, QString, _S(".local/share/sddm/wayland-session.log"),_S("Path to the user session log file")); + Entry(EnableHiDPI, bool, true, _S("Enable Qt's automatic high-DPI scaling")); ); Section(Users, diff -Nru sddm-0.19.0/src/common/Constants.h.in sddm-0.20.0/src/common/Constants.h.in --- sddm-0.19.0/src/common/Constants.h.in 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/src/common/Constants.h.in 2023-06-23 12:28:38.000000000 +0000 @@ -28,6 +28,7 @@ #define COMPONENTS_TRANSLATION_DIR "@COMPONENTS_TRANSLATION_DIR@" #define RUNTIME_DIR "@RUNTIME_DIR@" #define STATE_DIR "@STATE_DIR@" +#define ACCOUNTSSERVICE_DATA_DIR "@ACCOUNTSSERVICE_DATA_DIR@" #define SESSION_COMMAND "@SESSION_COMMAND@" #define WAYLAND_SESSION_COMMAND "@WAYLAND_SESSION_COMMAND@" @@ -37,7 +38,6 @@ #define SYSTEM_CONFIG_DIR "@SYSTEM_CONFIG_DIR@" #define LOG_FILE "@LOG_FILE@" -#define MINIMUM_VT @MINIMUM_VT@ #define UID_MIN @UID_MIN@ #define UID_MAX @UID_MAX@ diff -Nru sddm-0.19.0/src/common/MessageHandler.h sddm-0.20.0/src/common/MessageHandler.h --- sddm-0.19.0/src/common/MessageHandler.h 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/src/common/MessageHandler.h 2023-06-23 12:28:38.000000000 +0000 @@ -24,13 +24,15 @@ #include "Constants.h" #include +#include #include +#include #include +#include #ifdef HAVE_JOURNALD #include -#include #endif namespace SDDM { @@ -41,6 +43,9 @@ case QtDebugMsg: priority = LOG_DEBUG; break; + case QtInfoMsg: + priority = LOG_INFO; + break; case QtWarningMsg: priority = LOG_WARNING; break; @@ -50,8 +55,6 @@ case QtFatalMsg: priority = LOG_ALERT; break; - default: - break; } char fileBuffer[PATH_MAX + sizeof("CODE_FILE=")]; @@ -69,17 +72,27 @@ static void standardLogger(QtMsgType type, const QString &msg) { static QFile file(QStringLiteral(LOG_FILE)); - // try to open file only if it's not already open - if (!file.isOpen()) { + // Try to open the log file if we're not outputting to a terminal + if (!file.isOpen() && !isatty(STDERR_FILENO)) { if (!file.open(QFile::Append | QFile::WriteOnly)) file.open(QFile::Truncate | QFile::WriteOnly); + + // If we can't open the file, create it in a writable location + // It will look spmething like ~/.local/share/$appname/sddm.log + // or for the sddm user /var/lib/sddm/.local/share/$appname/sddm.log + if (!file.isOpen()) { + QDir().mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation)); + file.setFileName(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QLatin1String("/sddm.log")); + if (!file.open(QFile::Append | QFile::WriteOnly)) + file.open(QFile::Truncate | QFile::WriteOnly); + } } // create timestamp QString timestamp = QDateTime::currentDateTime().toString(QStringLiteral("hh:mm:ss.zzz")); // set log priority - QString logPriority = QStringLiteral("(II)"); + QString logPriority = QStringLiteral("(II)"); switch (type) { case QtDebugMsg: break; @@ -102,36 +115,27 @@ file.write(logMessage.toLocal8Bit()); file.flush(); } else { - printf("%s", qPrintable(logMessage)); - fflush(stdout); + fputs(qPrintable(logMessage), stderr); + fflush(stderr); } } static void messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &prefix, const QString &msg) { - // copy message to edit it - QString logMessage = msg; - #ifdef HAVE_JOURNALD // don't log to journald if running interactively, this is likely // the case when running sddm in test mode - static bool isInteractive = isatty(STDIN_FILENO); + static bool isInteractive = isatty(STDERR_FILENO) && qgetenv("USER") != "sddm"; if (!isInteractive) { // log to journald - journaldLogger(type, context, logMessage); - } else { - // prepend program name - logMessage = prefix + msg; - - // log to file or stdout - standardLogger(type, logMessage); + journaldLogger(type, context, msg); + return; } -#else +#endif // prepend program name - logMessage = prefix + msg; + QString logMessage = prefix + msg; - // log to file or stdout + // log to file or stderr standardLogger(type, logMessage); -#endif } void DaemonMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) { diff -Nru sddm-0.19.0/src/common/Messages.h sddm-0.20.0/src/common/Messages.h --- sddm-0.19.0/src/common/Messages.h 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/src/common/Messages.h 2023-06-23 12:28:38.000000000 +0000 @@ -37,7 +37,8 @@ HostName, Capabilities, LoginSucceeded, - LoginFailed + LoginFailed, + InformationMessage, }; enum Capability { diff -Nru sddm-0.19.0/src/common/SafeDataStream.cpp sddm-0.20.0/src/common/SafeDataStream.cpp --- sddm-0.19.0/src/common/SafeDataStream.cpp 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/src/common/SafeDataStream.cpp 2023-06-23 12:28:38.000000000 +0000 @@ -21,6 +21,7 @@ #include "SafeDataStream.h" #include +#include namespace SDDM { SafeDataStream::SafeDataStream(QIODevice* device) diff -Nru sddm-0.19.0/src/common/SafeDataStream.h sddm-0.20.0/src/common/SafeDataStream.h --- sddm-0.19.0/src/common/SafeDataStream.h 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/src/common/SafeDataStream.h 2023-06-23 12:28:38.000000000 +0000 @@ -22,6 +22,7 @@ #define SAFEDATASTREAM_H #include +#include namespace SDDM { class SafeDataStream : public QDataStream { diff -Nru sddm-0.19.0/src/common/Session.cpp sddm-0.20.0/src/common/Session.cpp --- sddm-0.19.0/src/common/Session.cpp 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/src/common/Session.cpp 2023-06-23 12:28:38.000000000 +0000 @@ -21,6 +21,12 @@ #include #include #include +#include +#include +#include +#include +#include +#include #include "Configuration.h" #include "Session.h" @@ -52,6 +58,16 @@ return m_type; } + int Session::vt() const + { + return m_vt; + } + + void Session::setVt(int vt) + { + m_vt = vt; + } + QString Session::xdgSessionType() const { return m_xdgSessionType; @@ -89,7 +105,7 @@ QString Session::desktopSession() const { - return fileName().replace(s_entryExtention, QString()); + return QFileInfo(m_fileName).completeBaseName(); } QString Session::desktopNames() const @@ -107,6 +123,10 @@ return m_isNoDisplay; } + QProcessEnvironment Session::additionalEnv() const { + return m_additionalEnv; + } + void Session::setTo(Type type, const QString &_fileName) { QString fileName(_fileName); @@ -119,13 +139,15 @@ m_valid = false; m_desktopNames.clear(); + QStringList sessionDirs; + switch (type) { case WaylandSession: - m_dir = QDir(mainConfig.Wayland.SessionDir.get()); + sessionDirs = mainConfig.Wayland.SessionDir.get(); m_xdgSessionType = QStringLiteral("wayland"); break; case X11Session: - m_dir = QDir(mainConfig.X11.SessionDir.get()); + sessionDirs = mainConfig.X11.SessionDir.get(); m_xdgSessionType = QStringLiteral("x11"); break; default: @@ -133,54 +155,56 @@ break; } - m_fileName = m_dir.absoluteFilePath(fileName); - - qDebug() << "Reading from" << m_fileName; - - QFile file(m_fileName); - if (!file.open(QIODevice::ReadOnly)) + QFile file; + for (const auto &path: qAsConst(sessionDirs)) { + m_dir.setPath(path); + m_fileName = m_dir.absoluteFilePath(fileName); + + qDebug() << "Reading from" << m_fileName; + + file.setFileName(m_fileName); + if (file.open(QIODevice::ReadOnly)) + break; + } + if (!file.isOpen()) return; - QString current_section; + QSettings settings(m_fileName, QSettings::IniFormat); +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + settings.setIniCodec("UTF-8"); +#endif + QStringList locales = { QLocale().name() }; + if (auto clean = QLocale().name().remove(QRegularExpression(QLatin1String("_.*"))); clean != locales.constFirst()) { + locales << clean; + } - QTextStream in(&file); - while (!in.atEnd()) { - QString line = in.readLine(); - - if (line.startsWith(QLatin1String("["))) { - // The section name ends before the last ] before the start of a comment - int end = line.lastIndexOf(QLatin1Char(']'), line.indexOf(QLatin1Char('#'))); - if (end != -1) - current_section = line.mid(1, end - 1); - } + if (settings.status() != QSettings::NoError) + return; - if (current_section != QLatin1String("Desktop Entry")) - continue; // We are only interested in the "Desktop Entry" section + settings.beginGroup(QLatin1String("Desktop Entry")); - if (line.startsWith(QLatin1String("Name="))) { - if (type == WaylandSession) - if (line.mid(5).endsWith(QLatin1String(" (Wayland)"))) - m_displayName = QObject::tr("%1").arg(line.mid(5)); - else - m_displayName = QObject::tr("%1 (Wayland)").arg(line.mid(5)); - else - m_displayName = line.mid(5); + auto localizedValue = [&] (const QLatin1String &key) { + for (QString locale : qAsConst(locales)) { + QString localizedValue = settings.value(key + QLatin1Char('[') + locale + QLatin1Char(']'), QString()).toString(); + if (!localizedValue.isEmpty()) { + return localizedValue; + } } - if (line.startsWith(QLatin1String("Comment="))) - m_comment = line.mid(8); - if (line.startsWith(QLatin1String("Exec="))) - m_exec = line.mid(5); - if (line.startsWith(QStringLiteral("TryExec="))) - m_tryExec = line.mid(8); - if (line.startsWith(QLatin1String("DesktopNames="))) - m_desktopNames = line.mid(13).replace(QLatin1Char(';'), QLatin1Char(':')); - if (line.startsWith(QLatin1String("Hidden="))) - m_isHidden = line.mid(7).toLower() == QLatin1String("true"); - if (line.startsWith(QLatin1String("NoDisplay="))) - m_isNoDisplay = line.mid(10).toLower() == QLatin1String("true"); - } + return settings.value(key).toString(); + }; - file.close(); + m_displayName = localizedValue(QLatin1String("Name")); + m_comment = localizedValue(QLatin1String("Comment")); + m_exec = settings.value(QLatin1String("Exec"), QString()).toString(); + m_tryExec = settings.value(QLatin1String("TryExec"), QString()).toString(); + m_desktopNames = settings.value(QLatin1String("DesktopNames"), QString()).toString().replace(QLatin1Char(';'), QLatin1Char(':')); + QString hidden = settings.value(QLatin1String("Hidden"), QString()).toString(); + m_isHidden = hidden.toLower() == QLatin1String("true"); + QString noDisplay = settings.value(QLatin1String("NoDisplay"), QString()).toString(); + m_isNoDisplay = noDisplay.toLower() == QLatin1String("true"); + QString additionalEnv = settings.value(QLatin1String("X-SDDM-Env"), QString()).toString(); + m_additionalEnv = parseEnv(additionalEnv); + settings.endGroup(); m_type = type; m_valid = true; @@ -191,4 +215,21 @@ setTo(other.type(), other.fileName()); return *this; } + + QProcessEnvironment SDDM::Session::parseEnv(const QString &list) + { + QProcessEnvironment env; + const auto entryList = QStringView{list}.split(u',', Qt::SkipEmptyParts); + for (const auto &entry: entryList) { + int midPoint = entry.indexOf(QLatin1Char('=')); + if (midPoint < 0) { + qWarning() << "Malformed entry in" << fileName() << ":" << entry; + continue; + } + env.insert(entry.left(midPoint).toString(), entry.mid(midPoint+1).toString()); + } + return env; + } + + } diff -Nru sddm-0.19.0/src/common/Session.h sddm-0.20.0/src/common/Session.h --- sddm-0.19.0/src/common/Session.h 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/src/common/Session.h 2023-06-23 12:28:38.000000000 +0000 @@ -23,6 +23,7 @@ #include #include #include +#include namespace SDDM { class SessionModel; @@ -42,6 +43,9 @@ Type type() const; + int vt() const; + void setVt(int vt); + QString xdgSessionType() const; QDir directory() const; @@ -59,13 +63,17 @@ bool isHidden() const; bool isNoDisplay() const; + QProcessEnvironment additionalEnv() const; + void setTo(Type type, const QString &name); Session &operator=(const Session &other); private: + QProcessEnvironment parseEnv(const QString &list); bool m_valid; Type m_type; + int m_vt = 0; QDir m_dir; QString m_name; QString m_fileName; @@ -75,6 +83,7 @@ QString m_tryExec; QString m_xdgSessionType; QString m_desktopNames; + QProcessEnvironment m_additionalEnv; bool m_isHidden; bool m_isNoDisplay; diff -Nru sddm-0.19.0/src/common/SignalHandler.cpp sddm-0.20.0/src/common/SignalHandler.cpp --- sddm-0.19.0/src/common/SignalHandler.cpp 1970-01-01 00:00:00.000000000 +0000 +++ sddm-0.20.0/src/common/SignalHandler.cpp 2023-06-23 12:28:38.000000000 +0000 @@ -0,0 +1,189 @@ +/*************************************************************************** +* Copyright (c) 2013 Abdurrahman AVCI +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the +* Free Software Foundation, Inc., +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +***************************************************************************/ + +#include "SignalHandler.h" + +#include +#include +#include + +#include +#include +#include + +#include + +namespace SDDM { + std::once_flag signalsInitialized; + + int sigintFd[2]; + int sigtermFd[2]; + int sigcustomFd[2]; + + SignalHandler::SignalHandler(QObject *parent) : QObject(parent) { + std::call_once(signalsInitialized, &initialize); + + // If it's done before creating the QCoreApplication, it just will not work + Q_ASSERT(QCoreApplication::instance()); + + snint = new QSocketNotifier(sigintFd[1], QSocketNotifier::Read, this); + connect(snint, &QSocketNotifier::activated, this, &SignalHandler::handleSigint); + + snterm = new QSocketNotifier(sigtermFd[1], QSocketNotifier::Read, this); + connect(snterm, &QSocketNotifier::activated, this, &SignalHandler::handleSigterm); + + sncustom = new QSocketNotifier(sigcustomFd[1], QSocketNotifier::Read, this); + connect(sncustom, &QSocketNotifier::activated, this, &SignalHandler::handleSigCustom); + } + + void SignalHandler::initialize() { + if (::socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sigintFd)) + qCritical() << "Failed to create socket pair for SIGINT handling."; + + struct sigaction sigint = { }; + sigint.sa_handler = SignalHandler::intSignalHandler; + sigemptyset(&sigint.sa_mask); + sigint.sa_flags = SA_RESTART; + + if (sigaction(SIGINT, &sigint, 0) > 0) { + qCritical() << "Failed to set up SIGINT handler."; + return; + } + + if (::socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sigtermFd)) + qCritical() << "Failed to create socket pair for SIGTERM handling."; + + struct sigaction sigterm = { }; + sigterm.sa_handler = SignalHandler::termSignalHandler; + sigemptyset(&sigterm.sa_mask); + sigterm.sa_flags = SA_RESTART; + + if (sigaction(SIGTERM, &sigterm, 0) > 0) { + qCritical() << "Failed to set up SIGTERM handler."; + return; + } + + if (::socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sigcustomFd)) + qCritical() << "Failed to create socket pair for custom signals handling."; + } + + void SignalHandler::addCustomSignal(int signal) + { + struct sigaction sigcustom = { }; + sigcustom.sa_handler = SignalHandler::customSignalHandler; + sigemptyset(&sigcustom.sa_mask); + sigcustom.sa_flags = SA_RESTART; + + if (sigaction(signal, &sigcustom, 0) > 0) { + qCritical() << "Failed to set up " << strsignal(signal) << " handler."; + return; + } + } + + void SignalHandler::intSignalHandler(int) { + char a = 1; + if (::write(sigintFd[0], &a, sizeof(a)) == -1) { + qCritical() << "Error writing to the SIGINT handler"; + return; + } + } + + void SignalHandler::termSignalHandler(int) { + char a = 1; + if (::write(sigtermFd[0], &a, sizeof(a)) == -1) { + qCritical() << "Error writing to the SIGTERM handler"; + return; + } + } + + void SignalHandler::customSignalHandler(int signal) { + if (::write(sigcustomFd[0], &signal, sizeof(signal)) == -1) { + qCritical() << "Error writing to the " << strsignal(signal) << " handler"; + return; + } + } + + void SignalHandler::handleSigint() { + // disable notifier + snint->setEnabled(false); + + // read from socket + char a; + if (::read(sigintFd[1], &a, sizeof(a)) == -1) { + // something went wrong! + qCritical() << "Error reading from the socket"; + return; + } + + // log event + qWarning() << "Signal received: SIGINT"; + + // emit signal + emit sigintReceived(); + + // enable notifier + snint->setEnabled(true); + } + + void SignalHandler::handleSigterm() { + // disable notifier + snterm->setEnabled(false); + + // read from socket + char a; + if (::read(sigtermFd[1], &a, sizeof(a)) == -1) { + // something went wrong! + qCritical() << "Error reading from the socket"; + return; + } + + // log event + qWarning() << "Signal received: SIGTERM"; + + // emit signal + emit sigtermReceived(); + + // enable notifier + snterm->setEnabled(true); + } + + void SignalHandler::handleSigCustom() { + // disable notifier + sncustom->setEnabled(false); + + // read from socket + int signal; + if (::read(sigcustomFd[1], &signal, sizeof(signal)) == -1) { + // something went wrong! + qCritical() << "Error reading from the socket"; + return; + } + + // log event + qWarning() << "Signal received: " << strsignal(signal); + + // emit signal + emit customSignalReceived(signal); + + // enable notifier + sncustom->setEnabled(true); + } + + +} diff -Nru sddm-0.19.0/src/common/SignalHandler.h sddm-0.20.0/src/common/SignalHandler.h --- sddm-0.19.0/src/common/SignalHandler.h 1970-01-01 00:00:00.000000000 +0000 +++ sddm-0.20.0/src/common/SignalHandler.h 2023-06-23 12:28:38.000000000 +0000 @@ -0,0 +1,58 @@ +/*************************************************************************** +* Copyright (c) 2013 Abdurrahman AVCI +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the +* Free Software Foundation, Inc., +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +***************************************************************************/ + +#ifndef SDDM_SIGNALHANDLER_H +#define SDDM_SIGNALHANDLER_H + +#include + +class QSocketNotifier; + +namespace SDDM { + class SignalHandler : public QObject { + Q_OBJECT + Q_DISABLE_COPY(SignalHandler) + public: + SignalHandler(QObject *parent = 0); + + void addCustomSignal(int signal); + + signals: + void sighupReceived(); + void sigintReceived(); + void sigtermReceived(); + void customSignalReceived(int signal); + + private slots: + void handleSigint(); + void handleSigterm(); + void handleSigCustom(); + + private: + static void initialize(); + static void intSignalHandler(int unused); + static void termSignalHandler(int unused); + static void customSignalHandler(int unused); + + QSocketNotifier *snint { nullptr }; + QSocketNotifier *snterm { nullptr }; + QSocketNotifier *sncustom { nullptr }; + }; +} +#endif // SDDM_SIGNALHANDLER_H diff -Nru sddm-0.19.0/src/common/ThemeConfig.cpp sddm-0.20.0/src/common/ThemeConfig.cpp --- sddm-0.19.0/src/common/ThemeConfig.cpp 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/src/common/ThemeConfig.cpp 2023-06-23 12:28:38.000000000 +0000 @@ -1,4 +1,5 @@ /*************************************************************************** +* Copyright (c) 2023 Fabian Vogt * Copyright (c) 2016 Pier Luigi Fiorini * Copyright (c) 2014 David Edmundson * Copyright (c) 2013 Abdurrahman AVCI @@ -26,12 +27,20 @@ #include namespace SDDM { - ThemeConfig::ThemeConfig(const QString &path) { + ThemeConfig::ThemeConfig(const QString &path, QObject *parent) + : QQmlPropertyMap(this, parent) { setTo(path); } void ThemeConfig::setTo(const QString &path) { - clear(); + for(const QString &key : keys()) { + clear(key); + } + + if (path.isNull()) { + qDebug() << "Loaded empty theme configuration"; + return; + } qDebug() << "Loading theme configuration from" << path; @@ -41,8 +50,10 @@ // Support non-latin strings in background picture path // Warning: The codec must be set immediately after creating the QSettings object, // before accessing any data. +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) settings.setIniCodec("UTF-8"); userSettings.setIniCodec("UTF-8"); +#endif // read default keys for (const QString &key: settings.allKeys()) { @@ -61,4 +72,38 @@ insert(QStringLiteral("defaultBackground"), settings.value(QStringLiteral("background"))); } } + + QVariant ThemeConfig::value(const QString &key, const QVariant &def) { + if (!contains(key)) { + return def; + } + + return value(key); + } + + bool ThemeConfig::boolValue(const QString &key) { + return value(key).toBool(); + } + + int ThemeConfig::intValue(const QString &key) { + bool ok; + auto ret = value(key).toInt(&ok); + if (!ok) { + qWarning() << "Could not convert" << key << "(value" << value(key) << ") to int"; + } + return ret; + } + + qreal ThemeConfig::realValue(const QString &key) { + bool ok; + auto ret = value(key).toReal(&ok); + if (!ok) { + qWarning() << "Could not convert" << key << "(value" << value(key) << ") to real"; + } + return ret; + } + + QString ThemeConfig::stringValue(const QString &key) { + return value(key).toString(); + } } diff -Nru sddm-0.19.0/src/common/ThemeConfig.h sddm-0.20.0/src/common/ThemeConfig.h --- sddm-0.19.0/src/common/ThemeConfig.h 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/src/common/ThemeConfig.h 2023-06-23 12:28:38.000000000 +0000 @@ -21,14 +21,26 @@ #ifndef SDDM_THEMECONFIG_H #define SDDM_THEMECONFIG_H -#include +#include namespace SDDM { - class ThemeConfig : public QVariantMap { + class ThemeConfig : public QQmlPropertyMap { + Q_OBJECT public: - explicit ThemeConfig(const QString &path); + explicit ThemeConfig(const QString &path, QObject *parent = nullptr); void setTo(const QString &path); + + // Also provide QVariantMap's value(key, default) method + using QQmlPropertyMap::value; + QVariant value(const QString &key, const QVariant &def); + + // QSettings::IniFormat returns string types for basic + // types. Let the theme request specific conversions. + Q_INVOKABLE bool boolValue(const QString &key); + Q_INVOKABLE int intValue(const QString &key); + Q_INVOKABLE qreal realValue(const QString &key); + Q_INVOKABLE QString stringValue(const QString &key); }; } diff -Nru sddm-0.19.0/src/common/VirtualTerminal.cpp sddm-0.20.0/src/common/VirtualTerminal.cpp --- sddm-0.19.0/src/common/VirtualTerminal.cpp 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/src/common/VirtualTerminal.cpp 2023-06-23 12:28:38.000000000 +0000 @@ -27,29 +27,68 @@ #include #include #include +#ifdef __FreeBSD__ +#include +#else #include #include +#endif #include +#include +#include #define RELEASE_DISPLAY_SIGNAL (SIGRTMAX) #define ACQUIRE_DISPLAY_SIGNAL (SIGRTMAX - 1) namespace SDDM { namespace VirtualTerminal { - static void onAcquireDisplay(int signal) { - int fd = open("/dev/tty0", O_RDWR | O_NOCTTY); +#ifdef __FreeBSD__ + static const char *defaultVtPath = "/dev/ttyv0"; + + QString path(int vt) { + char c = (vt <= 10 ? '0' : 'a') + (vt - 1); + return QStringLiteral("/dev/ttyv%1").arg(c); + } + + int getVtActive(int fd) { + int vtActive = 0; + if (ioctl(fd, VT_GETACTIVE, &vtActive) < 0) { + qCritical() << "Failed to get current VT:" << strerror(errno); + return -1; + } + return vtActive; + } +#else + static const char *defaultVtPath = "/dev/tty0"; + + QString path(int vt) { + return QStringLiteral("/dev/tty%1").arg(vt); + } + + int getVtActive(int fd) { + vt_stat vtState { }; + if (ioctl(fd, VT_GETSTATE, &vtState) < 0) { + qCritical() << "Failed to get current VT:" << strerror(errno); + return -1; + } + return vtState.v_active; + } +#endif + + static void onAcquireDisplay([[maybe_unused]] int signal) { + int fd = open(defaultVtPath, O_RDWR | O_NOCTTY); ioctl(fd, VT_RELDISP, VT_ACKACQ); close(fd); } - static void onReleaseDisplay(int signal) { - int fd = open("/dev/tty0", O_RDWR | O_NOCTTY); + static void onReleaseDisplay([[maybe_unused]] int signal) { + int fd = open(defaultVtPath, O_RDWR | O_NOCTTY); ioctl(fd, VT_RELDISP, 1); close(fd); } static bool handleVtSwitches(int fd) { - vt_mode setModeRequest = { 0 }; + vt_mode setModeRequest { }; bool ok = true; setModeRequest.mode = VT_PROCESS; @@ -68,7 +107,7 @@ } static void fixVtMode(int fd, bool vt_auto) { - vt_mode getmodeReply = { 0 }; + vt_mode getmodeReply { }; int kernelDisplayMode = 0; bool modeFixed = false; bool ok = true; @@ -115,34 +154,43 @@ qDebug() << "VT mode didn't need to be fixed"; } - int setUpNewVt() { - // open VT master - int fd = open("/dev/tty0", O_RDWR | O_NOCTTY); + int currentVt() + { + int fd = open(defaultVtPath, O_RDWR | O_NOCTTY); if (fd < 0) { qCritical() << "Failed to open VT master:" << strerror(errno); return -1; } - - vt_stat vtState = { 0 }; - if (ioctl(fd, VT_GETSTATE, &vtState) < 0) { - qCritical() << "Failed to get current VT:" << strerror(errno); + auto closeFd = qScopeGuard([fd] { close(fd); + }); + + return getVtActive(fd); + } + + + int setUpNewVt() { + // open VT master + int fd = open(defaultVtPath, O_RDWR | O_NOCTTY); + if (fd < 0) { + qCritical() << "Failed to open VT master:" << strerror(errno); return -1; } + auto closeFd = qScopeGuard([fd] { + close(fd); + }); int vt = 0; if (ioctl(fd, VT_OPENQRY, &vt) < 0) { qCritical() << "Failed to open new VT:" << strerror(errno); - close(fd); return -1; } - close(fd); - // fallback to active VT if (vt <= 0) { - qWarning() << "New VT" << vt << "is not valid, fall back to" << vtState.v_active; - return vtState.v_active; + int vtActive = getVtActive(fd); + qWarning() << "New VT" << vt << "is not valid, fall back to" << vtActive; + return vtActive; } return vt; @@ -153,13 +201,19 @@ int fd; - int activeVtFd = open("/dev/tty0", O_RDWR | O_NOCTTY); + int activeVtFd = open(defaultVtPath, O_RDWR | O_NOCTTY); - QString ttyString = QStringLiteral("/dev/tty%1").arg(vt); + QString ttyString = path(vt); int vtFd = open(qPrintable(ttyString), O_RDWR | O_NOCTTY); if (vtFd != -1) { fd = vtFd; + // Clear VT + static const char *clearEscapeSequence = "\33[H\33[2J"; + if (write(vtFd, clearEscapeSequence, sizeof(clearEscapeSequence)) == -1) { + qWarning("Failed to clear VT %d: %s", vt, strerror(errno)); + } + // set graphics mode to prevent flickering if (ioctl(fd, KDSETMODE, KD_GRAPHICS) < 0) qWarning("Failed to set graphics mode for VT %d: %s", vt, strerror(errno)); @@ -171,7 +225,7 @@ fixVtMode(activeVtFd, vt_auto); } else { qWarning("Failed to open %s: %s", qPrintable(ttyString), strerror(errno)); - qDebug("Using /dev/tty0 instead of %s!", qPrintable(ttyString)); + qDebug("Using %s instead of %s!", defaultVtPath, qPrintable(ttyString)); fd = activeVtFd; } @@ -181,11 +235,21 @@ if (!vt_auto) handleVtSwitches(fd); - if (ioctl(fd, VT_ACTIVATE, vt) < 0) - qWarning("Couldn't initiate jump to VT %d: %s", vt, strerror(errno)); - else if (ioctl(fd, VT_WAITACTIVE, vt) < 0) - qWarning("Couldn't finalize jump to VT %d: %s", vt, strerror(errno)); + do { + errno = 0; + + if (ioctl(fd, VT_ACTIVATE, vt) < 0) { + if (errno == EINTR) + continue; + + qWarning("Couldn't initiate jump to VT %d: %s", vt, strerror(errno)); + break; + } + + if (ioctl(fd, VT_WAITACTIVE, vt) < 0 && errno != EINTR) + qWarning("Couldn't finalize jump to VT %d: %s", vt, strerror(errno)); + } while (errno == EINTR); close(activeVtFd); if (vtFd != -1) close(vtFd); diff -Nru sddm-0.19.0/src/common/VirtualTerminal_FreeBSD.cpp sddm-0.20.0/src/common/VirtualTerminal_FreeBSD.cpp --- sddm-0.19.0/src/common/VirtualTerminal_FreeBSD.cpp 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/src/common/VirtualTerminal_FreeBSD.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,37 +0,0 @@ -/*************************************************************************** -* Copyright (c) 2015 Pier Luigi Fiorini -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation; either version 2 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the -* Free Software Foundation, Inc., -* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -***************************************************************************/ - -#include -#include - -#include "VirtualTerminal.h" - - -namespace SDDM { - namespace VirtualTerminal { - int setUpNewVt() { - qDebug() << "New VT is unsupported on FreeBSD"; - return -1; - } - - void jumpToVt(int vt, bool vt_auto) { - qDebug() << "Jumping to VT" << vt << "is unsupported on FreeBSD"; - } - } -} diff -Nru sddm-0.19.0/src/common/VirtualTerminal.h sddm-0.20.0/src/common/VirtualTerminal.h --- sddm-0.19.0/src/common/VirtualTerminal.h 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/src/common/VirtualTerminal.h 2023-06-23 12:28:38.000000000 +0000 @@ -20,8 +20,12 @@ #ifndef SDDM_VIRTUALTERMINAL_H #define SDDM_VIRTUALTERMINAL_H +#include + namespace SDDM { namespace VirtualTerminal { + QString path(int vt); + int currentVt(); int setUpNewVt(); void jumpToVt(int vt, bool vt_auto); } diff -Nru sddm-0.19.0/src/common/XAuth.cpp sddm-0.20.0/src/common/XAuth.cpp --- sddm-0.19.0/src/common/XAuth.cpp 1970-01-01 00:00:00.000000000 +0000 +++ sddm-0.20.0/src/common/XAuth.cpp 2023-06-23 12:28:38.000000000 +0000 @@ -0,0 +1,182 @@ +/*************************************************************************** +* Copyright (c) 2023 Fabian Vogt +* Copyright (c) 2021 Pier Luigi Fiorini +* Copyright (c) 2013 Abdurrahman AVCI +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the +* Free Software Foundation, Inc., +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +***************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __FreeBSD__ +#define HOST_NAME_MAX _POSIX_HOST_NAME_MAX +#endif + +#include "Configuration.h" +#include "Constants.h" +#include "XAuth.h" + +namespace SDDM { + +XAuth::XAuth() +{ + m_authDir = QStringLiteral(RUNTIME_DIR); +} + +QString XAuth::authDirectory() const +{ + return m_authDir; +} + +void XAuth::setAuthDirectory(const QString &path) +{ + if (m_setup) { + qWarning("Unable to set xauth directory after setup"); + return; + } + + m_authDir = path; +} + +QString XAuth::authPath() const +{ + return m_authFile.fileName(); +} + +QByteArray XAuth::cookie() const +{ + return m_cookie; +} + +void XAuth::setup() +{ + if (m_setup) + return; + + m_setup = true; + + // Create directory if not existing + QDir().mkpath(m_authDir); + + // Set path + m_authFile.setFileTemplate(m_authDir + QStringLiteral("/xauth_XXXXXX")); + if(!m_authFile.open()) { + qFatal("Failed to create xauth file"); + } + + qDebug() << "Xauthority path:" << authPath(); + + // Generate cookie + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution<> dis(0, 0xFF); + + m_cookie.truncate(0); + m_cookie.reserve(16); + + // Create a random hexadecimal number + for(int i = 0; i < 16; i++) + m_cookie.append(dis(gen)); +} + +bool XAuth::addCookie(const QString &display) +{ + if (!m_setup) { + qWarning("Please setup xauth before adding a cookie"); + return false; + } + + return XAuth::writeCookieToFile(display, authPath(), m_cookie); +} + +bool XAuth::writeCookieToFile(const QString &display, const QString &fileName, + QByteArray cookie) +{ + + qDebug() << "Writing cookie to" << fileName; + + if(display.size() < 2 || display[0] != QLatin1Char(':') || cookie.size() != 16) { + qWarning().nospace() << "Unexpected DISPLAY='" << display << "' or cookie.size() = " << cookie.size(); + return false; + } + + // The file needs 0600 permissions + const int oldumask = umask(077); + + // Truncate the file. We don't support merging like the xauth tool does. + FILE * const authFp = fopen(qPrintable(fileName), "wb"); + auto error = errno; + umask(oldumask); + if (authFp == nullptr) { + qWarning().nospace() << "fopen() failed with errno=" << error; + return false; + } + + auto fileCloser = qScopeGuard([authFp]{ fclose(authFp); }); + char localhost[HOST_NAME_MAX + 1] = ""; + if (gethostname(localhost, sizeof(localhost)) < 0) + strcpy(localhost, "localhost"); + + ::Xauth auth = {}; + char cookieName[] = "MIT-MAGIC-COOKIE-1"; + + // Skip the ':' + QByteArray displayNumberUtf8 = QStringView{display}.mid(1).toUtf8(); + + auth.family = FamilyLocal; + auth.address = localhost; + auth.address_length = strlen(auth.address); + auth.number = displayNumberUtf8.data(); + auth.number_length = displayNumberUtf8.size(); + auth.name = cookieName; + auth.name_length = sizeof(cookieName) - 1; + auth.data = cookie.data(); + auth.data_length = cookie.size(); + + errno = 0; + if (XauWriteAuth(authFp, &auth) == 0) { + qWarning().nospace() << "XauWriteAuth(FamilyLocal) failed with errno=" << errno; + return false; + } + + // Write the same entry again, just with FamilyWild + auth.family = FamilyWild; + auth.address_length = 0; + errno = 0; + if (XauWriteAuth(authFp, &auth) == 0) { + qWarning().nospace() << "XauWriteAuth(FamilyWild) failed with errno=" << errno; + return false; + } + + if (fflush(authFp) != 0) { + qWarning().nospace() << "fflush() failed with errno=" << errno; + return false; + } + + return true; +} + +} // namespace SDDM diff -Nru sddm-0.19.0/src/common/XAuth.h sddm-0.20.0/src/common/XAuth.h --- sddm-0.19.0/src/common/XAuth.h 1970-01-01 00:00:00.000000000 +0000 +++ sddm-0.20.0/src/common/XAuth.h 2023-06-23 12:28:38.000000000 +0000 @@ -0,0 +1,56 @@ +/*************************************************************************** +* Copyright (c) 2021 Pier Luigi Fiorini +* Copyright (c) 2013 Abdurrahman AVCI +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the +* Free Software Foundation, Inc., +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +***************************************************************************/ + +#ifndef SDDM_XAUTH_H +#define SDDM_XAUTH_H + +#include +#include + +namespace SDDM { + +class XAuth +{ +public: + XAuth(); + + QString authDirectory() const; + void setAuthDirectory(const QString &path); + + QString authPath() const; + QByteArray cookie() const; + + void setup(); + bool addCookie(const QString &display); + + static bool writeCookieToFile(const QString &display, + const QString &fileName, + QByteArray cookie); + +private: + bool m_setup = false; + QString m_authDir; + QTemporaryFile m_authFile; + QByteArray m_cookie; +}; + +} // namespace SDDM + +#endif // SDDM_XAUTH_H diff -Nru sddm-0.19.0/src/daemon/CMakeLists.txt sddm-0.20.0/src/daemon/CMakeLists.txt --- sddm-0.19.0/src/daemon/CMakeLists.txt 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/src/daemon/CMakeLists.txt 2023-06-23 12:28:38.000000000 +0000 @@ -2,9 +2,11 @@ "${CMAKE_SOURCE_DIR}/src/common" "${CMAKE_SOURCE_DIR}/src/auth" "${CMAKE_BINARY_DIR}/src/common" + ${LIBXAU_INCLUDE_DIRS} "${LIBXCB_INCLUDE_DIR}" ) +configure_file(config.h.in config.h IMMEDIATE @ONLY) set(DAEMON_SOURCES ${CMAKE_SOURCE_DIR}/src/common/Configuration.cpp ${CMAKE_SOURCE_DIR}/src/common/SafeDataStream.cpp @@ -13,6 +15,8 @@ ${CMAKE_SOURCE_DIR}/src/common/ThemeMetadata.cpp ${CMAKE_SOURCE_DIR}/src/common/Session.cpp ${CMAKE_SOURCE_DIR}/src/common/SocketWriter.cpp + ${CMAKE_SOURCE_DIR}/src/common/XAuth.cpp + ${CMAKE_SOURCE_DIR}/src/common/SignalHandler.cpp ${CMAKE_SOURCE_DIR}/src/auth/Auth.cpp ${CMAKE_SOURCE_DIR}/src/auth/AuthPrompt.cpp ${CMAKE_SOURCE_DIR}/src/auth/AuthRequest.cpp @@ -22,27 +26,23 @@ DisplayManager.cpp DisplayServer.cpp LogindDBusTypes.cpp - XorgDisplayServer.cpp Greeter.cpp PowerManager.cpp Seat.cpp SeatManager.cpp - SignalHandler.cpp SocketServer.cpp + XorgDisplayServer.cpp + XorgUserDisplayServer.cpp + XorgUserDisplayServer.h + WaylandDisplayServer.cpp + WaylandDisplayServer.h ) -# Different implementations of the VT switching code -# (where the FreeBSD version does nothing). -if(${CMAKE_SYSTEM} MATCHES "FreeBSD") - list(APPEND DAEMON_SOURCES ${CMAKE_SOURCE_DIR}/src/common/VirtualTerminal_FreeBSD.cpp) -else() - list(APPEND DAEMON_SOURCES ${CMAKE_SOURCE_DIR}/src/common/VirtualTerminal.cpp) -endif() - -qt5_add_dbus_adaptor(DAEMON_SOURCES "${CMAKE_SOURCE_DIR}/data/interfaces/org.freedesktop.DisplayManager.xml" "DisplayManager.h" SDDM::DisplayManager) -qt5_add_dbus_adaptor(DAEMON_SOURCES "${CMAKE_SOURCE_DIR}/data/interfaces/org.freedesktop.DisplayManager.Seat.xml" "DisplayManager.h" SDDM::DisplayManagerSeat) -qt5_add_dbus_adaptor(DAEMON_SOURCES "${CMAKE_SOURCE_DIR}/data/interfaces/org.freedesktop.DisplayManager.Session.xml" "DisplayManager.h" SDDM::DisplayManagerSession) +list(APPEND DAEMON_SOURCES ${CMAKE_SOURCE_DIR}/src/common/VirtualTerminal.cpp) +qt_add_dbus_adaptor(DAEMON_SOURCES "${CMAKE_SOURCE_DIR}/data/interfaces/org.freedesktop.DisplayManager.xml" "DisplayManager.h" SDDM::DisplayManager) +qt_add_dbus_adaptor(DAEMON_SOURCES "${CMAKE_SOURCE_DIR}/data/interfaces/org.freedesktop.DisplayManager.Seat.xml" "DisplayManager.h" SDDM::DisplayManagerSeat) +qt_add_dbus_adaptor(DAEMON_SOURCES "${CMAKE_SOURCE_DIR}/data/interfaces/org.freedesktop.DisplayManager.Session.xml" "DisplayManager.h" SDDM::DisplayManagerSession) set_source_files_properties("${CMAKE_SOURCE_DIR}/data/interfaces/org.freedesktop.login1.Manager.xml" PROPERTIES INCLUDE "LogindDBusTypes.h" @@ -55,15 +55,16 @@ INCLUDE "LogindDBusTypes.h" ) -qt5_add_dbus_interface(DAEMON_SOURCES "${CMAKE_SOURCE_DIR}/data/interfaces/org.freedesktop.login1.Manager.xml" "Login1Manager") -qt5_add_dbus_interface(DAEMON_SOURCES "${CMAKE_SOURCE_DIR}/data/interfaces/org.freedesktop.login1.Seat.xml" "Login1Seat") -qt5_add_dbus_interface(DAEMON_SOURCES "${CMAKE_SOURCE_DIR}/data/interfaces/org.freedesktop.login1.Session.xml" "Login1Session") +qt_add_dbus_interface(DAEMON_SOURCES "${CMAKE_SOURCE_DIR}/data/interfaces/org.freedesktop.login1.Manager.xml" "Login1Manager") +qt_add_dbus_interface(DAEMON_SOURCES "${CMAKE_SOURCE_DIR}/data/interfaces/org.freedesktop.login1.Seat.xml" "Login1Seat") +qt_add_dbus_interface(DAEMON_SOURCES "${CMAKE_SOURCE_DIR}/data/interfaces/org.freedesktop.login1.Session.xml" "Login1Session") add_executable(sddm ${DAEMON_SOURCES}) target_link_libraries(sddm - Qt5::DBus - Qt5::Network - Qt5::Qml + Qt${QT_MAJOR_VERSION}::DBus + Qt${QT_MAJOR_VERSION}::Network + Qt${QT_MAJOR_VERSION}::Qml + ${LIBXAU_LINK_LIBRARIES} ${LIBXCB_LIBRARIES}) if(PAM_FOUND) target_link_libraries(sddm ${PAM_LIBRARIES}) diff -Nru sddm-0.19.0/src/daemon/config.h.in sddm-0.20.0/src/daemon/config.h.in --- sddm-0.19.0/src/daemon/config.h.in 1970-01-01 00:00:00.000000000 +0000 +++ sddm-0.20.0/src/daemon/config.h.in 2023-06-23 12:28:38.000000000 +0000 @@ -0,0 +1,2 @@ +#pragma once +static const int SDDM_INITIAL_VT = @SDDM_INITIAL_VT@; diff -Nru sddm-0.19.0/src/daemon/DaemonApp.cpp sddm-0.20.0/src/daemon/DaemonApp.cpp --- sddm-0.19.0/src/daemon/DaemonApp.cpp 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/src/daemon/DaemonApp.cpp 2023-06-23 12:28:38.000000000 +0000 @@ -28,6 +28,7 @@ #include "MessageHandler.h" +#include #include #include #include @@ -49,6 +50,21 @@ // set testing parameter m_testing = (arguments().indexOf(QStringLiteral("--test-mode")) != -1); + bool consoleKitServiceActivatable = false; + QDBusReply activatableNamesReply = QDBusConnection::systemBus().interface()->activatableServiceNames(); + if (activatableNamesReply.isValid()) { + consoleKitServiceActivatable = activatableNamesReply.value().contains(QStringLiteral("org.freedesktop.ConsoleKit")); + } + + // If ConsoleKit isn't started by the OS init system (FreeBSD, for instance), + // we start it ourselves during the sddm startup + if (consoleKitServiceActivatable) { + QDBusReply registeredReply = QDBusConnection::systemBus().interface()->isServiceRegistered(QStringLiteral("org.freedesktop.ConsoleKit")); + if (registeredReply.isValid() && registeredReply.value() == false) { + QDBusConnection::systemBus().interface()->startService(QStringLiteral("org.freedesktop.ConsoleKit")); + } + } + // create display manager m_displayManager = new DisplayManager(this); @@ -65,9 +81,6 @@ // create signal handler m_signalHandler = new SignalHandler(this); - // initialize signal signalHandler - SignalHandler::initialize(); - // quit when SIGINT, SIGTERM received connect(m_signalHandler, &SignalHandler::sigintReceived, this, &DaemonApp::quit); connect(m_signalHandler, &SignalHandler::sigtermReceived, this, &DaemonApp::quit); diff -Nru sddm-0.19.0/src/daemon/Display.cpp sddm-0.20.0/src/daemon/Display.cpp --- sddm-0.19.0/src/daemon/Display.cpp 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/src/daemon/Display.cpp 2023-06-23 12:28:38.000000000 +0000 @@ -25,11 +25,11 @@ #include "DaemonApp.h" #include "DisplayManager.h" #include "XorgDisplayServer.h" +#include "XorgUserDisplayServer.h" #include "Seat.h" #include "SocketServer.h" #include "Greeter.h" #include "Utils.h" -#include "SignalHandler.h" #include #include @@ -38,6 +38,10 @@ #include #include +#include + +#include +#include #include #include @@ -45,16 +49,89 @@ #include "Login1Manager.h" #include "Login1Session.h" +#include "VirtualTerminal.h" +#include "WaylandDisplayServer.h" +#include "config.h" +static int s_ttyFailures = 0; +#define STRINGIFY(x) #x namespace SDDM { - Display::Display(const int terminalId, Seat *parent) : QObject(parent), - m_terminalId(terminalId), + bool isTtyInUse(const QString &desiredTty) { + if (Logind::isAvailable()) { + OrgFreedesktopLogin1ManagerInterface manager(Logind::serviceName(), Logind::managerPath(), QDBusConnection::systemBus()); + auto reply = manager.ListSessions(); + reply.waitForFinished(); + + const auto info = reply.value(); + for(const SessionInfo &s : info) { + OrgFreedesktopLogin1SessionInterface session(Logind::serviceName(), s.sessionPath.path(), QDBusConnection::systemBus()); + if (desiredTty == session.tTY() && session.state() != QLatin1String("closing")) { + qDebug() << "tty" << desiredTty << "already in use by" << session.user().path.path() << session.state() + << session.display() << session.desktop() << session.vTNr(); + return true; + } + } + } + return false; + } + + int fetchAvailableVt() { + if (!isTtyInUse(QStringLiteral("tty" STRINGIFY(SDDM_INITIAL_VT)))) { + return SDDM_INITIAL_VT; + } + const auto vt = VirtualTerminal::currentVt(); + if (vt > 0 && !isTtyInUse(QStringLiteral("tty%1").arg(vt))) { + return vt; + } + return VirtualTerminal::setUpNewVt(); + } + + Display::DisplayServerType Display::defaultDisplayServerType() + { + const QString &displayServerType = mainConfig.DisplayServer.get().toLower(); + DisplayServerType ret; + if (displayServerType == QStringLiteral("x11-user")) { + ret = X11UserDisplayServerType; + } else if (displayServerType == QStringLiteral("wayland")) { + ret = WaylandDisplayServerType; + } else { + if (displayServerType != QLatin1String("x11")) { + qWarning("\"%s\" is an invalid value for General.DisplayServer: fall back to \"x11\"", + qPrintable(displayServerType)); + } + ret = X11DisplayServerType; + } + return ret; + } + + Display::Display(Seat *parent, DisplayServerType serverType) + : QObject(parent), + m_displayServerType(serverType), m_auth(new Auth(this)), - m_displayServer(new XorgDisplayServer(this)), m_seat(parent), m_socketServer(new SocketServer(this)), - m_greeter(new Greeter(this)) { + m_greeter(new Greeter(this)) + { + // Create display server + switch (m_displayServerType) { + case X11DisplayServerType: + m_terminalId = VirtualTerminal::setUpNewVt(); + m_displayServer = new XorgDisplayServer(this); + break; + case X11UserDisplayServerType: + m_terminalId = fetchAvailableVt(); + m_displayServer = new XorgUserDisplayServer(this); + m_greeter->setDisplayServerCommand(XorgUserDisplayServer::command(this)); + break; + case WaylandDisplayServerType: + m_terminalId = fetchAvailableVt(); + m_displayServer = new WaylandDisplayServer(this); + m_greeter->setDisplayServerCommand(mainConfig.Wayland.CompositorCommand.get()); + break; + } + + qDebug("Using VT %d", m_terminalId); // respond to authentication requests m_auth->setVerbose(true); @@ -73,20 +150,40 @@ connect(m_socketServer, &SocketServer::login, this, &Display::login); // connect login result signals - connect(this, SIGNAL(loginFailed(QLocalSocket*)), m_socketServer, SLOT(loginFailed(QLocalSocket*))); - connect(this, SIGNAL(loginSucceeded(QLocalSocket*)), m_socketServer, SLOT(loginSucceeded(QLocalSocket*))); + connect(this, &Display::loginFailed, m_socketServer, &SocketServer::loginFailed); + connect(this, &Display::loginSucceeded, m_socketServer, &SocketServer::loginSucceeded); + + connect(m_greeter, &Greeter::failed, this, &Display::stop); + connect(m_greeter, &Greeter::ttyFailed, this, [this] { + ++s_ttyFailures; + if (s_ttyFailures > 5) { + QCoreApplication::exit(23); + } + // It might be the case that we are trying a tty that has been taken over by a + // different process. In such a case, switch back to the initial one and try again. + VirtualTerminal::jumpToVt(SDDM_INITIAL_VT, true); + stop(); + }); + connect(m_greeter, &Greeter::displayServerFailed, this, &Display::displayServerFailed); } Display::~Display() { + disconnect(m_auth, &Auth::finished, this, &Display::slotHelperFinished); stop(); } - QString Display::displayId() const { - return m_displayServer->display(); + Display::DisplayServerType Display::displayServerType() const + { + return m_displayServerType; } - const int Display::terminalId() const { - return m_terminalId; + DisplayServer *Display::displayServer() const + { + return m_displayServer; + } + + int Display::terminalId() const { + return m_auth->isActive() ? m_sessionTerminalId : m_terminalId; } const QString &Display::name() const { @@ -154,6 +251,8 @@ bool success = attemptAutologin(); if (success) { return; + } else { + qWarning() << "Autologin failed!"; } } @@ -171,9 +270,6 @@ } } - // set greeter params - m_greeter->setDisplay(this); - m_greeter->setAuthPath(qobject_cast(m_displayServer)->authPath()); m_greeter->setSocket(m_socketServer->socketAddress()); m_greeter->setTheme(findGreeterTheme()); @@ -195,6 +291,8 @@ // stop the greeter m_greeter->stop(); + m_auth->stop(); + // stop socket server m_socketServer->stop(); @@ -218,6 +316,7 @@ //the SDDM user has special privileges that skip password checking so that we can load the greeter //block ever trying to log in as the SDDM user if (user == QLatin1String("sddm")) { + emit loginFailed(m_socket); return; } @@ -244,7 +343,8 @@ return QString(); } - bool Display::findSessionEntry(const QDir &dir, const QString &name) const { + bool Display::findSessionEntry(const QStringList &dirPaths, const QString &name) const { + const QFileInfo fileInfo(name); QString fileName = name; // append extension @@ -252,7 +352,17 @@ if (!fileName.endsWith(extension)) fileName += extension; - return dir.exists(fileName); + for (const auto &path: dirPaths) { + QDir dir = path; + + // Given an absolute path: Check that it matches dir + if (fileInfo.isAbsolute() && fileInfo.absolutePath() != dir.absolutePath()) + continue; + + if (dir.exists(fileName)) + return true; + } + return false; } void Display::startAuth(const QString &user, const QString &password, const Session &session) { @@ -297,54 +407,64 @@ } } - // cache last session - m_lastSession = session; - // save session desktop file name, we'll use it to set the // last session later, in slotAuthenticationFinished() m_sessionName = session.fileName(); + m_sessionTerminalId = m_terminalId; + if ((session.type() == Session::WaylandSession && m_displayServerType == X11DisplayServerType) || (m_greeter->isRunning() && m_displayServerType != X11DisplayServerType)) { + // Create a new VT when we need to have another compositor running + m_sessionTerminalId = VirtualTerminal::setUpNewVt(); + } + // some information - qDebug() << "Session" << m_sessionName << "selected, command:" << session.exec(); + qDebug() << "Session" << m_sessionName << "selected, command:" << session.exec() << "for VT" << m_sessionTerminalId; QProcessEnvironment env; - - if (seat()->name() == QLatin1String("seat0")) { - // Use the greeter VT, for wayland sessions the helper overwrites this - env.insert(QStringLiteral("XDG_VTNR"), QString::number(terminalId())); - } + env.insert(session.additionalEnv()); env.insert(QStringLiteral("PATH"), mainConfig.Users.DefaultPath.get()); - if (session.xdgSessionType() == QLatin1String("x11")) - env.insert(QStringLiteral("DISPLAY"), name()); env.insert(QStringLiteral("XDG_SEAT_PATH"), daemonApp->displayManager()->seatPath(seat()->name())); env.insert(QStringLiteral("XDG_SESSION_PATH"), daemonApp->displayManager()->sessionPath(QStringLiteral("Session%1").arg(daemonApp->newSessionId()))); env.insert(QStringLiteral("DESKTOP_SESSION"), session.desktopSession()); - env.insert(QStringLiteral("XDG_CURRENT_DESKTOP"), session.desktopNames()); + if (!session.desktopNames().isEmpty()) + env.insert(QStringLiteral("XDG_CURRENT_DESKTOP"), session.desktopNames()); env.insert(QStringLiteral("XDG_SESSION_CLASS"), QStringLiteral("user")); env.insert(QStringLiteral("XDG_SESSION_TYPE"), session.xdgSessionType()); env.insert(QStringLiteral("XDG_SEAT"), seat()->name()); + env.insert(QStringLiteral("XDG_VTNR"), QString::number(m_sessionTerminalId)); +#ifdef HAVE_SYSTEMD env.insert(QStringLiteral("XDG_SESSION_DESKTOP"), session.desktopNames()); +#endif - m_auth->insertEnvironment(env); - + if (session.xdgSessionType() == QLatin1String("x11")) { + if (m_displayServerType == X11DisplayServerType) + env.insert(QStringLiteral("DISPLAY"), name()); + else + m_auth->setDisplayServerCommand(XorgUserDisplayServer::command(this)); + } else { + m_auth->setDisplayServerCommand(QStringLiteral()); + } m_auth->setUser(user); if (m_reuseSessionId.isNull()) { m_auth->setSession(session.exec()); } + m_auth->insertEnvironment(env); m_auth->start(); } void Display::slotAuthenticationFinished(const QString &user, bool success) { if (success) { - qDebug() << "Authenticated successfully"; + qDebug() << "Authentication for user " << user << " successful"; if (!m_reuseSessionId.isNull()) { OrgFreedesktopLogin1ManagerInterface manager(Logind::serviceName(), Logind::managerPath(), QDBusConnection::systemBus()); manager.UnlockSession(m_reuseSessionId); manager.ActivateSession(m_reuseSessionId); + m_started = true; } else { - m_auth->setCookie(qobject_cast(m_displayServer)->cookie()); + if (qobject_cast(m_displayServer)) + m_auth->setCookie(qobject_cast(m_displayServer)->cookie()); } // save last user and last session @@ -361,25 +481,28 @@ if (m_socket) emit loginSucceeded(m_socket); } else if (m_socket) { - qDebug() << "Authentication failure"; + qDebug() << "Authentication for user " << user << " failed"; emit loginFailed(m_socket); } m_socket = nullptr; } void Display::slotAuthInfo(const QString &message, Auth::Info info) { - // TODO: presentable to the user, eventually - Q_UNUSED(info); - qWarning() << "Authentication information:" << message; + qWarning() << "Authentication information:" << info << message; + + if (!m_socket) + return; + + m_socketServer->informationMessage(m_socket, message); } void Display::slotAuthError(const QString &message, Auth::Error error) { - // TODO: handle more errors - qWarning() << "Authentication error:" << message; + qWarning() << "Authentication error:" << error << message; if (!m_socket) return; + m_socketServer->informationMessage(m_socket, message); if (error == Auth::ERROR_AUTHENTICATION) emit loginFailed(m_socket); } @@ -406,6 +529,9 @@ } void Display::slotSessionStarted(bool success) { - qDebug() << "Session started"; + qDebug() << "Session started" << success; + if (success) { + QTimer::singleShot(5000, m_greeter, &Greeter::stop); + } } } diff -Nru sddm-0.19.0/src/daemon/Display.h sddm-0.20.0/src/daemon/Display.h --- sddm-0.19.0/src/daemon/Display.h 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/src/daemon/Display.h 2023-06-23 12:28:38.000000000 +0000 @@ -23,6 +23,7 @@ #define SDDM_DISPLAY_H #include +#include #include #include "Auth.h" @@ -41,15 +42,26 @@ Q_OBJECT Q_DISABLE_COPY(Display) public: - explicit Display(int terminalId, Seat *parent); + enum DisplayServerType { + X11DisplayServerType, + X11UserDisplayServerType, + WaylandDisplayServerType + }; + Q_ENUM(DisplayServerType) + + static DisplayServerType defaultDisplayServerType(); + explicit Display(Seat *parent, DisplayServerType serverType); ~Display(); - QString displayId() const; - const int terminalId() const; + DisplayServerType displayServerType() const; + DisplayServer *displayServer() const; + + int terminalId() const; const QString &name() const; QString sessionType() const; + QString reuseSessionId() const { return m_reuseSessionId; } Seat *seat() const; @@ -65,23 +77,25 @@ signals: void stopped(); + void displayServerFailed(); void loginFailed(QLocalSocket *socket); void loginSucceeded(QLocalSocket *socket); private: QString findGreeterTheme() const; - bool findSessionEntry(const QDir &dir, const QString &name) const; + bool findSessionEntry(const QStringList &dirPaths, const QString &name) const; void startAuth(const QString &user, const QString &password, const Session &session); + DisplayServerType m_displayServerType = X11DisplayServerType; + bool m_relogin { true }; bool m_started { false }; - int m_terminalId { 7 }; - - Session m_lastSession; + int m_terminalId = 0; + int m_sessionTerminalId = 0; QString m_passPhrase; QString m_sessionName; @@ -91,7 +105,7 @@ DisplayServer *m_displayServer { nullptr }; Seat *m_seat { nullptr }; SocketServer *m_socketServer { nullptr }; - QLocalSocket *m_socket { nullptr }; + QPointer m_socket; Greeter *m_greeter { nullptr }; private slots: diff -Nru sddm-0.19.0/src/daemon/Greeter.cpp sddm-0.20.0/src/daemon/Greeter.cpp --- sddm-0.19.0/src/daemon/Greeter.cpp 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/src/daemon/Greeter.cpp 2023-06-23 12:28:38.000000000 +0000 @@ -27,12 +27,19 @@ #include "ThemeConfig.h" #include "ThemeMetadata.h" #include "Display.h" +#include "XorgDisplayServer.h" +#include "XorgUserDisplayServer.h" +#include "WaylandDisplayServer.h" #include #include +#include namespace SDDM { - Greeter::Greeter(QObject *parent) : QObject(parent) { + Greeter::Greeter(Display *parent) + : QObject(parent) + , m_display(parent) + { m_metadata = new ThemeMetadata(QString()); m_themeConfig = new ThemeConfig(QString()); } @@ -44,14 +51,6 @@ delete m_themeConfig; } - void Greeter::setDisplay(Display *display) { - m_display = display; - } - - void Greeter::setAuthPath(const QString &authPath) { - m_authPath = authPath; - } - void Greeter::setSocket(const QString &socket) { m_socket = socket; } @@ -71,6 +70,16 @@ } } + QString Greeter::displayServerCommand() const + { + return m_displayServerCmd; + } + + void Greeter::setDisplayServerCommand(const QString &cmd) + { + m_displayServerCmd = cmd; + } + bool Greeter::start() { // check flag if (m_started) @@ -80,6 +89,9 @@ QString xcursorTheme = mainConfig.Theme.CursorTheme.get(); if (m_themeConfig->contains(QLatin1String("cursorTheme"))) xcursorTheme = m_themeConfig->value(QLatin1String("cursorTheme")).toString(); + QString xcursorSize = mainConfig.Theme.CursorSize.get(); + if (m_themeConfig->contains(QLatin1String("cursorSize"))) + xcursorSize = m_themeConfig->value(QLatin1String("cursorSize")).toString(); QString platformTheme; if (m_themeConfig->contains(QLatin1String("platformTheme"))) platformTheme = m_themeConfig->value(QLatin1String("platformTheme")).toString(); @@ -98,6 +110,9 @@ if (!style.isEmpty()) args << QLatin1String("-style") << style; + Q_ASSERT(m_display); + auto *displayServer = m_display->displayServer(); + if (daemonApp->testing()) { // create process m_process = new QProcess(this); @@ -111,17 +126,19 @@ // log message qDebug() << "Greeter starting..."; - // set process environment - QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); - env.insert(QStringLiteral("DISPLAY"), m_display->name()); - env.insert(QStringLiteral("XAUTHORITY"), m_authPath); - env.insert(QStringLiteral("XCURSOR_THEME"), xcursorTheme); - env.insert(QStringLiteral("QT_IM_MODULE"), mainConfig.InputMethod.get()); - m_process->setProcessEnvironment(env); + args << QStringLiteral("--test-mode"); - // start greeter - if (daemonApp->testing()) - args << QStringLiteral("--test-mode"); + if (m_display->displayServerType() == Display::X11DisplayServerType) { + // set process environment + QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); + env.insert(QStringLiteral("DISPLAY"), m_display->name()); + env.insert(QStringLiteral("XAUTHORITY"), qobject_cast(displayServer)->authPath()); + env.insert(QStringLiteral("XCURSOR_THEME"), xcursorTheme); + if (!xcursorSize.isEmpty()) + env.insert(QStringLiteral("XCURSOR_SIZE"), xcursorSize); + m_process->setProcessEnvironment(env); + } + // Greeter command m_process->start(QStringLiteral("%1/sddm-greeter").arg(QStringLiteral(BIN_INSTALL_DIR)), args); //if we fail to start bail immediately, and don't block in waitForStarted @@ -149,11 +166,12 @@ m_auth->setVerbose(true); connect(m_auth, &Auth::requestChanged, this, &Greeter::onRequestChanged); connect(m_auth, &Auth::sessionStarted, this, &Greeter::onSessionStarted); + connect(m_auth, &Auth::displayServerReady, this, &Greeter::onDisplayServerReady); connect(m_auth, &Auth::finished, this, &Greeter::onHelperFinished); connect(m_auth, &Auth::info, this, &Greeter::authInfo); connect(m_auth, &Auth::error, this, &Greeter::authError); - // greeter command + // command QStringList cmd; cmd << QStringLiteral("%1/sddm-greeter").arg(QStringLiteral(BIN_INSTALL_DIR)) << args; @@ -173,9 +191,9 @@ }, sysenv, env); env.insert(QStringLiteral("PATH"), mainConfig.Users.DefaultPath.get()); - env.insert(QStringLiteral("DISPLAY"), m_display->name()); - env.insert(QStringLiteral("XAUTHORITY"), m_authPath); env.insert(QStringLiteral("XCURSOR_THEME"), xcursorTheme); + if (!xcursorSize.isEmpty()) + env.insert(QStringLiteral("XCURSOR_SIZE"), xcursorSize); env.insert(QStringLiteral("XDG_SEAT"), m_display->seat()->name()); env.insert(QStringLiteral("XDG_SEAT_PATH"), daemonApp->displayManager()->seatPath(m_display->seat()->name())); env.insert(QStringLiteral("XDG_SESSION_PATH"), daemonApp->displayManager()->sessionPath(QStringLiteral("Session%1").arg(daemonApp->newSessionId()))); @@ -183,11 +201,14 @@ env.insert(QStringLiteral("XDG_VTNR"), QString::number(m_display->terminalId())); env.insert(QStringLiteral("XDG_SESSION_CLASS"), QStringLiteral("greeter")); env.insert(QStringLiteral("XDG_SESSION_TYPE"), m_display->sessionType()); - env.insert(QStringLiteral("QT_IM_MODULE"), mainConfig.InputMethod.get()); - - //some themes may use KDE components and that will automatically load KDE's crash handler which we don't want - //counterintuitively setting this env disables that handler - env.insert(QStringLiteral("KDE_DEBUG"), QStringLiteral("1")); + if (m_display->displayServerType() == Display::X11DisplayServerType) { + env.insert(QStringLiteral("DISPLAY"), m_display->name()); + env.insert(QStringLiteral("QT_QPA_PLATFORM"), QStringLiteral("xcb")); + m_auth->setCookie(qobject_cast(displayServer)->cookie()); + } else if (m_display->displayServerType() == Display::WaylandDisplayServerType) { + env.insert(QStringLiteral("QT_QPA_PLATFORM"), QStringLiteral("wayland")); + env.insert(QStringLiteral("QT_WAYLAND_SHELL_INTEGRATION"), QStringLiteral("fullscreen-shell-v1")); + } m_auth->insertEnvironment(env); // log message @@ -195,6 +216,7 @@ // start greeter m_auth->setUser(QStringLiteral("sddm")); + m_auth->setDisplayServerCommand(m_displayServerCmd); m_auth->setGreeter(true); m_auth->setSession(cmd.join(QLatin1Char(' '))); m_auth->start(); @@ -225,6 +247,8 @@ // wait for finished if (!m_process->waitForFinished(5000)) m_process->kill(); + } else { + m_auth->stop(); } } @@ -240,8 +264,10 @@ qDebug() << "Greeter stopped."; // clean up - m_process->deleteLater(); - m_process = nullptr; + if (m_process) { + m_process->deleteLater(); + m_process = nullptr; + } } void Greeter::onRequestChanged() { @@ -259,29 +285,55 @@ qDebug() << "Greeter session failed to start"; } + void Greeter::onDisplayServerReady(const QString &displayName) + { + auto *displayServer = m_display->displayServer(); + + auto *xorgUser = qobject_cast(displayServer); + if (xorgUser) + xorgUser->setDisplayName(displayName); + + auto *wayland = qobject_cast(displayServer); + if (wayland) + wayland->setDisplayName(displayName); + } + void Greeter::onHelperFinished(Auth::HelperExitStatus status) { // reset flag m_started = false; // log message - qDebug() << "Greeter stopped."; + qDebug() << "Greeter stopped." << status; // clean up m_auth->deleteLater(); m_auth = nullptr; + + if (status == Auth::HELPER_DISPLAYSERVER_ERROR) { + Q_EMIT displayServerFailed(); + } else if (status == Auth::HELPER_TTY_ERROR) { + Q_EMIT ttyFailed(); + } else if (status == Auth::HELPER_SESSION_ERROR) { + Q_EMIT failed(); + } + } + + bool Greeter::isRunning() const { + return (m_process && m_process->state() == QProcess::Running) + || (m_auth && m_auth->isActive()); } void Greeter::onReadyReadStandardError() { if (m_process) { - qDebug() << "Greeter errors:" << qPrintable(QString::fromLocal8Bit(m_process->readAllStandardError())); + qDebug() << "Greeter errors:" << m_process->readAllStandardError().constData(); } } void Greeter::onReadyReadStandardOutput() { if (m_process) { - qDebug() << "Greeter output:" << qPrintable(QString::fromLocal8Bit(m_process->readAllStandardOutput())); + qDebug() << "Greeter output:" << m_process->readAllStandardOutput().constData(); } } diff -Nru sddm-0.19.0/src/daemon/Greeter.h sddm-0.20.0/src/daemon/Greeter.h --- sddm-0.19.0/src/daemon/Greeter.h 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/src/daemon/Greeter.h 2023-06-23 12:28:38.000000000 +0000 @@ -35,14 +35,16 @@ Q_OBJECT Q_DISABLE_COPY(Greeter) public: - explicit Greeter(QObject *parent = 0); + explicit Greeter(Display *parent = 0); ~Greeter(); - void setDisplay(Display *display); - void setAuthPath(const QString &authPath); void setSocket(const QString &socket); void setTheme(const QString &theme); + QString displayServerCommand() const; + void setDisplayServerCommand(const QString &cmd); + bool isRunning() const; + public slots: bool start(); void stop(); @@ -51,19 +53,25 @@ private slots: void onRequestChanged(); void onSessionStarted(bool success); + void onDisplayServerReady(const QString &displayName); void onHelperFinished(Auth::HelperExitStatus status); void onReadyReadStandardOutput(); void onReadyReadStandardError(); void authInfo(const QString &message, Auth::Info info); void authError(const QString &message, Auth::Error error); + signals: + void ttyFailed(); + void failed(); + void displayServerFailed(); + private: bool m_started { false }; - Display *m_display { nullptr }; - QString m_authPath; + Display * const m_display { nullptr }; QString m_socket; QString m_themePath; + QString m_displayServerCmd; ThemeMetadata *m_metadata { nullptr }; ThemeConfig *m_themeConfig { nullptr }; diff -Nru sddm-0.19.0/src/daemon/PowerManager.cpp sddm-0.20.0/src/daemon/PowerManager.cpp --- sddm-0.19.0/src/daemon/PowerManager.cpp 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/src/daemon/PowerManager.cpp 2023-06-23 12:28:38.000000000 +0000 @@ -87,11 +87,15 @@ } void powerOff() const { - QProcess::execute(mainConfig.HaltCommand.get()); + auto command = QProcess::splitCommand(mainConfig.HaltCommand.get()); + const QString program = command.takeFirst(); + QProcess::execute(program, command); } void reboot() const { - QProcess::execute(mainConfig.RebootCommand.get()); + auto command = QProcess::splitCommand(mainConfig.RebootCommand.get()); + const QString program = command.takeFirst(); + QProcess::execute(program, command); } void suspend() const { diff -Nru sddm-0.19.0/src/daemon/Seat.cpp sddm-0.20.0/src/daemon/Seat.cpp --- sddm-0.19.0/src/daemon/Seat.cpp 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/src/daemon/Seat.cpp 2023-06-23 12:28:38.000000000 +0000 @@ -28,80 +28,78 @@ #include #include +#include #include +#include +#include +#include namespace SDDM { - int findUnused(int minimum, std::function used) { - // initialize with minimum - int number = minimum; - - // find unused - while (used(number)) - number++; - - // return number; - return number; - } - Seat::Seat(const QString &name, QObject *parent) : QObject(parent), m_name(name) { - createDisplay(); + createDisplay(Display::defaultDisplayServerType()); } const QString &Seat::name() const { return m_name; } - bool Seat::createDisplay(int terminalId) { + void Seat::createDisplay(Display::DisplayServerType serverType) { //reload config if needed mainConfig.load(); - if (m_name == QLatin1String("seat0")) { - if (terminalId == -1) { - // find unused terminal - terminalId = findUnused(mainConfig.X11.MinimumVT.get(), [&](const int number) { - return m_terminalIds.contains(number); - }); - } - - // mark terminal as used - m_terminalIds << terminalId; - - // log message - qDebug() << "Adding new display" << "on vt" << terminalId << "..."; - } - else { - qDebug() << "Adding new VT-less display..."; - } - // create a new display - Display *display = new Display(terminalId, this); + qDebug() << "Adding new display..."; + Display *display = new Display(this, serverType); // restart display on stop connect(display, &Display::stopped, this, &Seat::displayStopped); + connect(display, &Display::displayServerFailed, this, [this, display] { + removeDisplay(display); + + // If we failed to create a display with wayland or rootful x11, try with + // x11-user. There's a chance it might work. It's a handy fallback + // since the alternative is a black screen + if (display->displayServerType() != Display::X11UserDisplayServerType) { + qWarning() << "Failed to launch the display server, falling back to DisplayServer=x11-user"; + createDisplay(Display::X11UserDisplayServerType); + } else if (m_displays.isEmpty()) { + qWarning() << "Failed to launch a DisplayServer=x11-user session, aborting"; + QCoreApplication::instance()->exit(12); + } + }); // add display to the list m_displays << display; // start the display - if (!display->start()) { - qCritical() << "Could not start Display server on vt" << terminalId; - return false; + startDisplay(display); + } + + void Seat::startDisplay(Display *display, int tryNr) { + if (display->start()) + return; + + // It's possible that the system isn't ready yet (driver not loaded, + // device not enumerated, ...). It's not possible to tell when that changes, + // so try a few times with a delay in between. + qWarning() << "Attempt" << tryNr << "starting the Display server on vt" << display->terminalId() << "failed"; + + if(tryNr >= 3) { + qCritical() << "Could not start Display server on vt" << display->terminalId(); + return; } - return true; + QTimer::singleShot(2000, display, [=] { startDisplay(display, tryNr + 1); }); } void Seat::removeDisplay(Display* display) { - qDebug() << "Removing display" << display->displayId() << "..."; + qDebug() << "Removing display" << display << "..."; // remove display from list m_displays.removeAll(display); - // mark display and terminal ids as unused - m_terminalIds.removeAll(display->terminalId()); - // stop the display display->blockSignals(true); display->stop(); @@ -113,22 +111,34 @@ void Seat::displayStopped() { Display *display = qobject_cast(sender()); + OrgFreedesktopLogin1ManagerInterface manager(Logind::serviceName(), Logind::managerPath(), QDBusConnection::systemBus()); + std::optional nextVt; + auto reusing = display->reuseSessionId(); + if (manager.isValid() && !reusing.isEmpty()) { + auto sessionPath = manager.GetSession(reusing); + OrgFreedesktopLogin1SessionInterface sessionIface(Logind::serviceName(), sessionPath.value().path(), QDBusConnection::systemBus()); + nextVt = QStringView(sessionIface.tTY()).mid(3).toInt(); // we need to convert ttyN to N + } // remove display removeDisplay(display); // restart otherwise if (m_displays.isEmpty()) { - createDisplay(); + createDisplay(Display::defaultDisplayServerType()); } // If there is still a session running on some display, // switch to last display in display vector. // Set vt_auto to true, so let the kernel handle the // vt switch automatically (VT_AUTO). - else { + else if (!nextVt) { int disp = m_displays.last()->terminalId(); if (disp != -1) - VirtualTerminal::jumpToVt(disp, true); + nextVt = disp; + } + + if (nextVt) { + VirtualTerminal::jumpToVt(*nextVt, true); } } } diff -Nru sddm-0.19.0/src/daemon/Seat.h sddm-0.20.0/src/daemon/Seat.h --- sddm-0.19.0/src/daemon/Seat.h 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/src/daemon/Seat.h 2023-06-23 12:28:38.000000000 +0000 @@ -22,6 +22,7 @@ #include #include +#include "Display.h" namespace SDDM { class Display; @@ -33,19 +34,20 @@ explicit Seat(const QString &name, QObject *parent = 0); const QString &name() const; + void createDisplay(Display::DisplayServerType serverType); public slots: - bool createDisplay(int terminalId = -1); void removeDisplay(SDDM::Display* display); private slots: void displayStopped(); private: + void startDisplay(SDDM::Display *display, int tryNr = 1); + QString m_name; QVector m_displays; - QVector m_terminalIds; }; } diff -Nru sddm-0.19.0/src/daemon/SeatManager.cpp sddm-0.20.0/src/daemon/SeatManager.cpp --- sddm-0.19.0/src/daemon/SeatManager.cpp 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/src/daemon/SeatManager.cpp 2023-06-23 12:28:38.000000000 +0000 @@ -34,7 +34,7 @@ class LogindSeat : public QObject { Q_OBJECT public: - LogindSeat(const QString &name, const QDBusObjectPath &objectPath, QObject *parent); + LogindSeat(const QString &name, const QDBusObjectPath &objectPath); QString name() const; bool canGraphical() const; Q_SIGNALS: @@ -46,7 +46,7 @@ bool m_canGraphical; }; - LogindSeat::LogindSeat(const QString& name, const QDBusObjectPath& objectPath, QObject* parent): + LogindSeat::LogindSeat(const QString& name, const QDBusObjectPath& objectPath): m_name(name), m_canGraphical(false) { @@ -149,12 +149,12 @@ return; // switch to greeter - m_seats.value(name)->createDisplay(); + m_seats.value(name)->createDisplay(Display::defaultDisplayServerType()); } void SDDM::SeatManager::logindSeatAdded(const QString& name, const QDBusObjectPath& objectPath) { - auto logindSeat = new LogindSeat(name, objectPath, this); + auto logindSeat = new LogindSeat(name, objectPath); connect(logindSeat, &LogindSeat::canGraphicalChanged, this, [=]() { if (logindSeat->canGraphical()) { createSeat(logindSeat->name()); diff -Nru sddm-0.19.0/src/daemon/SignalHandler.cpp sddm-0.20.0/src/daemon/SignalHandler.cpp --- sddm-0.19.0/src/daemon/SignalHandler.cpp 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/src/daemon/SignalHandler.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,237 +0,0 @@ -/*************************************************************************** -* Copyright (c) 2013 Abdurrahman AVCI -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation; either version 2 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the -* Free Software Foundation, Inc., -* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -***************************************************************************/ - -#include "SignalHandler.h" - -#include -#include - -#include -#include - -#include - -namespace SDDM { - int sighupFd[2]; - int sigintFd[2]; - int sigtermFd[2]; - int sigusr1Fd[2]; - - SignalHandler::SignalHandler(QObject *parent) : QObject(parent) { - if (::socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sighupFd)) - qCritical() << "Failed to create socket pair for SIGHUP handling."; - - snhup = new QSocketNotifier(sighupFd[1], QSocketNotifier::Read, this); - connect(snhup, &QSocketNotifier::activated, this, &SignalHandler::handleSighup); - - if (::socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sigintFd)) - qCritical() << "Failed to create socket pair for SIGINT handling."; - - snint = new QSocketNotifier(sigintFd[1], QSocketNotifier::Read, this); - connect(snint, &QSocketNotifier::activated, this, &SignalHandler::handleSigint); - - if (::socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sigtermFd)) - qCritical() << "Failed to create socket pair for SIGTERM handling."; - - snterm = new QSocketNotifier(sigtermFd[1], QSocketNotifier::Read, this); - connect(snterm, &QSocketNotifier::activated, this, &SignalHandler::handleSigterm); - - if (::socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sigusr1Fd)) - qCritical() << "Failed to create socket pair for SIGUSR1 handling."; - - snusr1 = new QSocketNotifier(sigusr1Fd[1], QSocketNotifier::Read, this); - connect(snusr1, &QSocketNotifier::activated, this, &SignalHandler::handleSigusr1); - } - - void SignalHandler::initialize() { - struct sigaction sighup = { }; - sighup.sa_handler = SignalHandler::hupSignalHandler; - sigemptyset(&sighup.sa_mask); - sighup.sa_flags = SA_RESTART; - - if (sigaction(SIGHUP, &sighup, 0) > 0) { - qCritical() << "Failed to setup SIGHUP handler."; - return; - } - - struct sigaction sigint = { }; - sigint.sa_handler = SignalHandler::intSignalHandler; - sigemptyset(&sigint.sa_mask); - sigint.sa_flags = SA_RESTART; - - if (sigaction(SIGINT, &sigint, 0) > 0) { - qCritical() << "Failed to set up SIGINT handler."; - return; - } - - struct sigaction sigterm = { }; - sigterm.sa_handler = SignalHandler::termSignalHandler; - sigemptyset(&sigterm.sa_mask); - sigterm.sa_flags = SA_RESTART; - - if (sigaction(SIGTERM, &sigterm, 0) > 0) { - qCritical() << "Failed to set up SIGTERM handler."; - return; - } - } - - void SignalHandler::initializeSigusr1() { - struct sigaction sigusr1 = { }; - sigusr1.sa_handler = SignalHandler::usr1SignalHandler; - sigemptyset(&sigusr1.sa_mask); - sigusr1.sa_flags = SA_RESTART; - - if (sigaction(SIGUSR1, &sigusr1, 0) > 0) { - qCritical() << "Failed to set up SIGUSR1 handler."; - return; - } - } - - void SignalHandler::ignoreSigusr1() { - struct sigaction sigusr1 = { }; - sigusr1.sa_handler = SIG_IGN; - sigemptyset(&sigusr1.sa_mask); - sigusr1.sa_flags = SA_RESTART; - - if (sigaction(SIGUSR1, &sigusr1, 0) > 0) { - qCritical() << "Failed to set up SIGUSR1 handler."; - return; - } - } - - void SignalHandler::hupSignalHandler(int) { - char a = 1; - if (::write(sighupFd[0], &a, sizeof(a)) == -1) { - qCritical() << "Error writing to the SIGHUP handler"; - return; - } - } - - void SignalHandler::intSignalHandler(int) { - char a = 1; - if (::write(sigintFd[0], &a, sizeof(a)) == -1) { - qCritical() << "Error writing to the SIGINT handler"; - return; - } - } - - void SignalHandler::termSignalHandler(int) { - char a = 1; - if (::write(sigtermFd[0], &a, sizeof(a)) == -1) { - qCritical() << "Error writing to the SIGTERM handler"; - return; - } - } - - void SignalHandler::usr1SignalHandler(int) { - char a = 1; - if (::write(sigusr1Fd[0], &a, sizeof(a)) == -1) { - qCritical() << "Error writing to the SIGUSR1 handler"; - return; - } - } - - void SignalHandler::handleSighup() { - // disable notifier - snhup->setEnabled(false); - - // read from socket - char a; - if (::read(sighupFd[1], &a, sizeof(a)) == -1) { - // something went wrong! - qCritical() << "Error reading from the socket"; - return; - } - - // log event - qWarning() << "Signal received: SIGHUP"; - - // emit signal - emit sighupReceived(); - - // enable notifier - snhup->setEnabled(true); - } - - void SignalHandler::handleSigint() { - // disable notifier - snint->setEnabled(false); - - // read from socket - char a; - if (::read(sigintFd[1], &a, sizeof(a)) == -1) { - // something went wrong! - qCritical() << "Error reading from the socket"; - return; - } - - // log event - qWarning() << "Signal received: SIGINT"; - - // emit signal - emit sigintReceived(); - - // enable notifier - snint->setEnabled(true); - } - - void SignalHandler::handleSigterm() { - // disable notifier - snterm->setEnabled(false); - - // read from socket - char a; - if (::read(sigtermFd[1], &a, sizeof(a)) == -1) { - // something went wrong! - qCritical() << "Error reading from the socket"; - return; - } - - // log event - qWarning() << "Signal received: SIGTERM"; - - // emit signal - emit sigtermReceived(); - - // enable notifier - snterm->setEnabled(true); - } - - void SignalHandler::handleSigusr1() { - // disable notifier - snusr1->setEnabled(false); - - // read from socket - char a; - if (::read(sigusr1Fd[1], &a, sizeof(a)) == -1) { - // something went wrong! - qCritical() << "Error reading from the socket"; - return; - } - - // log event - qWarning() << "Signal received: SIGUSR1"; - - // emit signal - emit sigusr1Received(); - - // enable notifier - snusr1->setEnabled(true); - } -} diff -Nru sddm-0.19.0/src/daemon/SignalHandler.h sddm-0.20.0/src/daemon/SignalHandler.h --- sddm-0.19.0/src/daemon/SignalHandler.h 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/src/daemon/SignalHandler.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,61 +0,0 @@ -/*************************************************************************** -* Copyright (c) 2013 Abdurrahman AVCI -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation; either version 2 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the -* Free Software Foundation, Inc., -* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -***************************************************************************/ - -#ifndef SDDM_SIGNALHANDLER_H -#define SDDM_SIGNALHANDLER_H - -#include - -class QSocketNotifier; - -namespace SDDM { - class SignalHandler : public QObject { - Q_OBJECT - Q_DISABLE_COPY(SignalHandler) - public: - SignalHandler(QObject *parent = 0); - - static void initialize(); - static void initializeSigusr1(); - static void ignoreSigusr1(); - static void hupSignalHandler(int unused); - static void intSignalHandler(int unused); - static void termSignalHandler(int unused); - static void usr1SignalHandler(int unused); - - signals: - void sighupReceived(); - void sigintReceived(); - void sigtermReceived(); - void sigusr1Received(); - - private slots: - void handleSighup(); - void handleSigint(); - void handleSigterm(); - void handleSigusr1(); - - private: - QSocketNotifier *snhup { nullptr }; - QSocketNotifier *snint { nullptr }; - QSocketNotifier *snterm { nullptr }; - QSocketNotifier *snusr1 { nullptr }; - }; -} -#endif // SDDM_SIGNALHANDLER_H diff -Nru sddm-0.19.0/src/daemon/SocketServer.cpp sddm-0.20.0/src/daemon/SocketServer.cpp --- sddm-0.19.0/src/daemon/SocketServer.cpp 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/src/daemon/SocketServer.cpp 2023-06-23 12:28:38.000000000 +0000 @@ -109,83 +109,87 @@ // input stream QDataStream input(socket); - // read message - quint32 message; - input >> message; - - switch (GreeterMessages(message)) { - case GreeterMessages::Connect: { - // log message - qDebug() << "Message received from greeter: Connect"; - - // send capabilities - SocketWriter(socket) << quint32(DaemonMessages::Capabilities) << quint32(daemonApp->powerManager()->capabilities()); - - // send host name - SocketWriter(socket) << quint32(DaemonMessages::HostName) << daemonApp->hostName(); - - // emit signal - emit connected(); - } - break; - case GreeterMessages::Login: { - // log message - qDebug() << "Message received from greeter: Login"; - - // read username, pasword etc. - QString user, password, filename; - Session session; - input >> user >> password >> session; - - // emit signal - emit login(socket, user, password, session); - } - break; - case GreeterMessages::PowerOff: { - // log message - qDebug() << "Message received from greeter: PowerOff"; - - // power off - daemonApp->powerManager()->powerOff(); - } - break; - case GreeterMessages::Reboot: { - // log message - qDebug() << "Message received from greeter: Reboot"; - - // reboot - daemonApp->powerManager()->reboot(); - } - break; - case GreeterMessages::Suspend: { - // log message - qDebug() << "Message received from greeter: Suspend"; - - // suspend - daemonApp->powerManager()->suspend(); - } - break; - case GreeterMessages::Hibernate: { - // log message - qDebug() << "Message received from greeter: Hibernate"; - - // hibernate - daemonApp->powerManager()->hibernate(); - } - break; - case GreeterMessages::HybridSleep: { - // log message - qDebug() << "Message received from greeter: HybridSleep"; - - // hybrid sleep - daemonApp->powerManager()->hybridSleep(); - } - break; - default: { - // log message - qWarning() << "Unknown message" << message; + // Qt's QLocalSocket::readyRead is not designed to be called at every socket.write(), + // so we need to use a loop to read all the signals. + while(socket->bytesAvailable()) { + // read message + quint32 message; + input >> message; + + switch (GreeterMessages(message)) { + case GreeterMessages::Connect: { + // log message + qDebug() << "Message received from greeter: Connect"; + + // send capabilities + SocketWriter(socket) << quint32(DaemonMessages::Capabilities) << quint32(daemonApp->powerManager()->capabilities()); + + // send host name + SocketWriter(socket) << quint32(DaemonMessages::HostName) << daemonApp->hostName(); + + // emit signal + emit connected(); + } + break; + case GreeterMessages::Login: { + // log message + qDebug() << "Message received from greeter: Login"; + + // read username, pasword etc. + QString user, password, filename; + Session session; + input >> user >> password >> session; + + // emit signal + emit login(socket, user, password, session); + } + break; + case GreeterMessages::PowerOff: { + // log message + qDebug() << "Message received from greeter: PowerOff"; + + // power off + daemonApp->powerManager()->powerOff(); + } + break; + case GreeterMessages::Reboot: { + // log message + qDebug() << "Message received from greeter: Reboot"; + + // reboot + daemonApp->powerManager()->reboot(); + } + break; + case GreeterMessages::Suspend: { + // log message + qDebug() << "Message received from greeter: Suspend"; + + // suspend + daemonApp->powerManager()->suspend(); + } + break; + case GreeterMessages::Hibernate: { + // log message + qDebug() << "Message received from greeter: Hibernate"; + + // hibernate + daemonApp->powerManager()->hibernate(); + } + break; + case GreeterMessages::HybridSleep: { + // log message + qDebug() << "Message received from greeter: HybridSleep"; + // hybrid sleep + daemonApp->powerManager()->hybridSleep(); + } + break; + default: { + // log message + qWarning() << "Unknown message" << message; + } } } + } void SocketServer::loginFailed(QLocalSocket *socket) { @@ -195,4 +199,8 @@ void SocketServer::loginSucceeded(QLocalSocket *socket) { SocketWriter(socket) << quint32(DaemonMessages::LoginSucceeded); } + + void SocketServer::informationMessage(QLocalSocket *socket, const QString &message) { + SocketWriter(socket) << quint32(DaemonMessages::InformationMessage) << message; + } } diff -Nru sddm-0.19.0/src/daemon/SocketServer.h sddm-0.20.0/src/daemon/SocketServer.h --- sddm-0.19.0/src/daemon/SocketServer.h 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/src/daemon/SocketServer.h 2023-06-23 12:28:38.000000000 +0000 @@ -45,6 +45,8 @@ void newConnection(); void readyRead(); + public slots: + void informationMessage(QLocalSocket *socket, const QString &message); void loginFailed(QLocalSocket *socket); void loginSucceeded(QLocalSocket *socket); diff -Nru sddm-0.19.0/src/daemon/Utils.h sddm-0.20.0/src/daemon/Utils.h --- sddm-0.19.0/src/daemon/Utils.h 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/src/daemon/Utils.h 2023-06-23 12:28:38.000000000 +0000 @@ -26,11 +26,11 @@ namespace SDDM { inline QString generateName(int length) { - QString digits = QStringLiteral("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); + const QString digits = QStringLiteral("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); // reserve space for name QString name; - name.reserve(length); + name.resize(length); // create random device std::random_device rd; diff -Nru sddm-0.19.0/src/daemon/WaylandDisplayServer.cpp sddm-0.20.0/src/daemon/WaylandDisplayServer.cpp --- sddm-0.19.0/src/daemon/WaylandDisplayServer.cpp 1970-01-01 00:00:00.000000000 +0000 +++ sddm-0.20.0/src/daemon/WaylandDisplayServer.cpp 2023-06-23 12:28:38.000000000 +0000 @@ -0,0 +1,76 @@ +/*************************************************************************** +* Copyright (c) 2021 Pier Luigi Fiorini +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the +* Free Software Foundation, Inc., +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +***************************************************************************/ + +#include "WaylandDisplayServer.h" + +namespace SDDM { + +WaylandDisplayServer::WaylandDisplayServer(Display *parent) + : DisplayServer(parent) +{ +} + +WaylandDisplayServer::~WaylandDisplayServer() +{ + stop(); +} + +QString WaylandDisplayServer::sessionType() const +{ + return QStringLiteral("wayland"); +} + +void WaylandDisplayServer::setDisplayName(const QString &displayName) +{ + m_display = displayName; +} + +bool WaylandDisplayServer::start() +{ + // Check flag + if (m_started) + return false; + + // Set flag + m_started = true; + emit started(); + + return true; +} + +void WaylandDisplayServer::stop() +{ + // Check flag + if (!m_started) + return; + + // Reset flag + m_started = false; + emit stopped(); +} + +void WaylandDisplayServer::finished() +{ +} + +void WaylandDisplayServer::setupDisplay() +{ +} + +} // namespace SDDM diff -Nru sddm-0.19.0/src/daemon/WaylandDisplayServer.h sddm-0.20.0/src/daemon/WaylandDisplayServer.h --- sddm-0.19.0/src/daemon/WaylandDisplayServer.h 1970-01-01 00:00:00.000000000 +0000 +++ sddm-0.20.0/src/daemon/WaylandDisplayServer.h 2023-06-23 12:28:38.000000000 +0000 @@ -0,0 +1,48 @@ +/*************************************************************************** +* Copyright (c) 2021 Pier Luigi Fiorini +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the +* Free Software Foundation, Inc., +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +***************************************************************************/ + +#ifndef SDDM_WAYLANDDISPLAYSERVER_H +#define SDDM_WAYLANDDISPLAYSERVER_H + +#include "DisplayServer.h" + +namespace SDDM { + +class WaylandDisplayServer : public DisplayServer +{ + Q_OBJECT + Q_DISABLE_COPY(WaylandDisplayServer) +public: + explicit WaylandDisplayServer(Display *parent); + ~WaylandDisplayServer(); + + QString sessionType() const; + + void setDisplayName(const QString &displayName); + +public Q_SLOTS: + bool start(); + void stop(); + void finished(); + void setupDisplay(); +}; + +} // namespace SDDM + +#endif // SDDM_WAYLANDDISPLAYSERVER_H diff -Nru sddm-0.19.0/src/daemon/XorgDisplayServer.cpp sddm-0.20.0/src/daemon/XorgDisplayServer.cpp --- sddm-0.19.0/src/daemon/XorgDisplayServer.cpp 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/src/daemon/XorgDisplayServer.cpp 2023-06-23 12:28:38.000000000 +0000 @@ -23,7 +23,6 @@ #include "Configuration.h" #include "DaemonApp.h" #include "Display.h" -#include "SignalHandler.h" #include "Seat.h" #include @@ -41,31 +40,9 @@ namespace SDDM { XorgDisplayServer::XorgDisplayServer(Display *parent) : DisplayServer(parent) { - // get auth directory - QString authDir = QStringLiteral(RUNTIME_DIR); - - // use "." as authdir in test mode if (daemonApp->testing()) - authDir = QStringLiteral("."); - - // create auth dir if not existing - QDir().mkpath(authDir); - - // set auth path - m_authPath = QStringLiteral("%1/%2").arg(authDir).arg(QUuid::createUuid().toString()); - - // generate cookie - std::random_device rd; - std::mt19937 gen(rd()); - std::uniform_int_distribution<> dis(0, 15); - - // resever 32 bytes - m_cookie.reserve(32); - - // create a random hexadecimal number - const char *digits = "0123456789abcdef"; - for (int i = 0; i < 32; ++i) - m_cookie[i] = digits[dis(gen)]; + m_xauth.setAuthDirectory(QStringLiteral(".")); + m_xauth.setup(); } XorgDisplayServer::~XorgDisplayServer() { @@ -76,41 +53,16 @@ return m_display; } - const QString &XorgDisplayServer::authPath() const { - return m_authPath; + QString XorgDisplayServer::authPath() const { + return m_xauth.authPath(); } QString XorgDisplayServer::sessionType() const { return QStringLiteral("x11"); } - const QString &XorgDisplayServer::cookie() const { - return m_cookie; - } - - bool XorgDisplayServer::addCookie(const QString &file) { - // log message - qDebug() << "Adding cookie to" << file; - - // Touch file - QFile file_handler(file); - file_handler.open(QIODevice::Append); - file_handler.close(); - - QString cmd = QStringLiteral("%1 -f %2 -q").arg(mainConfig.X11.XauthPath.get()).arg(file); - - // execute xauth - FILE *fp = popen(qPrintable(cmd), "w"); - - // check file - if (!fp) - return false; - fprintf(fp, "remove %s\n", qPrintable(m_display)); - fprintf(fp, "add %s . %s\n", qPrintable(m_display), qPrintable(m_cookie)); - fprintf(fp, "exit\n"); - - // close pipe - return pclose(fp) == 0; + const QByteArray XorgDisplayServer::cookie() const { + return m_xauth.cookie(); } bool XorgDisplayServer::start() { @@ -118,6 +70,11 @@ if (m_started) return false; + if (process) { + qCritical() << "Tried to start Xorg before previous instance exited"; + return false; + } + // create process process = new QProcess(this); @@ -131,110 +88,101 @@ // For the X server's copy, the display number doesn't matter. // An empty file would result in no access control! m_display = QStringLiteral(":0"); - if(!addCookie(m_authPath)) { + if(!m_xauth.addCookie(m_display)) { qCritical() << "Failed to write xauth file"; return false; } - if (daemonApp->testing()) { - QStringList args; - QDir x11socketDir(QStringLiteral("/tmp/.X11-unix")); - int display = 100; - while (x11socketDir.exists(QStringLiteral("X%1").arg(display))) { - ++display; - } - m_display = QStringLiteral(":%1").arg(display); - args << m_display << QStringLiteral("-auth") << m_authPath << QStringLiteral("-br") << QStringLiteral("-noreset") << QStringLiteral("-screen") << QStringLiteral("800x600"); - process->start(mainConfig.X11.XephyrPath.get(), args); - - - // wait for display server to start - if (!process->waitForStarted()) { - // log message - qCritical() << "Failed to start display server process."; - - // return fail - return false; - } - emit started(); - } else { - // set process environment - QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); - env.insert(QStringLiteral("XCURSOR_THEME"), mainConfig.Theme.CursorTheme.get()); - process->setProcessEnvironment(env); - - //create pipe for communicating with X server - //0 == read from X, 1== write to from X - int pipeFds[2]; - if (pipe(pipeFds) != 0) { - qCritical("Could not create pipe to start X server"); - } + // set process environment + QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); + env.insert(QStringLiteral("XCURSOR_THEME"), mainConfig.Theme.CursorTheme.get()); + QString xcursorSize = mainConfig.Theme.CursorSize.get(); + if (!xcursorSize.isEmpty()) + env.insert(QStringLiteral("XCURSOR_SIZE"), xcursorSize); + process->setProcessEnvironment(env); + + //create pipe for communicating with X server + //0 == read from X, 1== write to from X + int pipeFds[2]; + if (pipe(pipeFds) != 0) { + qCritical("Could not create pipe to start X server"); + } - // start display server - QStringList args = mainConfig.X11.ServerArguments.get().split(QLatin1Char(' '), QString::SkipEmptyParts); - args << QStringLiteral("-auth") << m_authPath + // start display server + QStringList args; + if (!daemonApp->testing()) { + process->setProgram(mainConfig.X11.ServerPath.get()); + args << mainConfig.X11.ServerArguments.get().split(QLatin1Char(' '), Qt::SkipEmptyParts) << QStringLiteral("-background") << QStringLiteral("none") - << QStringLiteral("-noreset") - << QStringLiteral("-displayfd") << QString::number(pipeFds[1]) - << QStringLiteral("-seat") << displayPtr()->seat()->name(); - - if (displayPtr()->seat()->name() == QLatin1String("seat0")) { - args << QStringLiteral("vt%1").arg(displayPtr()->terminalId()); - } - qDebug() << "Running:" - << qPrintable(mainConfig.X11.ServerPath.get()) - << qPrintable(args.join(QLatin1Char(' '))); - process->start(mainConfig.X11.ServerPath.get(), args); - - // wait for display server to start - if (!process->waitForStarted()) { - // log message - qCritical() << "Failed to start display server process."; - - // return fail - close(pipeFds[0]); - return false; - } + << QStringLiteral("-seat") << displayPtr()->seat()->name() + << QStringLiteral("vt%1").arg(displayPtr()->terminalId()); + } else { + process->setProgram(mainConfig.X11.XephyrPath.get()); + args << QStringLiteral("-br") + << QStringLiteral("-screen") << QStringLiteral("800x600"); + } - // close the other side of pipe in our process, otherwise reading - // from it may stuck even X server exit. - close(pipeFds[1]); + args << QStringLiteral("-auth") << m_xauth.authPath() + << QStringLiteral("-noreset") + << QStringLiteral("-displayfd") << QString::number(pipeFds[1]); + + process->setArguments(args); + qDebug() << "Running:" + << qPrintable(process->program()) + << qPrintable(process->arguments().join(QLatin1Char(' '))); + process->start(); + + // wait for display server to start + if (!process->waitForStarted()) { + // log message + qCritical() << "Failed to start display server process."; - QFile readPipe; + // return fail + close(pipeFds[0]); + return false; + } - if (!readPipe.open(pipeFds[0], QIODevice::ReadOnly)) { - qCritical("Failed to open pipe to start X Server"); + // close the other side of pipe in our process, otherwise reading + // from it may stuck even X server exit. + close(pipeFds[1]); - close(pipeFds[0]); - return false; - } - QByteArray displayNumber = readPipe.readLine(); - if (displayNumber.size() < 2) { - // X server gave nothing (or a whitespace). - qCritical("Failed to read display number from pipe"); + QFile readPipe; - close(pipeFds[0]); - return false; - } - displayNumber.prepend(QByteArray(":")); - displayNumber.remove(displayNumber.size() -1, 1); // trim trailing whitespace - m_display = QString::fromLocal8Bit(displayNumber); + if (!readPipe.open(pipeFds[0], QIODevice::ReadOnly)) { + qCritical("Failed to open pipe to start X Server"); - // close our pipe close(pipeFds[0]); + stop(); + return false; + } + QByteArray displayNumber = readPipe.readLine(); + if (displayNumber.size() < 2) { + // X server gave nothing (or a whitespace). + qCritical("Failed to read display number from pipe"); - emit started(); + close(pipeFds[0]); + stop(); + return false; } + displayNumber.prepend(QByteArray(":")); + displayNumber.remove(displayNumber.size() -1, 1); // trim trailing whitespace + m_display = QString::fromLocal8Bit(displayNumber); + + // close our pipe + close(pipeFds[0]); // The file is also used by the greeter, which does care about the // display number. Write the proper entry, if it's different. if(m_display != QStringLiteral(":0")) { - if(!addCookie(m_authPath)) { + if(!m_xauth.addCookie(m_display)) { qCritical() << "Failed to write xauth file"; + stop(); return false; } } - changeOwner(m_authPath); + changeOwner(m_xauth.authPath()); + + emit started(); // set flag m_started = true; @@ -244,8 +192,7 @@ } void XorgDisplayServer::stop() { - // check flag - if (!m_started) + if (!process) return; // log message @@ -255,11 +202,19 @@ process->terminate(); // wait for finished - if (!process->waitForFinished(5000)) + if (!process->waitForFinished(5000)) { process->kill(); + process->waitForFinished(25000); + } } void XorgDisplayServer::finished() { + // clean up + if (process) { + process->deleteLater(); + process = nullptr; + } + // check flag if (!m_started) return; @@ -270,7 +225,7 @@ // log message qDebug() << "Display server stopped."; - QString displayStopCommand = mainConfig.X11.DisplayStopCommand.get(); + QStringList displayStopCommand = QProcess::splitCommand(mainConfig.X11.DisplayStopCommand.get()); // create display setup script process QProcess *displayStopScript = new QProcess(); @@ -285,7 +240,8 @@ // start display stop script qDebug() << "Running display stop script " << displayStopCommand; - displayStopScript->start(displayStopCommand); + const auto program = displayStopCommand.takeFirst(); + displayStopScript->start(program, displayStopCommand); // wait for finished if (!displayStopScript->waitForFinished(5000)) @@ -295,20 +251,11 @@ displayStopScript->deleteLater(); displayStopScript = nullptr; - // clean up - process->deleteLater(); - process = nullptr; - - // remove authority file - QFile::remove(m_authPath); - // emit signal emit stopped(); } void XorgDisplayServer::setupDisplay() { - QString displayCommand = mainConfig.X11.DisplayCommand.get(); - // create cursor setup process QProcess *setCursor = new QProcess(); // create display setup script process @@ -319,14 +266,17 @@ env.insert(QStringLiteral("DISPLAY"), m_display); env.insert(QStringLiteral("HOME"), QStringLiteral("/")); env.insert(QStringLiteral("PATH"), mainConfig.Users.DefaultPath.get()); - env.insert(QStringLiteral("XAUTHORITY"), m_authPath); + env.insert(QStringLiteral("XAUTHORITY"), m_xauth.authPath()); env.insert(QStringLiteral("SHELL"), QStringLiteral("/bin/sh")); env.insert(QStringLiteral("XCURSOR_THEME"), mainConfig.Theme.CursorTheme.get()); + QString xcursorSize = mainConfig.Theme.CursorSize.get(); + if (!xcursorSize.isEmpty()) + env.insert(QStringLiteral("XCURSOR_SIZE"), xcursorSize); setCursor->setProcessEnvironment(env); displayScript->setProcessEnvironment(env); qDebug() << "Setting default cursor"; - setCursor->start(QStringLiteral("xsetroot -cursor_name left_ptr")); + setCursor->start(QStringLiteral("xsetroot"), { QStringLiteral("-cursor_name"), QStringLiteral("left_ptr") }); // delete setCursor on finish connect(setCursor, QOverload::of(&QProcess::finished), setCursor, &QProcess::deleteLater); @@ -338,8 +288,10 @@ } // start display setup script - qDebug() << "Running display setup script " << displayCommand; - displayScript->start(displayCommand); + qDebug() << "Running display setup script " << mainConfig.X11.DisplayCommand.get(); + QStringList displayCommand = QProcess::splitCommand(mainConfig.X11.DisplayCommand.get()); + const QString program = displayCommand.takeFirst(); + displayScript->start(program, displayCommand); // delete displayScript on finish connect(displayScript, QOverload::of(&QProcess::finished), displayScript, &QProcess::deleteLater); diff -Nru sddm-0.19.0/src/daemon/XorgDisplayServer.h sddm-0.20.0/src/daemon/XorgDisplayServer.h --- sddm-0.19.0/src/daemon/XorgDisplayServer.h 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/src/daemon/XorgDisplayServer.h 2023-06-23 12:28:38.000000000 +0000 @@ -22,6 +22,7 @@ #define SDDM_XORGDISPLAYSERVER_H #include "DisplayServer.h" +#include "XAuth.h" class QProcess; @@ -34,13 +35,11 @@ ~XorgDisplayServer(); const QString &display() const; - const QString &authPath() const; + QString authPath() const; QString sessionType() const; - const QString &cookie() const; - - bool addCookie(const QString &file); + const QByteArray cookie() const; public slots: bool start(); @@ -49,8 +48,7 @@ void setupDisplay(); private: - QString m_authPath; - QString m_cookie; + XAuth m_xauth; QProcess *process { nullptr }; diff -Nru sddm-0.19.0/src/daemon/XorgUserDisplayServer.cpp sddm-0.20.0/src/daemon/XorgUserDisplayServer.cpp --- sddm-0.19.0/src/daemon/XorgUserDisplayServer.cpp 1970-01-01 00:00:00.000000000 +0000 +++ sddm-0.20.0/src/daemon/XorgUserDisplayServer.cpp 2023-06-23 12:28:38.000000000 +0000 @@ -0,0 +1,102 @@ +/*************************************************************************** +* Copyright (c) 2021 Pier Luigi Fiorini +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the +* Free Software Foundation, Inc., +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +***************************************************************************/ + +#include "Configuration.h" +#include "DaemonApp.h" +#include "Display.h" +#include "Seat.h" +#include "XorgUserDisplayServer.h" + +namespace SDDM { + +XorgUserDisplayServer::XorgUserDisplayServer(Display *parent) + : DisplayServer(parent) +{ +} + +XorgUserDisplayServer::~XorgUserDisplayServer() +{ + stop(); +} + +QString XorgUserDisplayServer::sessionType() const +{ + return QStringLiteral("x11"); +} + +void XorgUserDisplayServer::setDisplayName(const QString &displayName) +{ + m_display = displayName; +} + +QString XorgUserDisplayServer::command(Display *display) +{ + QStringList args; + + if (daemonApp->testing()) { + args << mainConfig.X11.XephyrPath.get() + << QStringLiteral("-br") + << QStringLiteral("-screen") << QStringLiteral("800x600"); + } else { + args << mainConfig.X11.ServerPath.get() + << mainConfig.X11.ServerArguments.get().split(QLatin1Char(' '), Qt::SkipEmptyParts) + << QStringLiteral("-background") << QStringLiteral("none") + << QStringLiteral("-seat") << display->seat()->name() + << QStringLiteral("-noreset") + << QStringLiteral("-keeptty") + << QStringLiteral("-novtswitch") + << QStringLiteral("-verbose") << QStringLiteral("3"); + } + + return args.join(QLatin1Char(' ')); +} + +bool XorgUserDisplayServer::start() +{ + // Check flag + if (m_started) + return false; + + // Set flag + m_started = true; + emit started(); + + return true; +} + +void XorgUserDisplayServer::stop() +{ + // Check flag + if (!m_started) + return; + + // Reset flag + m_started = false; + emit stopped(); +} + +void XorgUserDisplayServer::finished() +{ +} + +void XorgUserDisplayServer::setupDisplay() +{ +} + +} // namespace SDDM diff -Nru sddm-0.19.0/src/daemon/XorgUserDisplayServer.h sddm-0.20.0/src/daemon/XorgUserDisplayServer.h --- sddm-0.19.0/src/daemon/XorgUserDisplayServer.h 1970-01-01 00:00:00.000000000 +0000 +++ sddm-0.20.0/src/daemon/XorgUserDisplayServer.h 2023-06-23 12:28:38.000000000 +0000 @@ -0,0 +1,53 @@ +/*************************************************************************** +* Copyright (c) 2021 Pier Luigi Fiorini +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the +* Free Software Foundation, Inc., +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +***************************************************************************/ + +#ifndef SDDM_XORGUSERDISPLAYSERVER_H +#define SDDM_XORGUSERDISPLAYSERVER_H + +#include "DisplayServer.h" +#include "XAuth.h" + +class QProcess; + +namespace SDDM { + +class XorgUserDisplayServer : public DisplayServer +{ + Q_OBJECT + Q_DISABLE_COPY(XorgUserDisplayServer) +public: + explicit XorgUserDisplayServer(Display *parent); + ~XorgUserDisplayServer(); + + QString sessionType() const; + + void setDisplayName(const QString &displayName); + + static QString command(Display *display); + +public Q_SLOTS: + bool start(); + void stop(); + void finished(); + void setupDisplay(); +}; + +} // namespace SDDM + +#endif // SDDM_XORGUSERDISPLAYSERVER_H diff -Nru sddm-0.19.0/src/greeter/CMakeLists.txt sddm-0.20.0/src/greeter/CMakeLists.txt --- sddm-0.19.0/src/greeter/CMakeLists.txt 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/src/greeter/CMakeLists.txt 2023-06-23 12:28:38.000000000 +0000 @@ -8,6 +8,7 @@ ${CMAKE_SOURCE_DIR}/src/common/Configuration.cpp ${CMAKE_SOURCE_DIR}/src/common/ConfigReader.cpp ${CMAKE_SOURCE_DIR}/src/common/Session.cpp + ${CMAKE_SOURCE_DIR}/src/common/SignalHandler.cpp ${CMAKE_SOURCE_DIR}/src/common/SocketWriter.cpp ${CMAKE_SOURCE_DIR}/src/common/ThemeConfig.cpp ${CMAKE_SOURCE_DIR}/src/common/ThemeMetadata.cpp @@ -18,16 +19,18 @@ ScreenModel.cpp SessionModel.cpp UserModel.cpp + waylandkeyboardbackend.cpp + waylandkeyboardbackend.h XcbKeyboardBackend.cpp ) configure_file("theme.qrc" "theme.qrc") -qt5_add_resources(RESOURCES ${CMAKE_CURRENT_BINARY_DIR}/theme.qrc) +qt_add_resources(RESOURCES ${CMAKE_CURRENT_BINARY_DIR}/theme.qrc) add_executable(sddm-greeter ${GREETER_SOURCES} ${RESOURCES}) target_link_libraries(sddm-greeter - Qt5::Quick + Qt${QT_MAJOR_VERSION}::Quick ${LIBXCB_LIBRARIES} ${LIBXKB_LIBRARIES}) diff -Nru sddm-0.19.0/src/greeter/GreeterApp.cpp sddm-0.20.0/src/greeter/GreeterApp.cpp --- sddm-0.19.0/src/greeter/GreeterApp.cpp 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/src/greeter/GreeterApp.cpp 2023-06-23 12:28:38.000000000 +0000 @@ -24,6 +24,7 @@ #include "Constants.h" #include "ScreenModel.h" #include "SessionModel.h" +#include "SignalHandler.h" #include "ThemeConfig.h" #include "ThemeMetadata.h" #include "UserModel.h" @@ -111,7 +112,7 @@ if (m_themeConfig) m_themeConfig->setTo(configFile); else - m_themeConfig = new ThemeConfig(configFile); + m_themeConfig = new ThemeConfig(configFile, this); const bool themeNeedsAllUsers = m_themeConfig->value(QStringLiteral("needsFullUserModel"), true).toBool(); if(m_userModel && themeNeedsAllUsers && !m_userModel->containsAllUsers()) { @@ -149,6 +150,7 @@ view->setResizeMode(QQuickView::SizeRootObjectToView); //view->setGeometry(QRect(QPoint(0, 0), screen->geometry().size())); view->setGeometry(screen->geometry()); + view->setFlags(Qt::FramelessWindowHint); m_views.append(view); // remove the view when the screen is removed, but we @@ -185,7 +187,7 @@ view->rootContext()->setContextProperty(QStringLiteral("sessionModel"), m_sessionModel); view->rootContext()->setContextProperty(QStringLiteral("screenModel"), screenModel); view->rootContext()->setContextProperty(QStringLiteral("userModel"), m_userModel); - view->rootContext()->setContextProperty(QStringLiteral("config"), *m_themeConfig); + view->rootContext()->setContextProperty(QStringLiteral("config"), m_themeConfig); view->rootContext()->setContextProperty(QStringLiteral("sddm"), m_proxy); view->rootContext()->setContextProperty(QStringLiteral("keyboard"), m_keyboard); view->rootContext()->setContextProperty(QStringLiteral("primaryScreen"), QGuiApplication::primaryScreen() == screen); @@ -226,7 +228,7 @@ // show qDebug() << "Adding view for" << screen->name() << screen->geometry(); - view->show(); + view->showFullScreen(); // activate windows for the primary screen to give focus to text fields if (QGuiApplication::primaryScreen() == screen) @@ -258,14 +260,20 @@ } // Set font - QVariant fontEntry = mainConfig.Theme.Font.get(); - QFont font = fontEntry.value(); - if (!fontEntry.toString().isEmpty()) - QGuiApplication::setFont(font); + const QString fontStr = mainConfig.Theme.Font.get(); + if (!fontStr.isEmpty()) { + QFont font; + if (font.fromString(fontStr)) { + QGuiApplication::setFont(font); + } + } // Set session model on proxy m_proxy->setSessionModel(m_sessionModel); + // If the socket ends, bail. There is not much we can do. + connect(m_proxy, &GreeterProxy::socketDisconnected, qGuiApp, &QCoreApplication::quit); + // Create views const QList screens = qGuiApp->primaryScreen()->virtualSiblings(); for (QScreen *screen : screens) @@ -296,9 +304,7 @@ int main(int argc, char **argv) { - // Install message handler - qInstallMessageHandler(SDDM::GreeterMessageHandler); - + bool testMode = false; // We set an attribute based on the platform we run on. // We only know the platform after we constructed QGuiApplication // though, so we need to find it out ourselves. @@ -307,6 +313,7 @@ if(qstrcmp(argv[i], "-platform") == 0) { platform = QString::fromUtf8(argv[i + 1]); } + testMode |= qstrcmp(argv[i], "--test-mode") == 0; } if (platform.isEmpty()) { platform = QString::fromUtf8(qgetenv("QT_QPA_PLATFORM")); @@ -315,6 +322,10 @@ platform = QStringLiteral("xcb"); } + // Install message handler + if (!testMode) + qInstallMessageHandler(SDDM::GreeterMessageHandler); + // HiDPI bool hiDpiEnabled = false; if (platform == QStringLiteral("xcb")) @@ -334,7 +345,23 @@ QSurfaceFormat::setDefaultFormat(format); } + // Some themes may use KDE components and that will automatically load KDE's + // crash handler which we don't want counterintuitively setting this env + // disables that handler + qputenv("KDE_DEBUG", "1"); + + // Qt IM module + if (!SDDM::mainConfig.InputMethod.get().isEmpty()) + qputenv("QT_IM_MODULE", SDDM::mainConfig.InputMethod.get().toLocal8Bit().constData()); + QGuiApplication app(argc, argv); + SDDM::SignalHandler s; + QObject::connect(&s, &SDDM::SignalHandler::sigtermReceived, &app, [] { + QCoreApplication::instance()->exit(-1); + }); + QObject::connect(&s, &SDDM::SignalHandler::sigintReceived, &app, [] { + QCoreApplication::instance()->exit(-1); + }); QCommandLineParser parser; parser.setApplicationDescription(TR("SDDM greeter")); diff -Nru sddm-0.19.0/src/greeter/GreeterProxy.cpp sddm-0.20.0/src/greeter/GreeterProxy.cpp --- sddm-0.19.0/src/greeter/GreeterProxy.cpp 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/src/greeter/GreeterProxy.cpp 2023-06-23 12:28:38.000000000 +0000 @@ -46,7 +46,7 @@ connect(d->socket, &QLocalSocket::connected, this, &GreeterProxy::connected); connect(d->socket, &QLocalSocket::disconnected, this, &GreeterProxy::disconnected); connect(d->socket, &QLocalSocket::readyRead, this, &GreeterProxy::readyRead); - connect(d->socket, QOverload::of(&QLocalSocket::error), this, &GreeterProxy::error); + connect(d->socket, &QLocalSocket::errorOccurred, this, &GreeterProxy::error); // connect to server d->socket->connectToServer(socket); @@ -138,6 +138,8 @@ void GreeterProxy::disconnected() { // log disconnection qDebug() << "Disconnected from the daemon."; + + Q_EMIT socketDisconnected(); } void GreeterProxy::error() { @@ -204,6 +206,14 @@ emit loginFailed(); } break; + case DaemonMessages::InformationMessage: { + QString message; + input >> message; + + qDebug() << "Information Message received from daemon: " << message; + emit informationMessage(message); + } + break; default: { // log message qWarning() << "Unknown message received from daemon."; diff -Nru sddm-0.19.0/src/greeter/GreeterProxy.h sddm-0.20.0/src/greeter/GreeterProxy.h --- sddm-0.19.0/src/greeter/GreeterProxy.h 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/src/greeter/GreeterProxy.h 2023-06-23 12:28:38.000000000 +0000 @@ -71,6 +71,7 @@ void error(); signals: + void informationMessage(const QString &message); void hostNameChanged(const QString &hostName); void canPowerOffChanged(bool canPowerOff); void canRebootChanged(bool canReboot); @@ -78,6 +79,7 @@ void canHibernateChanged(bool canHibernate); void canHybridSleepChanged(bool canHybridSleep); + void socketDisconnected(); void loginFailed(); void loginSucceeded(); diff -Nru sddm-0.19.0/src/greeter/KeyboardModel.cpp sddm-0.20.0/src/greeter/KeyboardModel.cpp --- sddm-0.19.0/src/greeter/KeyboardModel.cpp 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/src/greeter/KeyboardModel.cpp 2023-06-23 12:28:38.000000000 +0000 @@ -1,4 +1,5 @@ /*************************************************************************** +* Copyright (c) 2021 Pier Luigi Fiorini * Copyright (c) 2013 Nikita Mikhaylov * * This program is free software; you can redistribute it and/or modify @@ -17,8 +18,11 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ***************************************************************************/ +#include + #include "KeyboardModel.h" #include "KeyboardModel_p.h" +#include "waylandkeyboardbackend.h" #include "XcbKeyboardBackend.h" namespace SDDM { @@ -27,14 +31,21 @@ /**********************************************/ KeyboardModel::KeyboardModel() : d(new KeyboardModelPrivate) { - m_backend = new XcbKeyboardBackend(d); - m_backend->init(); - m_backend->connectEventsDispatcher(this); + if (QGuiApplication::platformName() == QLatin1String("xcb")) { + m_backend = new XcbKeyboardBackend(d); + m_backend->init(); + m_backend->connectEventsDispatcher(this); + } else if (QGuiApplication::platformName().contains(QLatin1String("wayland"))) { + m_backend = new WaylandKeyboardBackend(d); + m_backend->init(); + } } KeyboardModel::~KeyboardModel() { - m_backend->disconnect(); - delete m_backend; + if (m_backend) { + m_backend->disconnect(); + delete m_backend; + } for (QObject *layout: d->layouts) { delete layout; @@ -49,7 +60,8 @@ void KeyboardModel::setNumLockState(bool state) { if (d->numlock.enabled != state) { d->numlock.enabled = state; - m_backend->sendChanges(); + if (m_backend) + m_backend->sendChanges(); emit numLockStateChanged(); } @@ -62,7 +74,8 @@ void KeyboardModel::setCapsLockState(bool state) { if (d->capslock.enabled != state) { d->capslock.enabled = state; - m_backend->sendChanges(); + if (m_backend) + m_backend->sendChanges(); emit capsLockStateChanged(); } @@ -79,7 +92,8 @@ void KeyboardModel::setCurrentLayout(int id) { if (d->layout_id != id) { d->layout_id = id; - m_backend->sendChanges(); + if (m_backend) + m_backend->sendChanges(); emit currentLayoutChanged(); } @@ -96,7 +110,8 @@ QList layouts_old = d->layouts; // Process events - m_backend->dispatchEvents(); + if (m_backend) + m_backend->dispatchEvents(); // Send updates if (caps_old != d->capslock.enabled) diff -Nru sddm-0.19.0/src/greeter/KeyboardModel.h sddm-0.20.0/src/greeter/KeyboardModel.h --- sddm-0.19.0/src/greeter/KeyboardModel.h 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/src/greeter/KeyboardModel.h 2023-06-23 12:28:38.000000000 +0000 @@ -71,7 +71,7 @@ private: KeyboardModelPrivate * d { nullptr }; - KeyboardBackend * m_backend; + KeyboardBackend * m_backend = nullptr; }; } diff -Nru sddm-0.19.0/src/greeter/ScreenModel.cpp sddm-0.20.0/src/greeter/ScreenModel.cpp --- sddm-0.19.0/src/greeter/ScreenModel.cpp 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/src/greeter/ScreenModel.cpp 2023-06-23 12:28:38.000000000 +0000 @@ -58,7 +58,7 @@ } int ScreenModel::rowCount(const QModelIndex &parent) const { - return 1; + return parent.isValid() ? 0 : 1; } QVariant ScreenModel::data(const QModelIndex &index, int role) const { diff -Nru sddm-0.19.0/src/greeter/ScreenModel.h sddm-0.20.0/src/greeter/ScreenModel.h --- sddm-0.19.0/src/greeter/ScreenModel.h 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/src/greeter/ScreenModel.h 2023-06-23 12:28:38.000000000 +0000 @@ -40,6 +40,7 @@ NameRole = Qt::UserRole + 1, GeometryRole }; + Q_ENUM(ScreenRoles) ScreenModel(QScreen *screen, QObject *parent = 0); ~ScreenModel(); diff -Nru sddm-0.19.0/src/greeter/SessionModel.cpp sddm-0.20.0/src/greeter/SessionModel.cpp --- sddm-0.19.0/src/greeter/SessionModel.cpp 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/src/greeter/SessionModel.cpp 2023-06-23 12:28:38.000000000 +0000 @@ -22,6 +22,7 @@ #include "Configuration.h" +#include #include #include #include @@ -35,27 +36,36 @@ } int lastIndex { 0 }; + QStringList displayNames; QVector sessions; }; SessionModel::SessionModel(QObject *parent) : QAbstractListModel(parent), d(new SessionModelPrivate()) { + // Check for flag to show Wayland sessions + bool dri_active = QFileInfo::exists(QStringLiteral("/dev/dri")); + // initial population beginResetModel(); - populate(Session::WaylandSession, mainConfig.Wayland.SessionDir.get()); + if (dri_active) + populate(Session::WaylandSession, mainConfig.Wayland.SessionDir.get()); populate(Session::X11Session, mainConfig.X11.SessionDir.get()); endResetModel(); // refresh everytime a file is changed, added or removed QFileSystemWatcher *watcher = new QFileSystemWatcher(this); - connect(watcher, &QFileSystemWatcher::directoryChanged, [this](const QString &path) { + connect(watcher, &QFileSystemWatcher::directoryChanged, [this]() { + // Recheck for flag to show Wayland sessions + bool dri_active = QFileInfo::exists(QStringLiteral("/dev/dri")); beginResetModel(); d->sessions.clear(); - populate(Session::WaylandSession, mainConfig.Wayland.SessionDir.get()); + d->displayNames.clear(); + if (dri_active) + populate(Session::WaylandSession, mainConfig.Wayland.SessionDir.get()); populate(Session::X11Session, mainConfig.X11.SessionDir.get()); endResetModel(); }); - watcher->addPath(mainConfig.Wayland.SessionDir.get()); - watcher->addPath(mainConfig.X11.SessionDir.get()); + watcher->addPaths(mainConfig.Wayland.SessionDir.get()); + watcher->addPaths(mainConfig.X11.SessionDir.get()); } SessionModel::~SessionModel() { @@ -75,12 +85,12 @@ return roleNames; } - const int SessionModel::lastIndex() const { + int SessionModel::lastIndex() const { return d->lastIndex; } int SessionModel::rowCount(const QModelIndex &parent) const { - return d->sessions.length(); + return parent.isValid() ? 0 : d->sessions.length(); } QVariant SessionModel::data(const QModelIndex &index, int role) const { @@ -99,6 +109,8 @@ case TypeRole: return session->type(); case NameRole: + if (d->displayNames.count(session->displayName()) > 1 && session->type() == Session::WaylandSession) + return tr("%1 (Wayland)").arg(session->displayName()); return session->displayName(); case ExecRole: return session->exec(); @@ -112,17 +124,18 @@ return QVariant(); } - void SessionModel::populate(Session::Type type, const QString &path) { + void SessionModel::populate(Session::Type type, const QStringList &dirPaths) { // read session files - QDir dir(path); - dir.setNameFilters(QStringList() << QStringLiteral("*.desktop")); - dir.setFilter(QDir::Files); + QStringList sessions; + for (const auto &path: dirPaths) { + QDir dir = path; + dir.setNameFilters(QStringList() << QStringLiteral("*.desktop")); + dir.setFilter(QDir::Files); + sessions += dir.entryList(); + } // read session - const auto sessions = dir.entryList(); - for(const QString &session : sessions) { - if (!dir.exists(session)) - continue; - + sessions.removeDuplicates(); + for (auto& session : qAsConst(sessions)) { Session *si = new Session(type, session); bool execAllowed = true; QFileInfo fi(si->tryExec()); @@ -144,10 +157,12 @@ } } // add to sessions list - if (!si->isHidden() && !si->isNoDisplay() && execAllowed) + if (!si->isHidden() && !si->isNoDisplay() && execAllowed) { + d->displayNames.append(si->displayName()); d->sessions.push_back(si); - else + } else { delete si; + } } // find out index of the last session for (int i = 0; i < d->sessions.size(); ++i) { diff -Nru sddm-0.19.0/src/greeter/SessionModel.h sddm-0.20.0/src/greeter/SessionModel.h --- sddm-0.19.0/src/greeter/SessionModel.h 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/src/greeter/SessionModel.h 2023-06-23 12:28:38.000000000 +0000 @@ -43,13 +43,14 @@ ExecRole, CommentRole }; + Q_ENUM(SessionRole) SessionModel(QObject *parent = 0); ~SessionModel(); QHash roleNames() const override; - const int lastIndex() const; + int lastIndex() const; int rowCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; @@ -57,7 +58,7 @@ private: SessionModelPrivate *d { nullptr }; - void populate(Session::Type type, const QString &path); + void populate(Session::Type type, const QStringList &dirPaths); }; } diff -Nru sddm-0.19.0/src/greeter/theme/Main.qml sddm-0.20.0/src/greeter/theme/Main.qml --- sddm-0.19.0/src/greeter/theme/Main.qml 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/src/greeter/theme/Main.qml 2023-06-23 12:28:38.000000000 +0000 @@ -47,6 +47,10 @@ txtMessage.text = textConstants.loginFailed listView.currentItem.password = "" } + + onInformationMessage: { + txtMessage.text = message + } } Background { diff -Nru sddm-0.19.0/src/greeter/UserModel.cpp sddm-0.20.0/src/greeter/UserModel.cpp --- sddm-0.19.0/src/greeter/UserModel.cpp 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/src/greeter/UserModel.cpp 2023-06-23 12:28:38.000000000 +0000 @@ -136,14 +136,20 @@ if (avatarsEnabled) { const QString userFace = QStringLiteral("%1/.face.icon").arg(user->homeDir); const QString systemFace = QStringLiteral("%1/%2.face.icon").arg(facesDir).arg(user->name); - QString accountsServiceFace = QStringLiteral("/var/lib/AccountsService/icons/%1").arg(user->name); + const QString accountsServiceFace = QStringLiteral(ACCOUNTSSERVICE_DATA_DIR "/icons/%1").arg(user->name); - if (QFile::exists(userFace)) - user->icon = QStringLiteral("file://%1").arg(userFace); + QString userIcon; + // If the home is encrypted it takes a lot of time to open + // up the greeter, therefore we try the system avatar first + if (QFile::exists(systemFace)) + userIcon = systemFace; + else if (QFile::exists(userFace)) + userIcon = userFace; else if (QFile::exists(accountsServiceFace)) - user->icon = accountsServiceFace; - else if (QFile::exists(systemFace)) - user->icon = QStringLiteral("file://%1").arg(systemFace); + userIcon = accountsServiceFace; + + if (!userIcon.isEmpty()) + user->icon = QStringLiteral("file://%1").arg(userIcon); } } } @@ -164,7 +170,7 @@ return roleNames; } - const int UserModel::lastIndex() const { + int UserModel::lastIndex() const { return d->lastIndex; } @@ -173,7 +179,7 @@ } int UserModel::rowCount(const QModelIndex &parent) const { - return d->users.length(); + return parent.isValid() ? 0 : d->users.length(); } QVariant UserModel::data(const QModelIndex &index, int role) const { diff -Nru sddm-0.19.0/src/greeter/UserModel.h sddm-0.20.0/src/greeter/UserModel.h --- sddm-0.19.0/src/greeter/UserModel.h 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/src/greeter/UserModel.h 2023-06-23 12:28:38.000000000 +0000 @@ -43,13 +43,14 @@ IconRole, NeedsPasswordRole }; + Q_ENUM(UserRoles) UserModel(bool needAllUsers, QObject *parent = 0); ~UserModel(); QHash roleNames() const override; - const int lastIndex() const; + int lastIndex() const; QString lastUser() const; int rowCount(const QModelIndex &parent = QModelIndex()) const override; diff -Nru sddm-0.19.0/src/greeter/waylandkeyboardbackend.cpp sddm-0.20.0/src/greeter/waylandkeyboardbackend.cpp --- sddm-0.19.0/src/greeter/waylandkeyboardbackend.cpp 1970-01-01 00:00:00.000000000 +0000 +++ sddm-0.20.0/src/greeter/waylandkeyboardbackend.cpp 2023-06-23 12:28:38.000000000 +0000 @@ -0,0 +1,115 @@ +/*************************************************************************** +* Copyright (c) 2021 Pier Luigi Fiorini +* SPDX-FileCopyrightText: 2010 Andriy Rysin +* SPDX-FileCopyrightText: 2022 Aleix Pol Gonzalez +* SPDX-FileCopyrightText: 2022 Volker Krause +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the +* Free Software Foundation, Inc., +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +***************************************************************************/ + +#include +#include +#include +#include + +#include "KeyboardModel.h" +#include "KeyboardModel_p.h" +#include "KeyboardLayout.h" +#include "waylandkeyboardbackend.h" +#include + +namespace SDDM { + +WaylandKeyboardBackend::WaylandKeyboardBackend(KeyboardModelPrivate *kmp) + : KeyboardBackend(kmp) +{ +} + +WaylandKeyboardBackend::~WaylandKeyboardBackend() +{ +} + + +QList parseRules(const QString &filename, int ¤t) +{ + // FIXME: https://github.com/sddm/sddm/pull/1664#discussion_r1115361314 + current = 0; + QFile file(filename); + qDebug() << "Parsing xkb rules from" << file.fileName(); + if (!file.open(QFile::ReadOnly | QFile::Text)) { + qWarning() << "Cannot open the rules file" << file.fileName(); + return {}; + } + + QList layouts; + + QString lastName, lastDescription; + + QStringList path; + QXmlStreamReader reader(&file); + while (!reader.atEnd()) { + const auto token = reader.readNext(); + if (token == QXmlStreamReader::StartElement) { + path << reader.name().toString(); + QString strPath = path.join(QLatin1String("/")); + + if (strPath.endsWith(QLatin1String("layoutList/layout/configItem/name"))) { + lastName = reader.readElementText().trimmed(); + } else if (strPath.endsWith(QLatin1String("layoutList/layout/configItem/description"))) { + // TODO: This should be translated using i18nd("xkeyboard-config", lastDescription) + lastDescription = reader.readElementText().trimmed(); + } + } + // don't use token here, readElementText() above can have moved us forward meanwhile + if (reader.tokenType() == QXmlStreamReader::EndElement) { + const QString strPath = path.join(QLatin1String("/")); + if (strPath.endsWith(QLatin1String("layoutList/layout/configItem/description"))) { + layouts << new KeyboardLayout(lastName, lastDescription); + } + path.removeLast(); + } + } + + if (reader.hasError()) { + qWarning() << "Failed to parse the rules file" << file.fileName(); + return {}; + } + return layouts; +} + +void WaylandKeyboardBackend::init() +{ + d->layouts = parseRules(QStringLiteral("/usr/share/X11/xkb/rules/evdev.xml"), d->layout_id); +} + +void WaylandKeyboardBackend::disconnect() +{ +} + +void WaylandKeyboardBackend::sendChanges() +{ +} + +void WaylandKeyboardBackend::dispatchEvents() +{ +} + +void WaylandKeyboardBackend::connectEventsDispatcher(KeyboardModel *model) +{ + Q_UNUSED(model); +} + +} // namespace SDDM diff -Nru sddm-0.19.0/src/greeter/waylandkeyboardbackend.h sddm-0.20.0/src/greeter/waylandkeyboardbackend.h --- sddm-0.19.0/src/greeter/waylandkeyboardbackend.h 1970-01-01 00:00:00.000000000 +0000 +++ sddm-0.20.0/src/greeter/waylandkeyboardbackend.h 2023-06-23 12:28:38.000000000 +0000 @@ -0,0 +1,43 @@ +/*************************************************************************** +* Copyright (c) 2021 Pier Luigi Fiorini +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the +* Free Software Foundation, Inc., +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +***************************************************************************/ + +#ifndef WAYLANDKEYBOARDBACKEND_H +#define WAYLANDKEYBOARDBACKEND_H + +#include "KeyboardBackend.h" + +namespace SDDM { + +class WaylandKeyboardBackend : public KeyboardBackend +{ +public: + WaylandKeyboardBackend(KeyboardModelPrivate *kmp); + virtual ~WaylandKeyboardBackend(); + + void init() override; + void disconnect() override; + void sendChanges() override; + void dispatchEvents() override; + + void connectEventsDispatcher(KeyboardModel *model) override; +}; + +} // namespace SDDM + +#endif // WAYLANDKEYBOARDBACKEND_H diff -Nru sddm-0.19.0/src/greeter/XcbKeyboardBackend.cpp sddm-0.20.0/src/greeter/XcbKeyboardBackend.cpp --- sddm-0.19.0/src/greeter/XcbKeyboardBackend.cpp 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/src/greeter/XcbKeyboardBackend.cpp 2023-06-23 12:28:38.000000000 +0000 @@ -19,6 +19,7 @@ #include #include +#include #include "KeyboardModel.h" #include "KeyboardModel_p.h" @@ -267,8 +268,7 @@ } QList XcbKeyboardBackend::parseShortNames(QString text) { - QRegExp re(QStringLiteral(R"(\+([a-z]+))")); - re.setCaseSensitivity(Qt::CaseInsensitive); + QRegularExpression re(QStringLiteral(R"(\+([a-z]+))"), QRegularExpression::CaseInsensitiveOption); QList res; QSet blackList; // blacklist wrong tokens @@ -276,10 +276,11 @@ // Loop through matched substrings int pos = 0; - while ((pos = re.indexIn(text, pos)) != -1) { - if (!blackList.contains(re.cap(1))) - res << re.cap(1); - pos += re.matchedLength(); + QRegularExpressionMatch match; + while ((match = re.match(text, pos)).hasMatch()) { + if (!blackList.contains(match.captured(1))) + res << match.captured(1); + pos += match.capturedLength(); } return res; } @@ -307,7 +308,7 @@ void XcbKeyboardBackend::connectEventsDispatcher(KeyboardModel *model) { // Setup events filter xcb_void_cookie_t cookie; - xcb_xkb_select_events_details_t foo; + xcb_xkb_select_events_details_t foo = {}; xcb_generic_error_t *error = nullptr; cookie = xcb_xkb_select_events(m_conn, XCB_XKB_ID_USE_CORE_KBD, diff -Nru sddm-0.19.0/src/helper/backend/PamBackend.cpp sddm-0.20.0/src/helper/backend/PamBackend.cpp --- sddm-0.19.0/src/helper/backend/PamBackend.cpp 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/src/helper/backend/PamBackend.cpp 2023-06-23 12:28:38.000000000 +0000 @@ -23,9 +23,11 @@ #include "HelperApp.h" #include "UserSession.h" #include "Auth.h" +#include "VirtualTerminal.h" #include #include +#include #include @@ -58,14 +60,14 @@ AuthPrompt::Type PamData::detectPrompt(const struct pam_message* msg) const { if (msg->msg_style == PAM_PROMPT_ECHO_OFF) { QString message = QString::fromLocal8Bit(msg->msg); - if (message.indexOf(QRegExp(QStringLiteral("\\bpassword\\b"), Qt::CaseInsensitive)) >= 0) { - if (message.indexOf(QRegExp(QStringLiteral("\\b(re-?(enter|type)|again|confirm|repeat)\\b"), Qt::CaseInsensitive)) >= 0) { + if ((QRegularExpression(QStringLiteral("\\bpassword\\b"), QRegularExpression::CaseInsensitiveOption)).match(message).hasMatch()) { + if ((QRegularExpression(QStringLiteral("\\b(re-?(enter|type)|again|confirm|repeat)\\b"), QRegularExpression::CaseInsensitiveOption)).match(message).hasMatch()) { return AuthPrompt::CHANGE_REPEAT; } - else if (message.indexOf(QRegExp(QStringLiteral("\\bnew\\b"), Qt::CaseInsensitive)) >= 0) { + else if ((QRegularExpression(QStringLiteral("\\bnew\\b"), QRegularExpression::CaseInsensitiveOption)).match(message).hasMatch()) { return AuthPrompt::CHANGE_NEW; } - else if (message.indexOf(QRegExp(QStringLiteral("\\b(old|current)\\b"), Qt::CaseInsensitive)) >= 0) { + else if ((QRegularExpression(QStringLiteral("\\b(old|current)\\b"), QRegularExpression::CaseInsensitiveOption)).match(message).hasMatch()) { return AuthPrompt::CHANGE_CURRENT; } else { @@ -153,7 +155,7 @@ } Auth::Info PamData::handleInfo(const struct pam_message* msg, bool predict) { - if (QString::fromLocal8Bit(msg->msg).indexOf(QRegExp(QStringLiteral("^Changing password for [^ ]+$")))) { + if ((QRegularExpression(QStringLiteral("^Changing password for [^ ]+$"))).match(QString::fromLocal8Bit(msg->msg)).hasMatch()) { if (predict) m_currentRequest = Request(changePassRequest); return Auth::INFO_PASS_CHANGE_REQUIRED; @@ -248,17 +250,19 @@ } QProcessEnvironment sessionEnv = m_app->session()->processEnvironment(); - if (sessionEnv.value(QStringLiteral("XDG_SESSION_TYPE")) == QLatin1String("x11")) { + const auto sessionType = sessionEnv.value(QStringLiteral("XDG_SESSION_TYPE")); + const auto sessionClass = sessionEnv.value(QStringLiteral("XDG_SESSION_CLASS")); + QString tty = VirtualTerminal::path(sessionEnv.value(QStringLiteral("XDG_VTNR")).toInt()); + m_pam->setItem(PAM_TTY, qPrintable(tty)); + if (sessionType == QLatin1String("x11") && (sessionClass == QLatin1String("user") || !m_displayServer)) { QString display = sessionEnv.value(QStringLiteral("DISPLAY")); if (!display.isEmpty()) { #ifdef PAM_XDISPLAY m_pam->setItem(PAM_XDISPLAY, qPrintable(display)); -#endif +#else m_pam->setItem(PAM_TTY, qPrintable(display)); +#endif } - } else if (sessionEnv.value(QStringLiteral("XDG_SESSION_TYPE")) == QLatin1String("wayland")) { - QString tty = QStringLiteral("/dev/tty%1").arg(sessionEnv.value(QStringLiteral("XDG_VTNR"))); - m_pam->setItem(PAM_TTY, qPrintable(tty)); } if (!m_pam->putEnv(sessionEnv)) { diff -Nru sddm-0.19.0/src/helper/backend/PamBackend.h sddm-0.20.0/src/helper/backend/PamBackend.h --- sddm-0.19.0/src/helper/backend/PamBackend.h 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/src/helper/backend/PamBackend.h 2023-06-23 12:28:38.000000000 +0000 @@ -18,7 +18,7 @@ * */ -#if !defined(PAMBACKEND_H) && defined(USE_PAM) +#if !defined(PAMBACKEND_H) #define PAMBACKEND_H #include "Constants.h" diff -Nru sddm-0.19.0/src/helper/backend/PasswdBackend.cpp sddm-0.20.0/src/helper/backend/PasswdBackend.cpp --- sddm-0.19.0/src/helper/backend/PasswdBackend.cpp 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/src/helper/backend/PasswdBackend.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,109 +0,0 @@ -/* - * /etc/passwd authentication backend - * Copyright (C) 2014 Martin Bříza - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - */ - -#include "PasswdBackend.h" - -#include "AuthMessages.h" -#include "HelperApp.h" - -#include - -#include -#include -#include - -#ifdef HAVE_GETSPNAM -#include -#endif - -namespace SDDM { - PasswdBackend::PasswdBackend(HelperApp *parent) - : Backend(parent) { } - - bool PasswdBackend::authenticate() { - if (m_autologin) - return true; - - if (m_user == QLatin1String("sddm")) { - if (m_greeter) - return true; - else - return false; - } - - Request r; - QString password; - - if (m_user.isEmpty()) - r.prompts << Prompt(AuthPrompt::LOGIN_USER, QStringLiteral("Login"), false); - r.prompts << Prompt(AuthPrompt::LOGIN_PASSWORD, QStringLiteral("Password"), true); - - Request response = m_app->request(r); - for(const Prompt &p : qAsConst(response.prompts)) { - switch (p.type) { - case AuthPrompt::LOGIN_USER: - m_user = QString::fromUtf8(p.response); - break; - case AuthPrompt::LOGIN_PASSWORD: - password = QString::fromUtf8(p.response); - break; - default: - break; - } - } - - struct passwd *pw = getpwnam(qPrintable(m_user)); - if (!pw) { - m_app->error(QStringLiteral("Wrong user/password combination"), Auth::ERROR_AUTHENTICATION); - return false; - } - const char *system_passwd = pw->pw_passwd; - -#ifdef HAVE_GETSPNAM - struct spwd *spw = getspnam(pw->pw_name); - if (!spw) { - qWarning() << "[Passwd] Could get passwd but not shadow"; - return false; - } - - if(!spw->sp_pwdp || !spw->sp_pwdp[0]) - return true; - - system_passwd = spw->sp_pwdp; -#endif - - const char * const crypted = crypt(qPrintable(password), system_passwd); - if (0 == strcmp(crypted, system_passwd)) { - return true; - } - - m_app->error(QStringLiteral("Wrong user/password combination"), Auth::ERROR_AUTHENTICATION); - return false; - } - - bool PasswdBackend::start(const QString& user) { - m_user = user; - return true; - } - - QString PasswdBackend::userName() { - return m_user; - } -} diff -Nru sddm-0.19.0/src/helper/backend/PasswdBackend.h sddm-0.20.0/src/helper/backend/PasswdBackend.h --- sddm-0.19.0/src/helper/backend/PasswdBackend.h 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/src/helper/backend/PasswdBackend.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,43 +0,0 @@ -/* - * /etc/passwd authentication backend - * Copyright (C) 2014 Martin Bříza - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - */ - -#if !defined(PASSWDBACKEND_H) && !defined(USE_PAM) -#define PASSWDBACKEND_H - -#include "../Backend.h" - -namespace SDDM { - class PasswdBackend : public Backend { - Q_OBJECT - public: - PasswdBackend(HelperApp *parent); - - public slots: - virtual bool start(const QString &user = QString()); - virtual bool authenticate(); - - virtual QString userName(); - - private: - QString m_user { }; - }; -} - -#endif // PASSWDBACKEND_H diff -Nru sddm-0.19.0/src/helper/Backend.cpp sddm-0.20.0/src/helper/Backend.cpp --- sddm-0.19.0/src/helper/Backend.cpp 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/src/helper/Backend.cpp 2023-06-23 12:28:38.000000000 +0000 @@ -22,7 +22,6 @@ #include "HelperApp.h" #include "backend/PamBackend.h" -#include "backend/PasswdBackend.h" #include "Configuration.h" #include "UserSession.h" @@ -43,17 +42,18 @@ Backend *Backend::get(HelperApp* parent) { - #ifdef USE_PAM return new PamBackend(parent); - #else - return new PasswdBackend(parent); - #endif } void Backend::setAutologin(bool on) { m_autologin = on; } + void Backend::setDisplayServer(bool on) + { + m_displayServer = on; + } + void Backend::setGreeter(bool on) { m_greeter = on; } @@ -68,13 +68,6 @@ env.insert(QStringLiteral("SHELL"), QString::fromLocal8Bit(pw->pw_shell)); env.insert(QStringLiteral("USER"), QString::fromLocal8Bit(pw->pw_name)); env.insert(QStringLiteral("LOGNAME"), QString::fromLocal8Bit(pw->pw_name)); - if (env.contains(QStringLiteral("DISPLAY")) && !env.contains(QStringLiteral("XAUTHORITY"))) { - // determine Xauthority path - QString value = QStringLiteral("%1/%2") - .arg(QString::fromLocal8Bit(pw->pw_dir)) - .arg(mainConfig.X11.UserAuthFile.get()); - env.insert(QStringLiteral("XAUTHORITY"), value); - } #if defined(Q_OS_FREEBSD) /* get additional environment variables via setclassenvironment(); this needs to be done here instead of in UserSession::setupChildProcess diff -Nru sddm-0.19.0/src/helper/Backend.h sddm-0.20.0/src/helper/Backend.h --- sddm-0.19.0/src/helper/Backend.h 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/src/helper/Backend.h 2023-06-23 12:28:38.000000000 +0000 @@ -36,6 +36,7 @@ static Backend *get(HelperApp *parent); void setAutologin(bool on = true); + void setDisplayServer(bool on = true); void setGreeter(bool on = true); public slots: @@ -50,6 +51,7 @@ Backend(HelperApp *parent); HelperApp *m_app; bool m_autologin { false }; + bool m_displayServer = false; bool m_greeter { false }; }; } diff -Nru sddm-0.19.0/src/helper/CMakeLists.txt sddm-0.20.0/src/helper/CMakeLists.txt --- sddm-0.19.0/src/helper/CMakeLists.txt 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/src/helper/CMakeLists.txt 2023-06-23 12:28:38.000000000 +0000 @@ -3,6 +3,7 @@ include_directories( "${CMAKE_SOURCE_DIR}/src/common" "${CMAKE_SOURCE_DIR}/src/auth" + ${LIBXAU_INCLUDE_DIRS} ) include_directories("${CMAKE_BINARY_DIR}/src/common") @@ -10,38 +11,30 @@ ${CMAKE_SOURCE_DIR}/src/common/Configuration.cpp ${CMAKE_SOURCE_DIR}/src/common/ConfigReader.cpp ${CMAKE_SOURCE_DIR}/src/common/SafeDataStream.cpp + ${CMAKE_SOURCE_DIR}/src/common/XAuth.cpp + ${CMAKE_SOURCE_DIR}/src/common/SignalHandler.cpp + ${CMAKE_SOURCE_DIR}/src/auth/Auth.cpp + ${CMAKE_SOURCE_DIR}/src/auth/AuthRequest.cpp + ${CMAKE_SOURCE_DIR}/src/auth/AuthPrompt.cpp Backend.cpp HelperApp.cpp UserSession.cpp ) -# Different implementations of the VT switching code -# (where the FreeBSD version does nothing). -if(${CMAKE_SYSTEM} MATCHES "FreeBSD") - list(APPEND HELPER_SOURCES ${CMAKE_SOURCE_DIR}/src/common/VirtualTerminal_FreeBSD.cpp) -else() - list(APPEND HELPER_SOURCES ${CMAKE_SOURCE_DIR}/src/common/VirtualTerminal.cpp) -endif() +list(APPEND HELPER_SOURCES ${CMAKE_SOURCE_DIR}/src/common/VirtualTerminal.cpp) -if(PAM_FOUND) - set(HELPER_SOURCES - ${HELPER_SOURCES} - backend/PamHandle.cpp - backend/PamBackend.cpp - ) -else() - set(HELPER_SOURCES - ${HELPER_SOURCES} - backend/PasswdBackend.cpp - ) - - if(HAVE_GETSPNAM) - add_definitions(-DHAVE_GETSPNAM=1) - endif() -endif() +set(HELPER_SOURCES + ${HELPER_SOURCES} + backend/PamHandle.cpp + backend/PamBackend.cpp +) add_executable(sddm-helper ${HELPER_SOURCES}) -target_link_libraries(sddm-helper Qt5::Network Qt5::DBus Qt5::Qml) +target_link_libraries(sddm-helper + Qt${QT_MAJOR_VERSION}::Network + Qt${QT_MAJOR_VERSION}::DBus + Qt${QT_MAJOR_VERSION}::Qml + ${LIBXAU_LINK_LIBRARIES}) if("${CMAKE_SYSTEM_NAME}" STREQUAL "FreeBSD") # On FreeBSD (possibly other BSDs as well), we want to use # setusercontext() to set up the login configuration from login.conf @@ -60,8 +53,24 @@ target_link_libraries(sddm-helper crypt) endif() +install(TARGETS sddm-helper RUNTIME DESTINATION "${CMAKE_INSTALL_LIBEXECDIR}") + +add_executable(sddm-helper-start-wayland HelperStartWayland.cpp waylandsocketwatcher.cpp waylandhelper.cpp ${CMAKE_SOURCE_DIR}/src/common/SignalHandler.cpp) +target_link_libraries(sddm-helper-start-wayland Qt${QT_MAJOR_VERSION}::Core) +install(TARGETS sddm-helper-start-wayland RUNTIME DESTINATION "${CMAKE_INSTALL_LIBEXECDIR}") + +add_executable(sddm-helper-start-x11user HelperStartX11User.cpp xorguserhelper.cpp + ${CMAKE_SOURCE_DIR}/src/common/ConfigReader.cpp + ${CMAKE_SOURCE_DIR}/src/common/Configuration.cpp + ${CMAKE_SOURCE_DIR}/src/common/XAuth.cpp + ${CMAKE_SOURCE_DIR}/src/common/SignalHandler.cpp + ) +target_link_libraries(sddm-helper-start-x11user Qt${QT_MAJOR_VERSION}::Core + ${LIBXAU_LINK_LIBRARIES}) +install(TARGETS sddm-helper-start-x11user RUNTIME DESTINATION "${CMAKE_INSTALL_LIBEXECDIR}") + if(JOURNALD_FOUND) target_link_libraries(sddm-helper ${JOURNALD_LIBRARIES}) + target_link_libraries(sddm-helper-start-x11user ${JOURNALD_LIBRARIES}) + target_link_libraries(sddm-helper-start-wayland ${JOURNALD_LIBRARIES}) endif() - -install(TARGETS sddm-helper RUNTIME DESTINATION "${CMAKE_INSTALL_LIBEXECDIR}") diff -Nru sddm-0.19.0/src/helper/HelperApp.cpp sddm-0.20.0/src/helper/HelperApp.cpp --- sddm-0.19.0/src/helper/HelperApp.cpp 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/src/helper/HelperApp.cpp 2023-06-23 12:28:38.000000000 +0000 @@ -20,11 +20,13 @@ #include "HelperApp.h" #include "Backend.h" +#include "Configuration.h" #include "UserSession.h" #include "SafeDataStream.h" #include "MessageHandler.h" #include "VirtualTerminal.h" +#include "SignalHandler.h" #include #include @@ -41,6 +43,7 @@ #endif #include #include +#include namespace SDDM { HelperApp::HelperApp(int& argc, char** argv) @@ -49,12 +52,16 @@ , m_session(new UserSession(this)) , m_socket(new QLocalSocket(this)) { qInstallMessageHandler(HelperMessageHandler); + SignalHandler *s = new SignalHandler(this); + QObject::connect(s, &SignalHandler::sigtermReceived, m_session, [] { + QCoreApplication::instance()->exit(-1); + }); QTimer::singleShot(0, this, SLOT(setUp())); } void HelperApp::setUp() { - QStringList args = QCoreApplication::arguments(); + const QStringList args = QCoreApplication::arguments(); QString server; int pos; @@ -94,6 +101,16 @@ m_user = args[pos + 1]; } + if ((pos = args.indexOf(QStringLiteral("--display-server"))) >= 0) { + if (pos >= args.length() - 1) { + qCritical() << "This application is not supposed to be executed manually"; + exit(Auth::HELPER_OTHER_ERROR); + return; + } + m_session->setDisplayServerCommand(args[pos + 1]); + m_backend->setDisplayServer(true); + } + if ((pos = args.indexOf(QStringLiteral("--autologin"))) >= 0) { m_backend->setAutologin(true); } @@ -109,7 +126,7 @@ } connect(m_socket, &QLocalSocket::connected, this, &HelperApp::doAuth); - connect(m_session, QOverload::of(&QProcess::finished), this, &HelperApp::sessionFinished); + connect(m_session, &UserSession::finished, this, &HelperApp::sessionFinished); m_socket->connectToServer(server, QIODevice::ReadWrite | QIODevice::Unbuffered); } @@ -124,22 +141,23 @@ authenticated(QString()); // write failed login to btmp - QProcessEnvironment env = m_session->processEnvironment(); - QString displayId = env.value(QStringLiteral("DISPLAY")); - QString vt = env.value(QStringLiteral("XDG_VTNR")); + const QProcessEnvironment env = m_session->processEnvironment(); + const QString displayId = env.value(QStringLiteral("DISPLAY")); + const QString vt = env.value(QStringLiteral("XDG_VTNR")); utmpLogin(vt, displayId, m_user, 0, false); exit(Auth::HELPER_AUTH_ERROR); return; } + Q_ASSERT(getuid() == 0); if (!m_backend->authenticate()) { authenticated(QString()); // write failed login to btmp - QProcessEnvironment env = m_session->processEnvironment(); - QString displayId = env.value(QStringLiteral("DISPLAY")); - QString vt = env.value(QStringLiteral("XDG_VTNR")); + const QProcessEnvironment env = m_session->processEnvironment(); + const QString displayId = env.value(QStringLiteral("DISPLAY")); + const QString vt = env.value(QStringLiteral("XDG_VTNR")); utmpLogin(vt, displayId, m_user, 0, false); exit(Auth::HELPER_AUTH_ERROR); @@ -149,13 +167,21 @@ m_user = m_backend->userName(); QProcessEnvironment env = authenticated(m_user); + if (env.value(QStringLiteral("XDG_SESSION_CLASS")) == QLatin1String("greeter")) { + // Qt internally may load the xdg portal system early on, prevent this, we do not have a functional session running. + env.insert(QStringLiteral("QT_NO_XDG_DESKTOP_PORTAL"), QStringLiteral("1")); + for (const auto &entry : mainConfig.GreeterEnvironment.get()) { + const int index = entry.indexOf(QLatin1Char('=')); + if (index < 0) { + qWarning() << "Malformed environment variable" << entry; + continue; + } + env.insert(entry.left(index), entry.mid(index + 1)); + } + } + if (!m_session->path().isEmpty()) { env.insert(m_session->processEnvironment()); - // Allocate a new VT for the wayland session - if(env.value(QStringLiteral("XDG_SESSION_TYPE")) == QLatin1String("wayland")) { - int vtNumber = VirtualTerminal::setUpNewVt(); - env.insert(QStringLiteral("XDG_VTNR"), QString::number(vtNumber)); - } m_session->setProcessEnvironment(env); if (!m_backend->openSession()) { @@ -167,12 +193,11 @@ sessionOpened(true); // write successful login to utmp/wtmp - QProcessEnvironment env = m_session->processEnvironment(); - QString displayId = env.value(QStringLiteral("DISPLAY")); - QString vt = env.value(QStringLiteral("XDG_VTNR")); + const QProcessEnvironment env = m_session->processEnvironment(); + const QString displayId = env.value(QStringLiteral("DISPLAY")); + const QString vt = env.value(QStringLiteral("XDG_VTNR")); if (env.value(QStringLiteral("XDG_SESSION_CLASS")) != QLatin1String("greeter")) { // cache pid for session end - m_session->setCachedProcessId(m_session->processId()); utmpLogin(vt, displayId, m_user, m_session->processId(), true); } } @@ -182,17 +207,6 @@ } void HelperApp::sessionFinished(int status) { - m_backend->closeSession(); - - // write logout to utmp/wtmp - qint64 pid = m_session->cachedProcessId(); - QProcessEnvironment env = m_session->processEnvironment(); - if (env.value(QStringLiteral("XDG_SESSION_CLASS")) != QLatin1String("greeter")) { - QString vt = env.value(QStringLiteral("XDG_VTNR")); - QString displayId = env.value(QStringLiteral("DISPLAY")); - utmpLogout(vt, displayId, pid); - } - exit(status); } @@ -237,7 +251,7 @@ str >> m >> env >> m_cookie; if (m != AUTHENTICATED) { env = QProcessEnvironment(); - m_cookie = QString(); + m_cookie = {}; qCritical() << "Received a wrong opcode instead of AUTHENTICATED:" << m; } return env; @@ -255,6 +269,19 @@ } } + void HelperApp::displayServerStarted(const QString &displayName) + { + Msg m = Msg::MSG_UNKNOWN; + SafeDataStream str(m_socket); + str << Msg::DISPLAY_SERVER_STARTED << displayName; + str.send(); + str.receive(); + str >> m; + if (m != DISPLAY_SERVER_STARTED) { + qCritical() << "Received a wrong opcode instead of DISPLAY_SERVER_STARTED:" << m; + } + } + UserSession *HelperApp::session() { return m_session; } @@ -263,19 +290,33 @@ return m_user; } - const QString& HelperApp::cookie() const { + const QByteArray& HelperApp::cookie() const { return m_cookie; } HelperApp::~HelperApp() { + Q_ASSERT(getuid() == 0); + m_session->stop(); + m_backend->closeSession(); + + // write logout to utmp/wtmp + qint64 pid = m_session->cachedProcessId(); + if (pid < 0) { + return; + } + QProcessEnvironment env = m_session->processEnvironment(); + if (env.value(QStringLiteral("XDG_SESSION_CLASS")) != QLatin1String("greeter")) { + QString vt = env.value(QStringLiteral("XDG_VTNR")); + QString displayId = env.value(QStringLiteral("DISPLAY")); + utmpLogout(vt, displayId, pid); + } } void HelperApp::utmpLogin(const QString &vt, const QString &displayName, const QString &user, qint64 pid, bool authSuccessful) { - struct utmpx entry; + struct utmpx entry { }; struct timeval tv; - entry = { 0 }; entry.ut_type = USER_PROCESS; entry.ut_pid = pid; @@ -326,10 +367,9 @@ } void HelperApp::utmpLogout(const QString &vt, const QString &displayName, qint64 pid) { - struct utmpx entry; + struct utmpx entry { }; struct timeval tv; - entry = { 0 }; entry.ut_type = DEAD_PROCESS; entry.ut_pid = pid; diff -Nru sddm-0.19.0/src/helper/HelperApp.h sddm-0.20.0/src/helper/HelperApp.h --- sddm-0.19.0/src/helper/HelperApp.h 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/src/helper/HelperApp.h 2023-06-23 12:28:38.000000000 +0000 @@ -40,13 +40,14 @@ UserSession *session(); const QString &user() const; - const QString &cookie() const; + const QByteArray &cookie() const; public slots: Request request(const Request &request); void info(const QString &message, Auth::Info type); void error(const QString &message, Auth::Error type); QProcessEnvironment authenticated(const QString &user); + void displayServerStarted(const QString &displayName); void sessionOpened(bool success); private slots: @@ -62,7 +63,7 @@ QLocalSocket *m_socket { nullptr }; QString m_user { }; // TODO: get rid of this in a nice clean way along the way with moving to user session X server - QString m_cookie { }; + QByteArray m_cookie { }; /*! \brief Write utmp/wtmp/btmp records when a user logs in diff -Nru sddm-0.19.0/src/helper/HelperStartWayland.cpp sddm-0.20.0/src/helper/HelperStartWayland.cpp --- sddm-0.19.0/src/helper/HelperStartWayland.cpp 1970-01-01 00:00:00.000000000 +0000 +++ sddm-0.20.0/src/helper/HelperStartWayland.cpp 2023-06-23 12:28:38.000000000 +0000 @@ -0,0 +1,72 @@ +/* + * Session process wrapper + * Copyright (C) 2021 Aleix Pol Gonzalez + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +/** + * This application sole purpose is to launch a wayland compositor (first + * argument) and as soon as it's set up to launch a client (second argument) + */ + +#include +#include +#include +#include +#include "waylandhelper.h" +#include "MessageHandler.h" +#include +#include "Auth.h" +#include "SignalHandler.h" + +void WaylandHelperMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) { + SDDM::messageHandler(type, context, QStringLiteral("WaylandHelper: "), msg); +} + +int main(int argc, char** argv) +{ + qInstallMessageHandler(WaylandHelperMessageHandler); + QCoreApplication app(argc, argv); + using namespace SDDM; + SDDM::SignalHandler s; + + Q_ASSERT(::getuid() != 0); + if (argc != 3) { + QTextStream(stderr) << "Wrong number of arguments\n"; + return Auth::HELPER_OTHER_ERROR; + } + + WaylandHelper helper; + QObject::connect(&s, &SDDM::SignalHandler::sigtermReceived, &app, [] { + QCoreApplication::exit(0); + }); + QObject::connect(&app, &QCoreApplication::aboutToQuit, &helper, [&helper] { + qDebug("quitting helper-start-wayland"); + helper.stop(); + }); + QObject::connect(&helper, &WaylandHelper::failed, &app, [&app] { + QTextStream(stderr) << "Failed to start wayland session" << Qt::endl; + app.exit(Auth::HELPER_SESSION_ERROR); + }); + + if (!helper.startCompositor(app.arguments()[1])) { + qWarning() << "SDDM was unable to start" << app.arguments()[1]; + return Auth::HELPER_DISPLAYSERVER_ERROR; + } + helper.startGreeter(app.arguments()[2]); + return app.exec(); +} diff -Nru sddm-0.19.0/src/helper/HelperStartX11User.cpp sddm-0.20.0/src/helper/HelperStartX11User.cpp --- sddm-0.19.0/src/helper/HelperStartX11User.cpp 1970-01-01 00:00:00.000000000 +0000 +++ sddm-0.20.0/src/helper/HelperStartX11User.cpp 2023-06-23 12:28:38.000000000 +0000 @@ -0,0 +1,76 @@ +/* + * Session process wrapper + * Copyright (C) 2021 Aleix Pol Gonzalez + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +/** + * This application sole purpose is to launch an X11 rootless compositor compositor (first + * argument) and as soon as it's set up to launch a client (second argument) + */ + +#include +#include +#include +#include +#include +#include "xorguserhelper.h" +#include "MessageHandler.h" +#include +#include "SignalHandler.h" + +void X11UserHelperMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) { + SDDM::messageHandler(type, context, QStringLiteral("X11UserHelper: "), msg); +} + +int main(int argc, char** argv) +{ + qInstallMessageHandler(X11UserHelperMessageHandler); + QCoreApplication app(argc, argv); + SDDM::SignalHandler s; + QObject::connect(&s, &SDDM::SignalHandler::sigtermReceived, &app, [] { + QCoreApplication::instance()->exit(-1); + }); + + Q_ASSERT(::getuid() != 0); + if (argc != 3) { + QTextStream(stderr) << "Wrong number of arguments\n"; + return 33; + } + + using namespace SDDM; + XOrgUserHelper helper; + QObject::connect(&app, &QCoreApplication::aboutToQuit, &helper, [&helper] { + qDebug("quitting helper-start-x11"); + helper.stop(); + }); + QObject::connect(&helper, &XOrgUserHelper::displayChanged, &app, [&helper, &app] { + qDebug() << "starting XOrg Greeter..." << helper.sessionEnvironment().value(QStringLiteral("DISPLAY")); + auto args = QProcess::splitCommand(app.arguments()[2]); + + QProcess *process = new QProcess(&app); + process->setProcessChannelMode(QProcess::ForwardedChannels); + process->setProgram(args.takeFirst()); + process->setArguments(args); + process->setProcessEnvironment(helper.sessionEnvironment()); + process->start(); + QObject::connect(process, QOverload::of(&QProcess::finished), &app, &QCoreApplication::quit); + }); + + helper.start(app.arguments()[1]); + return app.exec(); +} diff -Nru sddm-0.19.0/src/helper/UserSession.cpp sddm-0.20.0/src/helper/UserSession.cpp --- sddm-0.19.0/src/helper/UserSession.cpp 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/src/helper/UserSession.cpp 2023-06-23 12:28:38.000000000 +0000 @@ -19,11 +19,16 @@ * */ +#include + #include "Configuration.h" +#include "Constants.h" #include "UserSession.h" #include "HelperApp.h" #include "VirtualTerminal.h" +#include "XAuth.h" +#include #include #include #include @@ -34,34 +39,136 @@ #include #include #include +#ifdef Q_OS_FREEBSD +#include +#endif namespace SDDM { UserSession::UserSession(HelperApp *parent) - : QProcess(parent) { + : QProcess(parent) + { + connect(this, QOverload::of(&QProcess::finished), this, &UserSession::finished); +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + setChildProcessModifier(std::bind(&UserSession::childModifier, this)); +#endif } - UserSession::~UserSession() { + bool UserSession::start() { + auto helper = qobject_cast(parent()); + QProcessEnvironment env = processEnvironment(); - } + bool isWaylandGreeter = false; - bool UserSession::start() { - QProcessEnvironment env = qobject_cast(parent())->session()->processEnvironment(); + // If the Xorg display server was already started, write the passed + // auth cookie to /tmp/xauth_XXXXXX. This is done in the parent process + // so that it can clean up the file on session end. + if (env.value(QStringLiteral("XDG_SESSION_TYPE")) == QLatin1String("x11") + && m_displayServerCmd.isEmpty()) { + // Create the Xauthority file + QByteArray cookie = helper->cookie(); + if (cookie.isEmpty()) { + qCritical() << "Can't start X11 session with empty auth cookie"; + return false; + } + + // Place it into /tmp, which is guaranteed to be read/writeable by + // everyone while having the sticky bit set to avoid messing with + // other's files. + m_xauthFile.setFileTemplate(QStringLiteral("/tmp/xauth_XXXXXX")); + + if (!m_xauthFile.open()) { + qCritical() << "Could not create the Xauthority file"; + return false; + } + + QString display = processEnvironment().value(QStringLiteral("DISPLAY")); + + if (!XAuth::writeCookieToFile(display, m_xauthFile.fileName(), cookie)) { + qCritical() << "Failed to write the Xauthority file"; + m_xauthFile.close(); + return false; + } + + env.insert(QStringLiteral("XAUTHORITY"), m_xauthFile.fileName()); + setProcessEnvironment(env); + } + + if (env.value(QStringLiteral("XDG_SESSION_TYPE")) == QLatin1String("x11")) { + QString command; + if (env.value(QStringLiteral("XDG_SESSION_CLASS")) == QLatin1String("greeter")) { + command = m_path; + } else { + command = QStringLiteral("%1 \"%2\"").arg(mainConfig.X11.SessionCommand.get()).arg(m_path); + } + + qInfo() << "Starting X11 session:" << m_displayServerCmd << command; + if (m_displayServerCmd.isEmpty()) { + auto args = QProcess::splitCommand(command); + setProgram(args.takeFirst()); + setArguments(args); + } else { + setProgram(QStringLiteral(LIBEXEC_INSTALL_DIR "/sddm-helper-start-x11user")); + setArguments({m_displayServerCmd, command}); + } + QProcess::start(); - if (env.value(QStringLiteral("XDG_SESSION_CLASS")) == QLatin1String("greeter")) { - QProcess::start(m_path); - } else if (env.value(QStringLiteral("XDG_SESSION_TYPE")) == QLatin1String("x11")) { - const QString cmd = QStringLiteral("%1 \"%2\"").arg(mainConfig.X11.SessionCommand.get()).arg(m_path); - qInfo() << "Starting:" << cmd; - QProcess::start(cmd); } else if (env.value(QStringLiteral("XDG_SESSION_TYPE")) == QLatin1String("wayland")) { - const QString cmd = QStringLiteral("%1 %2").arg(mainConfig.Wayland.SessionCommand.get()).arg(m_path); - qInfo() << "Starting:" << cmd; - QProcess::start(cmd); + if (env.value(QStringLiteral("XDG_SESSION_CLASS")) == QLatin1String("greeter")) { + Q_ASSERT(!m_displayServerCmd.isEmpty()); + setProgram(QStringLiteral(LIBEXEC_INSTALL_DIR "/sddm-helper-start-wayland")); + setArguments({m_displayServerCmd, m_path}); + QProcess::start(); + isWaylandGreeter = true; + } else { + setProgram(mainConfig.Wayland.SessionCommand.get()); + setArguments(QStringList{m_path}); + qInfo() << "Starting Wayland user session:" << program() << m_path; + QProcess::start(); + closeWriteChannel(); + closeReadChannel(QProcess::StandardOutput); + } } else { qCritical() << "Unable to run user session: unknown session type"; } - return waitForStarted(); + const bool started = waitForStarted(); + m_cachedProcessId = processId(); + if (started) { + return true; + } else if (isWaylandGreeter) { + // This is probably fine, we need the compositor to start first + return true; + } + + return false; + } + + void UserSession::stop() + { + if (state() != QProcess::NotRunning) { + terminate(); + const bool isGreeter = processEnvironment().value(QStringLiteral("XDG_SESSION_CLASS")) == QLatin1String("greeter"); + + // Wait longer for a session than a greeter + if (!waitForFinished(isGreeter ? 5000 : 60000)) { + kill(); + if (!waitForFinished(5000)) { + qWarning() << "Could not fully finish the process" << program(); + } + } + } else { + Q_EMIT finished(Auth::HELPER_OTHER_ERROR); + } + } + + QString UserSession::displayServerCommand() const + { + return m_displayServerCmd; + } + + void UserSession::setDisplayServerCommand(const QString &command) + { + m_displayServerCmd = command; } void UserSession::setPath(const QString& path) { @@ -72,16 +179,24 @@ return m_path; } +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + void UserSession::childModifier() { +#else void UserSession::setupChildProcess() { +#endif // Session type QString sessionType = processEnvironment().value(QStringLiteral("XDG_SESSION_TYPE")); - - // For Wayland sessions we leak the VT into the session as stdin so - // that it stays open without races - if (sessionType == QLatin1String("wayland")) { + QString sessionClass = processEnvironment().value(QStringLiteral("XDG_SESSION_CLASS")); + const bool hasDisplayServer = !m_displayServerCmd.isEmpty(); + const bool x11UserSession = sessionType == QLatin1String("x11") && sessionClass == QLatin1String("user"); + const bool waylandUserSession = sessionType == QLatin1String("wayland") && sessionClass == QLatin1String("user"); + + // When the display server is part of the session, we leak the VT into + // the session as stdin so that it stays open without races + if (hasDisplayServer || waylandUserSession) { // open VT and get the fd int vtNumber = processEnvironment().value(QStringLiteral("XDG_VTNR")).toInt(); - QString ttyString = QStringLiteral("/dev/tty%1").arg(vtNumber); + QString ttyString = VirtualTerminal::path(vtNumber); int vtFd = ::open(qPrintable(ttyString), O_RDWR | O_NOCTTY); // when this is true we'll take control of the tty @@ -101,18 +216,19 @@ if (setsid() < 0) { qCritical("Failed to set pid %lld as leader of the new session and process group: %s", QCoreApplication::applicationPid(), strerror(errno)); - exit(Auth::HELPER_OTHER_ERROR); + _exit(Auth::HELPER_OTHER_ERROR); } // take control of the tty if (takeControl) { if (ioctl(STDIN_FILENO, TIOCSCTTY) < 0) { - qCritical("Failed to take control of the tty: %s", strerror(errno)); - exit(Auth::HELPER_OTHER_ERROR); + const auto error = strerror(errno); + qCritical().nospace() << "Failed to take control of " << ttyString << " (" << QFileInfo(ttyString).owner() << "): " << error; + _exit(Auth::HELPER_TTY_ERROR); } } - VirtualTerminal::jumpToVt(vtNumber, false); + VirtualTerminal::jumpToVt(vtNumber, x11UserSession); } #ifdef Q_OS_LINUX @@ -140,8 +256,10 @@ if (bufsize == -1) bufsize = 16384; QScopedPointer buffer(static_cast(malloc(bufsize))); - if (buffer.isNull()) + if (buffer.isNull()) { + qCritical() << "Could not allocate buffer of size" << bufsize; exit(Auth::HELPER_OTHER_ERROR); + } int err = getpwnam_r(username.constData(), &pw, buffer.data(), bufsize, &rpw); if (rpw == NULL) { if (err == 0) @@ -150,13 +268,26 @@ qCritical() << "getpwnam_r(" << username << ") failed with error: " << strerror(err); exit(Auth::HELPER_OTHER_ERROR); } + + const int xauthHandle = m_xauthFile.handle(); + if (xauthHandle != -1 && fchown(xauthHandle, pw.pw_uid, pw.pw_gid) != 0) { + qCritical() << "fchown failed for" << m_xauthFile.fileName(); + exit(Auth::HELPER_OTHER_ERROR); + } + +#if defined(Q_OS_FREEBSD) + // execve() uses the environment prepared in Backend::openSession(), + // therefore environment variables which are set here are ignored. + if (setusercontext(NULL, &pw, pw.pw_uid, LOGIN_SETALL) != 0) { + qCritical() << "setusercontext(NULL, *, " << pw.pw_uid << ", LOGIN_SETALL) failed for user: " << username; + exit(Auth::HELPER_OTHER_ERROR); + } +#else if (setgid(pw.pw_gid) != 0) { qCritical() << "setgid(" << pw.pw_gid << ") failed for user: " << username; exit(Auth::HELPER_OTHER_ERROR); } -#ifdef USE_PAM - // fetch ambient groups from PAM's environment; // these are set by modules such as pam_groups.so int n_pam_groups = getgroups(0, NULL); @@ -175,13 +306,13 @@ // fetch session's user's groups int n_user_groups = 0; gid_t *user_groups = NULL; - if (-1 == getgrouplist(username.constData(), pw.pw_gid, + if (-1 == getgrouplist(pw.pw_name, pw.pw_gid, NULL, &n_user_groups)) { user_groups = new gid_t[n_user_groups]; - if ((n_user_groups = getgrouplist(username.constData(), + if ((n_user_groups = getgrouplist(pw.pw_name, pw.pw_gid, user_groups, &n_user_groups)) == -1 ) { - qCritical() << "getgrouplist(" << username << ", " << pw.pw_gid + qCritical() << "getgrouplist(" << pw.pw_name << ", " << pw.pw_gid << ") failed"; exit(Auth::HELPER_OTHER_ERROR); } @@ -206,99 +337,54 @@ delete[] pam_groups; delete[] user_groups; -#else - - if (initgroups(pw.pw_name, pw.pw_gid) != 0) { - qCritical() << "initgroups(" << pw.pw_name << ", " << pw.pw_gid << ") failed for user: " << username; - exit(Auth::HELPER_OTHER_ERROR); - } - -#endif /* USE_PAM */ - if (setuid(pw.pw_uid) != 0) { qCritical() << "setuid(" << pw.pw_uid << ") failed for user: " << username; exit(Auth::HELPER_OTHER_ERROR); } +#endif /* Q_OS_FREEBSD */ if (chdir(pw.pw_dir) != 0) { qCritical() << "chdir(" << pw.pw_dir << ") failed for user: " << username; qCritical() << "verify directory exist and has sufficient permissions"; exit(Auth::HELPER_OTHER_ERROR); } - const QString homeDir = QString::fromLocal8Bit(pw.pw_dir); - - //we cannot use setStandardError file as this code is run in the child process - //we want to redirect after we setuid so that the log file is owned by the user - - // determine stderr log file based on session type - QString sessionLog = QStringLiteral("%1/%2") - .arg(homeDir) - .arg(sessionType == QLatin1String("x11") - ? mainConfig.X11.SessionLogFile.get() - : mainConfig.Wayland.SessionLogFile.get()); - - // create the path - QFileInfo finfo(sessionLog); - QDir().mkpath(finfo.absolutePath()); - - //swap the stderr pipe of this subprcess into a file - int fd = ::open(qPrintable(sessionLog), O_WRONLY | O_CREAT | O_TRUNC, 0600); - if (fd >= 0) - { - dup2 (fd, STDERR_FILENO); - ::close(fd); - } else { - qWarning() << "Could not open stderr to" << sessionLog; - } - - //redirect any stdout to /dev/null - fd = ::open("/dev/null", O_WRONLY); - if (fd >= 0) - { - dup2 (fd, STDOUT_FILENO); - ::close(fd); - } else { - qWarning() << "Could not redirect stdout"; - } - - // set X authority for X11 sessions only - if (sessionType != QLatin1String("x11")) - return; - QString cookie = qobject_cast(parent())->cookie(); - if (!cookie.isEmpty()) { - QString file = processEnvironment().value(QStringLiteral("XAUTHORITY")); - QString display = processEnvironment().value(QStringLiteral("DISPLAY")); - qDebug() << "Adding cookie to" << file; + if (sessionClass != QLatin1String("greeter")) { + //we cannot use setStandardError file as this code is run in the child process + //we want to redirect after we setuid so that the log file is owned by the user + + // determine stderr log file based on session type + QString sessionLog = QStringLiteral("%1/%2") + .arg(QString::fromLocal8Bit(pw.pw_dir)) + .arg(sessionType == QLatin1String("x11") + ? mainConfig.X11.SessionLogFile.get() + : mainConfig.Wayland.SessionLogFile.get()); // create the path - QFileInfo finfo(file); + QFileInfo finfo(sessionLog); QDir().mkpath(finfo.absolutePath()); - QFile file_handler(file); - file_handler.open(QIODevice::Append); - file_handler.close(); - - QString cmd = QStringLiteral("%1 -f %2 -q").arg(mainConfig.X11.XauthPath.get()).arg(file); - - // execute xauth - FILE *fp = popen(qPrintable(cmd), "w"); - - // check file - if (!fp) - return; - fprintf(fp, "remove %s\n", qPrintable(display)); - fprintf(fp, "add %s . %s\n", qPrintable(display), qPrintable(cookie)); - fprintf(fp, "exit\n"); + //swap the stderr pipe of this subprcess into a file + int fd = ::open(qPrintable(sessionLog), O_WRONLY | O_CREAT | O_TRUNC, 0600); + if (fd >= 0) + { + dup2 (fd, STDERR_FILENO); + ::close(fd); + } else { + qWarning() << "Could not open stderr to" << sessionLog; + } - // close pipe - pclose(fp); + //redirect any stdout to /dev/null + fd = ::open("/dev/null", O_WRONLY); + if (fd >= 0) + { + dup2 (fd, STDOUT_FILENO); + ::close(fd); + } else { + qWarning() << "Could not redirect stdout"; + } } } - void UserSession::setCachedProcessId(qint64 pid) { - m_cachedProcessId = pid; - } - qint64 UserSession::cachedProcessId() { return m_cachedProcessId; } diff -Nru sddm-0.19.0/src/helper/UserSession.h sddm-0.20.0/src/helper/UserSession.h --- sddm-0.19.0/src/helper/UserSession.h 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/src/helper/UserSession.h 2023-06-23 12:28:38.000000000 +0000 @@ -23,42 +23,59 @@ #define SDDM_AUTH_SESSION_H #include -#include #include +#include namespace SDDM { class HelperApp; + class XOrgUserHelper; + class WaylandHelper; class UserSession : public QProcess { Q_OBJECT public: explicit UserSession(HelperApp *parent); - virtual ~UserSession(); bool start(); + void stop(); + + QString displayServerCommand() const; + void setDisplayServerCommand(const QString &command); void setPath(const QString &path); QString path() const; /*! - \brief Sets m_cachedProcessId. Needed for getting the PID of a finished UserSession - and calling HelperApp::utmpLogout - \param pid The process ID - */ - void setCachedProcessId(qint64 pid); - - /*! \brief Gets m_cachedProcessId \return The cached process ID */ qint64 cachedProcessId(); + + Q_SIGNALS: + void finished(int exitCode); + +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) protected: - void setupChildProcess(); + void setupChildProcess() override; +#endif private: + void setup(); + +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + // Don't call it directly, it will be invoked by the child process only + void childModifier(); +#endif + QString m_path { }; - qint64 m_cachedProcessId; + QTemporaryFile m_xauthFile; + QString m_displayServerCmd; + + /*! + Needed for getting the PID of a finished UserSession and calling HelperApp::utmpLogout + */ + qint64 m_cachedProcessId = -1; }; } diff -Nru sddm-0.19.0/src/helper/waylandhelper.cpp sddm-0.20.0/src/helper/waylandhelper.cpp --- sddm-0.19.0/src/helper/waylandhelper.cpp 1970-01-01 00:00:00.000000000 +0000 +++ sddm-0.20.0/src/helper/waylandhelper.cpp 2023-06-23 12:28:38.000000000 +0000 @@ -0,0 +1,140 @@ +/*************************************************************************** +* Copyright (c) 2021 Aleix Pol Gonzalez +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the +* Free Software Foundation, Inc., +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +***************************************************************************/ + +#include +#include +#include + +#include "Configuration.h" + +#include "waylandhelper.h" +#include "waylandsocketwatcher.h" +#include "VirtualTerminal.h" + +#include +#include + +namespace SDDM { + +WaylandHelper::WaylandHelper(QObject *parent) + : QObject(parent) + , m_environment(QProcessEnvironment::systemEnvironment()) + , m_watcher(new WaylandSocketWatcher(this)) +{ +} + +bool WaylandHelper::startCompositor(const QString &cmd) +{ + m_watcher->start(); + return startProcess(cmd, &m_serverProcess); +} + +void stopProcess(QProcess *process) +{ + if (process && process->state() != QProcess::NotRunning) { + qInfo() << "Stopping..." << process->program(); + process->terminate(); + if (!process->waitForFinished(5000)) { + process->kill(); + process->waitForFinished(25000); + } + process->deleteLater(); + process = nullptr; + } +} + +void WaylandHelper::stop() +{ + m_watcher->stop(); + stopProcess(m_greeterProcess); + stopProcess(m_serverProcess); +} + +bool WaylandHelper::startProcess(const QString &cmd, QProcess **p) +{ + auto *process = new QProcess(this); + process->setProcessEnvironment(m_environment); + process->setInputChannelMode(QProcess::ForwardedInputChannel); + connect(process, &QProcess::readyReadStandardError, this, [process] { + qWarning() << process->readAllStandardError(); + }); + connect(process, &QProcess::readyReadStandardOutput, this, [process] { + qInfo() << process->readAllStandardOutput(); + }); + qDebug() << "Starting Wayland process" << cmd << m_environment.value(QStringLiteral("USER")); + connect(process, QOverload::of(&QProcess::finished), + process, [](int exitCode, QProcess::ExitStatus exitStatus) { + qDebug() << "wayland compositor finished" << exitCode << exitStatus; + if (exitCode != 0 || exitStatus != QProcess::NormalExit) + QCoreApplication::instance()->quit(); + }); + + auto args = QProcess::splitCommand(cmd); + const auto program = args.takeFirst(); + process->start(program, args); + if (!process->waitForStarted(10000)) { + qWarning("Failed to start \"%s\": %s", + qPrintable(cmd), + qPrintable(process->errorString())); + return false; + } + + if (p) + *p = process; + + qDebug() << "started succesfully" << cmd; + return true; +} + +void WaylandHelper::startGreeter(const QString &cmd) +{ + auto args = QProcess::splitCommand(cmd); + + m_greeterProcess = new QProcess(this); + m_greeterProcess->setProgram(args.takeFirst()); + m_greeterProcess->setArguments(args); + connect(m_greeterProcess, &QProcess::readyReadStandardError, this, [this] { + qWarning() << m_greeterProcess->readAllStandardError(); + }); + connect(m_greeterProcess, &QProcess::readyReadStandardOutput, this, [this] { + qInfo() << m_greeterProcess->readAllStandardOutput(); + }); + connect(m_greeterProcess, QOverload::of(&QProcess::finished), + m_greeterProcess, [](int exitCode, QProcess::ExitStatus exitStatus) { + qDebug() << "wayland greeter finished" << exitCode << exitStatus; + QCoreApplication::instance()->quit(); + }); + if (m_watcher->status() == WaylandSocketWatcher::Started) { + m_environment.insert(QStringLiteral("WAYLAND_DISPLAY"), m_watcher->socketName()); + m_greeterProcess->setProcessEnvironment(m_environment); + m_greeterProcess->start(); + } else if (m_watcher->status() == WaylandSocketWatcher::Failed) { + Q_EMIT failed(); + } else { + connect(m_watcher, &WaylandSocketWatcher::failed, this, &WaylandHelper::failed); + connect(m_watcher, &WaylandSocketWatcher::started, this, [this] { + m_watcher->stop(); + m_environment.insert(QStringLiteral("WAYLAND_DISPLAY"), m_watcher->socketName()); + m_greeterProcess->setProcessEnvironment(m_environment); + m_greeterProcess->start(); + }); + } +} + +} // namespace SDDM diff -Nru sddm-0.19.0/src/helper/waylandhelper.h sddm-0.20.0/src/helper/waylandhelper.h --- sddm-0.19.0/src/helper/waylandhelper.h 1970-01-01 00:00:00.000000000 +0000 +++ sddm-0.20.0/src/helper/waylandhelper.h 2023-06-23 12:28:38.000000000 +0000 @@ -0,0 +1,54 @@ +/*************************************************************************** +* Copyright (c) 2021 Aleix Pol Gonzalez +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the +* Free Software Foundation, Inc., +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +***************************************************************************/ + +#ifndef WAYLANDHELPER_H +#define WAYLANDHELPER_H + +#include + +namespace SDDM { +class WaylandSocketWatcher; + +class WaylandHelper : public QObject +{ + Q_OBJECT +public: + explicit WaylandHelper(QObject *parent = nullptr); + + bool startCompositor(const QString &cmd); + void startGreeter(const QString &cmd); + void stop(); + +Q_SIGNALS: + void failed(); + +private: + QProcessEnvironment m_environment; + QProcess *m_serverProcess = nullptr; + QProcess *m_greeterProcess = nullptr; + WaylandSocketWatcher * const m_watcher; + + bool startProcess(const QString &cmd, QProcess **p = nullptr); +}; + +} // namespace SDDM + + + +#endif diff -Nru sddm-0.19.0/src/helper/waylandsocketwatcher.cpp sddm-0.20.0/src/helper/waylandsocketwatcher.cpp --- sddm-0.19.0/src/helper/waylandsocketwatcher.cpp 1970-01-01 00:00:00.000000000 +0000 +++ sddm-0.20.0/src/helper/waylandsocketwatcher.cpp 2023-06-23 12:28:38.000000000 +0000 @@ -0,0 +1,107 @@ +/*************************************************************************** +* Copyright (c) 2021 Pier Luigi Fiorini +* Copyright (C) 2021 Aleix Pol Gonzalez +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the +* Free Software Foundation, Inc., +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +***************************************************************************/ + +#include +#include +#include + +#include "waylandsocketwatcher.h" + +namespace SDDM { + +WaylandSocketWatcher::WaylandSocketWatcher(QObject *parent ) + : QObject(parent) + , m_runtimeDir(QDir(QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation))) +{ + m_runtimeDir.setFilter(QDir::Files | QDir::System); + m_runtimeDir.setNameFilters(QStringList() << QLatin1String("wayland-?")); +} + +WaylandSocketWatcher::Status WaylandSocketWatcher::status() const +{ + return m_status; +} + +QString WaylandSocketWatcher::socketName() const +{ + return m_socketName; +} + +void WaylandSocketWatcher::start() +{ + m_watcher = new QFileSystemWatcher(this); + + // Give the compositor some time to start + m_timer.setSingleShot(true); + m_timer.setInterval(15000); + connect(&m_timer, &QTimer::timeout, this, [this] { + // Time is up and a socket was not found + if (!m_watcher.isNull()) + m_watcher->deleteLater(); + qWarning("Wayland socket watcher timed out"); + m_status = Failed; + Q_EMIT failed(); + }); + + // Check if the socket exists + connect(m_watcher, &QFileSystemWatcher::directoryChanged, this, + [this](const QString &path) { + qDebug() << "Directory" << path << "has changed, checking for Wayland socket"; + + m_runtimeDir.refresh(); + const QFileInfoList fileInfoList = m_runtimeDir.entryInfoList(); + for (const QFileInfo &fileInfo : fileInfoList) { + if (fileInfo.ownerId() == ::getuid()) { + qDebug() << "Found Wayland socket" << fileInfo.absoluteFilePath(); + m_timer.stop(); + if (!m_watcher.isNull()) + m_watcher->deleteLater(); + m_socketName = fileInfo.fileName(); + m_status = Started; + Q_EMIT started(); + break; + } + } + }); + + // Watch for runtime directory changes + if (!m_runtimeDir.exists() || !m_watcher->addPath(m_runtimeDir.absolutePath())) { + qWarning("Cannot watch directory \"%s\" for Wayland socket", + qPrintable(m_runtimeDir.absolutePath())); + m_watcher->deleteLater(); + m_status = Failed; + Q_EMIT failed(); + } + + // Start + m_timer.start(); +} + +void WaylandSocketWatcher::stop() +{ + m_timer.stop(); + if (!m_watcher.isNull()) + m_watcher->deleteLater(); + m_watcher.clear(); + m_status = Stopped; + Q_EMIT stopped(); +} + +} // namespace SDDM diff -Nru sddm-0.19.0/src/helper/waylandsocketwatcher.h sddm-0.20.0/src/helper/waylandsocketwatcher.h --- sddm-0.19.0/src/helper/waylandsocketwatcher.h 1970-01-01 00:00:00.000000000 +0000 +++ sddm-0.20.0/src/helper/waylandsocketwatcher.h 2023-06-23 12:28:38.000000000 +0000 @@ -0,0 +1,64 @@ +/*************************************************************************** +* Copyright (c) 2021 Pier Luigi Fiorini +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the +* Free Software Foundation, Inc., +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +***************************************************************************/ + +#ifndef WAYLANDSOCKETWATCHER_H +#define WAYLANDSOCKETWATCHER_H + +#include +#include +#include +#include + +namespace SDDM { + +class WaylandSocketWatcher : public QObject +{ + Q_OBJECT +public: + enum Status { + Started, + Stopped, + Failed + }; + Q_ENUM(Status) + + explicit WaylandSocketWatcher(QObject *parent = nullptr); + + Status status() const; + QString socketName() const; + + void start(); + void stop(); + +Q_SIGNALS: + void started(); + void stopped(); + void failed(); + +private: + Status m_status = Stopped; + QDir m_runtimeDir; + QString m_socketName; + QTimer m_timer; + QPointer m_watcher; +}; + +} // namespace SDDM + +#endif // WAYLANDSOCKETWATCHER_H diff -Nru sddm-0.19.0/src/helper/xorguserhelper.cpp sddm-0.20.0/src/helper/xorguserhelper.cpp --- sddm-0.19.0/src/helper/xorguserhelper.cpp 1970-01-01 00:00:00.000000000 +0000 +++ sddm-0.20.0/src/helper/xorguserhelper.cpp 2023-06-23 12:28:38.000000000 +0000 @@ -0,0 +1,241 @@ +/*************************************************************************** +* Copyright (c) 2021 Pier Luigi Fiorini +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the +* Free Software Foundation, Inc., +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +***************************************************************************/ + +#include +#include +#include + +#include "Configuration.h" + +#include "xorguserhelper.h" + +#include +#include + +namespace SDDM { + +XOrgUserHelper::XOrgUserHelper(QObject *parent) + : QObject(parent) +{ +} + +QProcessEnvironment XOrgUserHelper::sessionEnvironment() const +{ + auto env = QProcessEnvironment::systemEnvironment(); + env.insert(QStringLiteral("DISPLAY"), m_display); + env.insert(QStringLiteral("XAUTHORITY"), m_xauth.authPath()); + env.insert(QStringLiteral("QT_QPA_PLATFORM"), QStringLiteral("xcb")); + return env; +} + +QString XOrgUserHelper::display() const +{ + return m_display; +} + +bool XOrgUserHelper::start(const QString &cmd) +{ + // Create xauthority + m_xauth.setAuthDirectory(qEnvironmentVariable("XDG_RUNTIME_DIR")); + m_xauth.setup(); + + // Start server process + if (!startServer(cmd)) + return false; + + // Setup display + startDisplayCommand(); + + return true; +} + +void XOrgUserHelper::stop() +{ + if (m_serverProcess) { + qInfo("Stopping server..."); + m_serverProcess->terminate(); + if (!m_serverProcess->waitForFinished(5000)) { + m_serverProcess->kill(); + m_serverProcess->waitForFinished(25000); + } + m_serverProcess->deleteLater(); + m_serverProcess = nullptr; + + displayFinished(); + } +} + +bool XOrgUserHelper::startProcess(const QString &cmd, + const QProcessEnvironment &env, + QProcess **p) +{ + auto args = QProcess::splitCommand(cmd); + const auto program = args.takeFirst(); + + // Make sure to forward the input of this process into the Xorg + // server, otherwise it will complain that only console users are allowed + auto *process = new QProcess(this); + process->setProcessEnvironment(env); + process->setInputChannelMode(QProcess::ForwardedInputChannel); + connect(process, &QProcess::readyReadStandardError, this, [process] { + qWarning() << process->readAllStandardError(); + }); + connect(process, &QProcess::readyReadStandardOutput, this, [process] { + qInfo() << process->readAllStandardOutput(); + }); + connect(process, QOverload::of(&QProcess::finished), + process, [](int exitCode, QProcess::ExitStatus exitStatus) { + if (exitCode != 0 || exitStatus != QProcess::NormalExit) + QCoreApplication::instance()->quit(); + }); + + process->start(program, args); + if (!process->waitForStarted(10000)) { + qWarning("Failed to start \"%s\": %s", + qPrintable(cmd), + qPrintable(process->errorString())); + return false; + } + + if (p) + *p = process; + + return true; +} + +bool XOrgUserHelper::startServer(const QString &cmd) +{ + QString serverCmd = cmd; + + // Create pipe for communicating with X server + // 0 == read from X, 1 == write to X + int pipeFds[2]; + if (::pipe(pipeFds) != 0) { + qCritical("Could not create pipe to start X server"); + return false; + } + + // Do not leak the read endpoint to the X server process + fcntl(pipeFds[0], F_SETFD, FD_CLOEXEC); + + // Server environment + // Not setting XORG_RUN_AS_USER_OK=1 will make Xorg require root privileges + // under Fedora and all distros that use their patch. + // https://src.fedoraproject.org/rpms/xorg-x11-server/blob/rawhide/f/0001-Fedora-hack-Make-the-suid-root-wrapper-always-start-.patch + // https://fedoraproject.org/wiki/Changes/XorgWithoutRootRights + auto serverEnv = QProcessEnvironment::systemEnvironment(); + serverEnv.insert(QStringLiteral("XORG_RUN_AS_USER_OK"), QStringLiteral("1")); + + // Append xauth and display fd to the command + auto args = QStringList() + << QStringLiteral("-auth") << m_xauth.authPath() + << QStringLiteral("-displayfd") << QString::number(pipeFds[1]); + + // Append VT from environment + args << QStringLiteral("vt%1").arg(serverEnv.value(QStringLiteral("XDG_VTNR"))); + + // Command string + serverCmd += QLatin1Char(' ') + args.join(QLatin1Char(' ')); + + // Start the server process + qInfo("Running server: %s", qPrintable(serverCmd)); + if (!startProcess(serverCmd, serverEnv, &m_serverProcess)) { + ::close(pipeFds[0]); + return false; + } + + // Close the other side of pipe in our process, otherwise reading + // from it may stuck even X server exit + ::close(pipeFds[1]); + + // Read the display number from the pipe + QFile readPipe; + if (!readPipe.open(pipeFds[0], QIODevice::ReadOnly)) { + qCritical("Failed to open pipe to start X Server"); + ::close(pipeFds[0]); + return false; + } + QByteArray displayNumber = readPipe.readLine(); + if (displayNumber.size() < 2) { + // X server gave nothing (or a whitespace) + qCritical("Failed to read display number from pipe"); + ::close(pipeFds[0]); + return false; + } + displayNumber.prepend(QByteArray(":")); + displayNumber.remove(displayNumber.size() -1, 1); // trim trailing whitespace + m_display = QString::fromLocal8Bit(displayNumber); + qDebug("X11 display: %s", qPrintable(m_display)); + Q_EMIT displayChanged(m_display); + + // Generate xauthority file + // For the X server's copy, the display number doesn't matter. + // An empty file would result in no access control! + if (!m_xauth.addCookie(m_display)) { + qCritical("Failed to write xauth file"); + return false; + } + + // Close our pipe + ::close(pipeFds[0]); + + return true; +} + +void XOrgUserHelper::startDisplayCommand() +{ + auto env = QProcessEnvironment::systemEnvironment(); + env.insert(QStringLiteral("DISPLAY"), m_display); + env.insert(QStringLiteral("XAUTHORITY"), m_xauth.authPath()); + + // Set cursor + qInfo("Setting default cursor..."); + QProcess *setCursor = nullptr; + if (startProcess(QStringLiteral("xsetroot -cursor_name left_ptr"), env, &setCursor)) { + if (!setCursor->waitForFinished(1000)) { + qWarning() << "Could not setup default cursor"; + setCursor->kill(); + } + setCursor->deleteLater(); + } + + // Display setup script + auto cmd = mainConfig.X11.DisplayCommand.get(); + qInfo("Running display setup script: %s", qPrintable(cmd)); + QProcess *displayScript = nullptr; + if (startProcess(cmd, env, &displayScript)) { + if (!displayScript->waitForFinished(30000)) + displayScript->kill(); + displayScript->deleteLater(); + } +} + +void XOrgUserHelper::displayFinished() +{ + auto cmd = mainConfig.X11.DisplayStopCommand.get(); + qInfo("Running display stop script: %s", qPrintable(cmd)); + QProcess *displayStopScript = nullptr; + if (startProcess(cmd, sessionEnvironment(), &displayStopScript)) { + if (!displayStopScript->waitForFinished(5000)) + displayStopScript->kill(); + displayStopScript->deleteLater(); + } +} + +} // namespace SDDM diff -Nru sddm-0.19.0/src/helper/xorguserhelper.h sddm-0.20.0/src/helper/xorguserhelper.h --- sddm-0.19.0/src/helper/xorguserhelper.h 1970-01-01 00:00:00.000000000 +0000 +++ sddm-0.20.0/src/helper/xorguserhelper.h 2023-06-23 12:28:38.000000000 +0000 @@ -0,0 +1,61 @@ +/*************************************************************************** +* Copyright (c) 2021 Pier Luigi Fiorini +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the +* Free Software Foundation, Inc., +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +***************************************************************************/ + +#ifndef XORGUSERHELPER_H +#define XORGUSERHELPER_H + +#include + +#include "XAuth.h" + +namespace SDDM { + +class XOrgUserHelper : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString display READ display NOTIFY displayChanged) +public: + explicit XOrgUserHelper(QObject *parent = nullptr); + + /// @returns the system environment plus the variables we need here + QProcessEnvironment sessionEnvironment() const; + + QString display() const; + + bool start(const QString &cmd); + void stop(); + +Q_SIGNALS: + void displayChanged(const QString &display); + +private: + QString m_display = QStringLiteral(":0"); + XAuth m_xauth; + QProcess *m_serverProcess = nullptr; + + bool startProcess(const QString &cmd, const QProcessEnvironment &env, + QProcess **p = nullptr); + bool startServer(const QString &cmd); + void startDisplayCommand(); + void displayFinished(); +}; + +} // namespace SDDM + +#endif // XORGUSERHELPER_H diff -Nru sddm-0.19.0/test/CMakeLists.txt sddm-0.20.0/test/CMakeLists.txt --- sddm-0.19.0/test/CMakeLists.txt 2020-11-03 09:55:31.000000000 +0000 +++ sddm-0.20.0/test/CMakeLists.txt 2023-06-23 12:28:38.000000000 +0000 @@ -6,4 +6,10 @@ add_executable(ConfigurationTest ${ConfigurationTest_SRCS}) add_test(NAME Configuration COMMAND ConfigurationTest) -target_link_libraries(ConfigurationTest Qt5::Core Qt5::Test) +target_link_libraries(ConfigurationTest Qt${QT_MAJOR_VERSION}::Core Qt${QT_MAJOR_VERSION}::Test) + +set(QMLThemeConfigTest_SRCS QMLThemeConfigTest.cpp ../src/common/ThemeConfig.cpp ../src/common/ThemeConfig.h) +add_executable(QMLThemeConfigTest ${QMLThemeConfigTest_SRCS}) +target_include_directories(QMLThemeConfigTest PRIVATE ../src/common/) +add_test(NAME QMLThemeConfig COMMAND QMLThemeConfigTest -platform offscreen -input ${CMAKE_CURRENT_SOURCE_DIR}/QMLThemeConfigTest.qml WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) +target_link_libraries(QMLThemeConfigTest PRIVATE Qt${QT_MAJOR_VERSION}::Quick Qt${QT_MAJOR_VERSION}::QuickTest) diff -Nru sddm-0.19.0/test/QMLThemeConfigTest.cpp sddm-0.20.0/test/QMLThemeConfigTest.cpp --- sddm-0.19.0/test/QMLThemeConfigTest.cpp 1970-01-01 00:00:00.000000000 +0000 +++ sddm-0.20.0/test/QMLThemeConfigTest.cpp 2023-06-23 12:28:38.000000000 +0000 @@ -0,0 +1,39 @@ +/*************************************************************************** +* Copyright (c) 2023 Fabian Vogt +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the +* Free Software Foundation, Inc., +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +***************************************************************************/ + +#include +#include +#include + +#include "ThemeConfig.h" + +class Setup : public QObject +{ + Q_OBJECT +public slots: + void qmlEngineAvailable(QQmlEngine *engine) + { + auto *config = new SDDM::ThemeConfig(QStringLiteral("theme.conf"), this); + engine->rootContext()->setContextProperty(QStringLiteral("config"), config); + } +}; + +QUICK_TEST_MAIN_WITH_SETUP(QMLThemeConfigTest, Setup) + +#include "QMLThemeConfigTest.moc" diff -Nru sddm-0.19.0/test/QMLThemeConfigTest.qml sddm-0.20.0/test/QMLThemeConfigTest.qml --- sddm-0.19.0/test/QMLThemeConfigTest.qml 1970-01-01 00:00:00.000000000 +0000 +++ sddm-0.20.0/test/QMLThemeConfigTest.qml 2023-06-23 12:28:38.000000000 +0000 @@ -0,0 +1,67 @@ +/*************************************************************************** +* Copyright (c) 2023 Fabian Vogt +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the +* Free Software Foundation, Inc., +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +***************************************************************************/ + +import QtQuick 2.3 +import QtTest 1.0 + +TestCase { + name: "QMLThemeConfigTest" + + function test_keys() { + let keys = Object.keys(config); + compare(keys.indexOf("doesnotexist"), -1); + verify(keys.indexOf("someInteger") >= 0); + keys = config.keys(); + compare(keys.indexOf("doesnotexist"), -1); + verify(keys.indexOf("someInteger") >= 0); + } + + // Everything is a string + function test_propertyAPI() { + compare(config.doesnotexist, undefined); + compare(config.someTrueBool, "yes"); + compare(!!config.someTrueBool, true); + compare(config.someFalseBool, "false"); + // "false" as a string is truthy! + compare(!!config.someFalseBool, true); + compare(config.someInteger, "042"); + compare(+config.someInteger, 42); + compare(config.someRealNumber, "01.5"); + compare(+config.someRealNumber, 1.5); + compare(config.someString, "Pie/180"); + } + + // Strings are converted to specific types + function test_typedAPI() { + compare(config.stringValue("doesnotexist"), ""); + compare(config.boolValue("someTrueBool"), true); + compare(config.boolValue("someFalseBool"), false); + // "false" as a string is truthy! + compare(!!config.someFalseBool, true); + compare(config.stringValue("someInteger"), "042"); + compare(config.intValue("someInteger"), 42); + compare(config.realValue("someRealNumber"), 1.5); + // conversion fails -> 0 + compare(config.intValue("someRealNumber"), 0); + compare(config.stringValue("someString"), "Pie/180"); + // conversion fails -> 0 + compare(config.intValue("someString"), 0); + compare(config.realValue("someString"), 0); + } +} diff -Nru sddm-0.19.0/test/theme.conf sddm-0.20.0/test/theme.conf --- sddm-0.19.0/test/theme.conf 1970-01-01 00:00:00.000000000 +0000 +++ sddm-0.20.0/test/theme.conf 2023-06-23 12:28:38.000000000 +0000 @@ -0,0 +1,6 @@ +[General] +someTrueBool=yes +someFalseBool=false +someInteger=042 +someRealNumber=01.5 +someString="Pie/180"