SPS · log · files · refs · tags · releases · people · languages · GitHub · commit
82b32d4
setup: set user and root passwords; start live Plasma via kwin.
Parents
Stat
Makefile | 5 +- bin/mkiso | 35 +++++-- bin/mkpkg | 57 +++++++++-- bin/setup | 233 +++++++++++++++++++++++++++++++++++++++--- lib/mkiso/initrd-init | 18 +++- lib/mkiso/live-bins | 2 + lib/mkiso/live-plasma | 54 +++++++++- lib/mkiso/live-plasma-session | 49 +++++++++ lib/mkiso/live-rc | 16 ++- lib/mkiso/plasma-bins | 1 + lib/setup/services | 2 + man/setup.1 | 26 ++++- tests/test_mkiso.sh | 27 +++++ tests/test_setup.sh | 29 ++++++ 14 files changed, 513 insertions(+), 41 deletions(-)
Diff
diff --git a/Makefile b/Makefile
index 87f5abe..c4e3591 100644
--- a/Makefile
+++ b/Makefile
@@ -30,7 +30,8 @@ lint:
$(POSIX_SHELL) -n $$library; \
done
@set -e; for script in lib/mkiso/initrd-init lib/mkiso/live-rc \
- lib/mkiso/live-plasma lib/mkiso/live-init lib/mkiso/udhcpc-script; do \
+ lib/mkiso/live-plasma lib/mkiso/live-plasma-session \
+ lib/mkiso/live-init lib/mkiso/udhcpc-script; do \
[ -f $$script ] || continue; \
$(POSIX_SHELL) -n $$script; \
done
@@ -173,7 +174,7 @@ install:
@set -e; for f in lib/mkiso/*; do \
[ -f "$$f" ] || continue; \
case $$f in \
- */live-init|*/initrd-init|*/live-rc|*/live-plasma|*/udhcpc-script) \
+ */live-init|*/initrd-init|*/live-rc|*/live-plasma|*/live-plasma-session|*/udhcpc-script) \
install -m 0755 $$f "$(DESTDIR)$(LIBDIR)/mkiso/" ;; \
*) \
install -m 0644 $$f "$(DESTDIR)$(LIBDIR)/mkiso/" ;; \
diff --git a/bin/mkiso b/bin/mkiso
index f1390f7..f32cce3 100755
--- a/bin/mkiso
+++ b/bin/mkiso
@@ -540,9 +540,8 @@ mkiso_stamp_os_release()
>"$mkiso_live/etc/os-release"
}
-# Overlay extra's Distro PNG and about-distro files after host Plasma trees
-# so KInfoCenter and plasma-welcome on the live disc match an installed
-# splux-base, not the builder's /etc/xdg.
+# Overlay extra's Distro PNG and a hardcoded plasma-welcome intro so the
+# live disc does not read /etc/os-release for the first Welcome paragraph.
mkiso_stamp_distro_branding()
{
mkiso_brand=
@@ -551,29 +550,34 @@ mkiso_stamp_distro_branding()
then
mkiso_brand=$mkiso_extra/base/splux-base/files
fi
- [ -n "$mkiso_brand" ] || return 0
mkdir -p "$mkiso_live/usr/share/pixmaps" \
"$mkiso_live/usr/share/icons/hicolor/512x512/apps" \
"$mkiso_live/etc/xdg" \
"$mkiso_live/usr/share/plasma/plasma-welcome"
- if [ -f "$mkiso_brand/sps.png" ]; then
+ if [ -n "$mkiso_brand" ] && [ -f "$mkiso_brand/sps.png" ]; then
cp "$mkiso_brand/sps.png" "$mkiso_live/usr/share/pixmaps/splux.png"
cp "$mkiso_brand/sps.png" "$mkiso_live/usr/share/pixmaps/sps.png"
cp "$mkiso_brand/sps.png" \
"$mkiso_live/usr/share/icons/hicolor/512x512/apps/splux.png"
fi
- if [ -f "$mkiso_brand/kcm-about-distrorc" ]; then
+ if [ -n "$mkiso_brand" ] && [ -f "$mkiso_brand/kcm-about-distrorc" ]; then
cp "$mkiso_brand/kcm-about-distrorc" \
"$mkiso_live/etc/xdg/kcm-about-distrorc"
fi
- if [ -f "$mkiso_brand/intro-customization.desktop" ]; then
- cp "$mkiso_brand/intro-customization.desktop" \
- "$mkiso_live/usr/share/plasma/plasma-welcome/intro-customization.desktop"
- fi
- if [ -f "$mkiso_brand/os-release" ]; then
+ if [ -n "$mkiso_brand" ] && [ -f "$mkiso_brand/os-release" ]; then
cp "$mkiso_brand/os-release" "$mkiso_live/etc/os-release"
fi
+ # Hardcoded. plasma-welcome prepends this Name; it must not fall
+ # back to PRETTY_NAME from os-release.
+ cat >"$mkiso_live/usr/share/plasma/plasma-welcome/intro-customization.desktop" <<'EOF'
+[Desktop Entry]
+Type=Application
+Name=Welcome to the SPS Linux operating system running KDE Plasma
+Comment=SPS Linux
+Icon=file:/usr/share/pixmaps/sps.png
+URL=https://splux.robertflexx.dev
+EOF
}
# Fail the ISO build rather than ship a disc that respawns getty with
@@ -914,6 +918,11 @@ Autolock=false
LockOnResume=false
LockOnStart=false
Timeout=0
+EOF
+ mkdir -p "$mkiso_live/root/.config"
+ cat >"$mkiso_live/root/.config/startkderc" <<'EOF'
+[General]
+systemdBoot=false
EOF
# Host /etc/dbus-1 may already contain system-local.conf. live-plasma
# used to skip the root-user override when that file existed, which
@@ -957,6 +966,10 @@ mkiso_install_pid1()
"$mkiso_live/etc/modprobe.d"
install -m 0755 "$mkiso_libdir/live-rc" "$mkiso_live/etc/sps/live-rc"
install -m 0755 "$mkiso_libdir/live-plasma" "$mkiso_live/etc/sps/live-plasma"
+ if [ -f "$mkiso_libdir/live-plasma-session" ]; then
+ install -m 0755 "$mkiso_libdir/live-plasma-session" \
+ "$mkiso_live/etc/sps/live-plasma-session"
+ fi
printf '%s\n' "$mkiso_init" >"$mkiso_live/etc/sps/init"
printf '%s\n' "$mkiso_session" >"$mkiso_live/etc/sps/live-session"
diff --git a/bin/mkpkg b/bin/mkpkg
index 25c2085..4a80419 100755
--- a/bin/mkpkg
+++ b/bin/mkpkg
@@ -840,6 +840,19 @@ if [ -n "${SPS_ROOT:-}" ] && [ "$SPS_ROOT" != / ]; then
export CXX
fi
+ # Autoconf sources CONFIG_SITE before AC_CHECK_LIB. AM_PATH_ALSA
+ # (alsa-utils) only adds -L when --with-alsa-prefix is set; this
+ # covers packages that do not have that switch.
+ {
+ printf '%s\n' '# generated by mkpkg for SPS_ROOT'
+ printf ': "${CPPFLAGS=%s}"\n' "$target_cflags"
+ printf ': "${CFLAGS=%s}"\n' "$target_cflags"
+ printf ': "${CXXFLAGS=%s}"\n' "$target_cflags"
+ printf ': "${LDFLAGS=%s}"\n' "$target_ldflags"
+ } >"$build_dir/run/config.site"
+ CONFIG_SITE=$build_dir/run/config.site
+ export CONFIG_SITE
+
say "using $target_root for headers, libraries, and pkg-config"
fi
@@ -849,14 +862,20 @@ run_phase()
phase_file=$build_dir/run/$phase_name.sh
[ -s "$phase_file" ] || return 0
say "running $phase_name phase"
- if [ -n "$run_env" ]; then
- if ! (CDPATH= cd "$WORK" && env $run_env sh -eu "$phase_file"); then
- die_status 10 "$phase_name phase failed"
- fi
- else
- if ! (CDPATH= cd "$WORK" && sh -eu "$phase_file"); then
- die_status 10 "$phase_name phase failed"
+ if ! (
+ CDPATH=
+ cd "$WORK" || exit 1
+ if [ -n "$build_root" ]; then
+ PATH=${wrapper_bin:+$wrapper_bin:}$build_root/usr/bin:$build_root/bin:$PATH
+ SYSROOT=$build_root
+ export PATH SYSROOT
+ elif [ -n "$wrapper_bin" ]; then
+ PATH=$wrapper_bin:$PATH
+ export PATH
fi
+ sh -eu "$phase_file"
+ ); then
+ die_status 10 "$phase_name phase failed"
fi
}
@@ -866,6 +885,30 @@ run_phase configure
run_phase build
run_phase install
+# -lfoo needs libfoo.so. Some DESTDIR installs only ship libfoo.so.N, or
+# leave a GNU ld script whose GROUP path is /usr/lib64 on the live ISO.
+if [ -d "$stage_dir" ]; then
+ find "$stage_dir" \( -type f -o -type l \) -name 'lib*.so.[0-9]*' |
+ while IFS= read -r sofile || [ -n "$sofile" ]
+ do
+ [ -n "$sofile" ] || continue
+ soname=${sofile##*/}
+ so_dir=${sofile%/*}
+ base=${soname%%.so.*}.so
+ [ "$base" != "$soname" ] || continue
+ link=$so_dir/$base
+ if [ -f "$link" ] && [ ! -L "$link" ] &&
+ grep -qE 'GROUP|INPUT' "$link" 2>/dev/null
+ then
+ ln -sf "$soname" "$link"
+ continue
+ fi
+ if [ ! -e "$link" ] && [ ! -L "$link" ]; then
+ ln -sf "$soname" "$link"
+ fi
+ done
+fi
+
if [ -e "$stage_dir/.SPS" ] || [ -L "$stage_dir/.SPS" ]; then
die_status 10 "install phase attempted to provide reserved .SPS metadata"
fi
diff --git a/bin/setup b/bin/setup
index dac3583..b920e2a 100755
--- a/bin/setup
+++ b/bin/setup
@@ -53,6 +53,10 @@ options:
--shell PATH login shell for the first user
--user NAME first unprivileged user (uid 1000)
--user-gecos TEXT
+ --user-password-file FILE hash the first line as the user password
+ --root-password-file FILE hash the first line as the root password
+ --user-password-hash HASH already-hashed user shadow field
+ --root-password-hash HASH already-hashed root shadow field
--extra PKG[,PKG...] individual packages in addition to sets
--ssh yes|no include the ssh set (OpenSSH)
--printing yes|no include the printing set (CUPS)
@@ -165,6 +169,8 @@ setup_locale=
setup_shell=
setup_user=
setup_user_gecos=
+setup_user_password_hash=
+setup_root_password_hash=
setup_extra=
setup_ssh=
setup_printing=
@@ -231,6 +237,109 @@ setup_append_csv()
eval "$setup_dest=\"\$(printf '%s' \"\$setup_old\" | sed 's/^ //')\""
}
+setup_hash_password_stdin()
+{
+ if command -v openssl >/dev/null 2>&1; then
+ openssl passwd -6 -stdin
+ return $?
+ fi
+ if command -v python3 >/dev/null 2>&1; then
+ python3 -c 'import crypt,sys
+p=sys.stdin.read().rstrip("\n")
+print(crypt.crypt(p, crypt.mksalt(crypt.METHOD_SHA512)))'
+ return $?
+ fi
+ if command -v busybox >/dev/null 2>&1; then
+ busybox cryptpw -m sha512 "$(cat)"
+ return $?
+ fi
+ return 1
+}
+
+setup_hash_password_file()
+{
+ setup_pwf=$1
+ [ -f "$setup_pwf" ] && [ -r "$setup_pwf" ] ||
+ setup_fail "$SPS_EX_NOTFOUND" "cannot read password file: $setup_pwf"
+ setup_pw=$(sed -n '1p' "$setup_pwf")
+ [ -n "$setup_pw" ] ||
+ setup_fail "$SPS_EX_USAGE" "password file is empty: $setup_pwf"
+ printf '%s\n' "$setup_pw" | setup_hash_password_stdin
+}
+
+setup_shadow_set_hash()
+{
+ setup_sh_file=$1
+ setup_sh_user=$2
+ setup_sh_hash=$3
+ [ -f "$setup_sh_file" ] || return 0
+ [ -n "$setup_sh_hash" ] || return 0
+ setup_sh_tmp=$setup_sh_file.tmp
+ setup_u=$setup_sh_user setup_h=$setup_sh_hash awk -F: '
+ BEGIN { OFS = ":" }
+ $1 == ENVIRON["setup_u"] { $2 = ENVIRON["setup_h"] }
+ { print }
+ ' "$setup_sh_file" >"$setup_sh_tmp" || return 1
+ mv "$setup_sh_tmp" "$setup_sh_file"
+ chmod 0640 "$setup_sh_file" 2>/dev/null || chmod 0600 "$setup_sh_file" || :
+}
+
+setup_group_ensure()
+{
+ setup_gf=$1
+ setup_gn=$2
+ setup_gid=$3
+ [ -f "$setup_gf" ] || return 0
+ grep -q "^$setup_gn:" "$setup_gf" && return 0
+ printf '%s:x:%s:\n' "$setup_gn" "$setup_gid" >>"$setup_gf"
+}
+
+setup_group_add_user()
+{
+ setup_gf=$1
+ setup_gn=$2
+ setup_gu=$3
+ [ -f "$setup_gf" ] || return 0
+ grep -q "^$setup_gn:" "$setup_gf" || return 0
+ setup_g_tmp=$setup_gf.tmp
+ setup_g=$setup_gn setup_u=$setup_gu awk -F: '
+ BEGIN { OFS = ":" }
+ $1 == ENVIRON["setup_g"] {
+ if ($4 == "") $4 = ENVIRON["setup_u"]
+ else {
+ n = split($4, a, ",")
+ found = 0
+ for (i = 1; i <= n; i++) if (a[i] == ENVIRON["setup_u"]) found = 1
+ if (!found) $4 = $4 "," ENVIRON["setup_u"]
+ }
+ }
+ { print }
+ ' "$setup_gf" >"$setup_g_tmp" || return 1
+ mv "$setup_g_tmp" "$setup_gf"
+}
+
+setup_ask_password_pair()
+{
+ setup_pw_title=$1
+ setup_pw_prompt=$2
+ setup_p1=$(setup_dialog --title "$setup_pw_title" \
+ --passwordbox "$setup_pw_prompt" 9 50) ||
+ setup_fail "$SPS_EX_USAGE" "cancelled"
+ setup_p2=$(setup_dialog --title "$setup_pw_title" \
+ --passwordbox "Repeat the password." 9 50) ||
+ setup_fail "$SPS_EX_USAGE" "cancelled"
+ if [ "$setup_p1" != "$setup_p2" ]; then
+ setup_fail "$SPS_EX_USAGE" "passwords do not match"
+ fi
+ if [ -z "$setup_p1" ]; then
+ printf '%s\n' ''
+ return 0
+ fi
+ printf '%s\n' "$setup_p1" | setup_hash_password_stdin ||
+ setup_fail "$SPS_EX_PACKAGE" \
+ "cannot hash passwords (need openssl passwd, python3, or busybox cryptpw)"
+}
+
while [ "$#" -gt 0 ]; do
case $1 in
-h|--help)
@@ -307,6 +416,32 @@ while [ "$#" -gt 0 ]; do
setup_user_gecos=$2
shift 2
;;
+ --user-password-file)
+ setup_need_value "$@"
+ setup_user_password_hash=$(setup_hash_password_file "$2") ||
+ setup_fail "$SPS_EX_PACKAGE" "cannot hash $2"
+ [ -n "$setup_user_password_hash" ] ||
+ setup_fail "$SPS_EX_USAGE" "user password hash was empty"
+ shift 2
+ ;;
+ --root-password-file)
+ setup_need_value "$@"
+ setup_root_password_hash=$(setup_hash_password_file "$2") ||
+ setup_fail "$SPS_EX_PACKAGE" "cannot hash $2"
+ [ -n "$setup_root_password_hash" ] ||
+ setup_fail "$SPS_EX_USAGE" "root password hash was empty"
+ shift 2
+ ;;
+ --user-password-hash)
+ setup_need_value "$@"
+ setup_user_password_hash=$2
+ shift 2
+ ;;
+ --root-password-hash)
+ setup_need_value "$@"
+ setup_root_password_hash=$2
+ shift 2
+ ;;
--flatpak)
setup_need_value "$@"
setup_flatpak=$2
@@ -588,6 +723,14 @@ setup_load_answers()
shell) [ -n "$setup_shell" ] || setup_shell=$setup_av ;;
user) [ -n "$setup_user" ] || setup_user=$setup_av ;;
user-gecos) [ -n "$setup_user_gecos" ] || setup_user_gecos=$setup_av ;;
+ user-password-hash)
+ [ -n "$setup_user_password_hash" ] ||
+ setup_user_password_hash=$setup_av
+ ;;
+ root-password-hash)
+ [ -n "$setup_root_password_hash" ] ||
+ setup_root_password_hash=$setup_av
+ ;;
extra) setup_append_csv setup_extra "$setup_av" "$setup_extra" ;;
ssh) [ -n "$setup_ssh" ] || setup_ssh=$setup_av ;;
printing) [ -n "$setup_printing" ] || setup_printing=$setup_av ;;
@@ -1060,6 +1203,15 @@ The live image and auto drivers use nouveau. This extra set is the proprietary/o
setup_fail "$SPS_EX_USAGE" "cancelled"
fi
+ if [ -n "$setup_user" ] && [ -z "$setup_user_password_hash" ]; then
+ setup_user_password_hash=$(setup_ask_password_pair "User password" \
+ "Password for $setup_user. Leave empty to lock the account until you run passwd after boot.")
+ fi
+ if [ -z "$setup_root_password_hash" ]; then
+ setup_root_password_hash=$(setup_ask_password_pair "Root password" \
+ "Password for root. Leave empty to keep root locked.")
+ fi
+
if [ -z "$setup_shell" ]; then
setup_sh_args=
if [ -f "$setup_dir/shells" ]; then
@@ -1425,6 +1577,16 @@ setup_summary()
printf 'Locale: %s\n' "$setup_locale"
printf 'Shell: %s\n' "$setup_shell"
printf 'User: %s\n' "${setup_user:-<none>}"
+ if [ -n "$setup_user_password_hash" ]; then
+ printf 'User pw: set\n'
+ elif [ -n "$setup_user" ]; then
+ printf 'User pw: locked\n'
+ fi
+ if [ -n "$setup_root_password_hash" ]; then
+ printf 'Root pw: set\n'
+ else
+ printf 'Root pw: locked\n'
+ fi
printf 'Sets: %s\n' "${setup_enable:-<none>}"
printf 'Extra: %s\n' "${setup_extra:-<none>}"
printf 'Flatpak: %s\n' "$setup_flatpak"
@@ -1528,21 +1690,33 @@ setup_apply_config()
# prefixes so it could index the copied trees before first boot.
setup_write_repos_conf ""
+ if [ ! -f "$setup_etc/passwd" ]; then
+ printf '%s\n' \
+ 'root:x:0:0:root:/root:/bin/sh' \
+ 'nobody:x:65534:65534:Unprivileged User:/:/usr/bin/nologin' \
+ >"$setup_etc/passwd"
+ printf '%s\n' 'root:x:0:' 'wheel:x:10:' 'audio:x:17:' \
+ 'video:x:18:' 'nogroup:x:65534:' \
+ >"$setup_etc/group"
+ printf '%s\n' \
+ 'root:!:0:0:99999:7:::' \
+ 'nobody:!:0:0:99999:7:::' \
+ >"$setup_etc/shadow"
+ fi
+ setup_group_ensure "$setup_etc/group" wheel 10
+ setup_group_ensure "$setup_etc/group" audio 17
+ setup_group_ensure "$setup_etc/group" video 18
+ setup_group_ensure "$setup_etc/group" input 97
+ setup_group_ensure "$setup_etc/group" kvm 36
+ setup_group_ensure "$setup_etc/group" users 100
+ if [ -f "$setup_etc/shadow" ] && ! grep -q '^root:' "$setup_etc/shadow"
+ then
+ printf '%s\n' 'root:!:0:0:99999:7:::' >>"$setup_etc/shadow"
+ fi
+
if [ -n "$setup_user" ]; then
setup_home=/home/$setup_user
mkdir -p "$setup_root$setup_home"
- if [ ! -f "$setup_etc/passwd" ]; then
- printf '%s\n' \
- 'root:x:0:0:root:/root:/bin/sh' \
- 'nobody:x:65534:65534:Unprivileged User:/:/usr/bin/nologin' \
- >"$setup_etc/passwd"
- printf '%s\n' 'root:x:0:' 'nogroup:x:65534:' \
- >"$setup_etc/group"
- printf '%s\n' \
- 'root:!:0:0:99999:7:::' \
- 'nobody:!:0:0:99999:7:::' \
- >"$setup_etc/shadow"
- fi
if ! grep -q "^$setup_user:" "$setup_etc/passwd"; then
printf '%s:x:1000:1000:%s:%s:%s\n' \
"$setup_user" \
@@ -1558,12 +1732,43 @@ setup_apply_config()
! grep -q "^$setup_user:" "$setup_etc/shadow"; then
printf '%s:!:0:0:99999:7:::\n' "$setup_user" >>"$setup_etc/shadow"
fi
- cat >"$setup_root/root/sps-user.txt" <<EOF
+ for setup_g in wheel audio video input kvm users
+ do
+ setup_group_add_user "$setup_etc/group" "$setup_g" "$setup_user"
+ done
+ chown -R 1000:1000 "$setup_root$setup_home" 2>/dev/null || :
+ chmod 0750 "$setup_root$setup_home" 2>/dev/null || :
+ setup_shadow_set_hash "$setup_etc/shadow" "$setup_user" \
+ "$setup_user_password_hash"
+ if [ -n "$setup_user_password_hash" ]; then
+ cat >"$setup_root/root/sps-user.txt" <<EOF
+The account '$setup_user' (uid 1000) was recorded in /etc/passwd.
+The password was set during setup. The account is in wheel, audio, video,
+input, kvm, and users when those groups exist.
+EOF
+ else
+ cat >"$setup_root/root/sps-user.txt" <<EOF
The account '$setup_user' (uid 1000) was recorded in /etc/passwd.
The password is locked. From a live root run:
passwd $setup_user
EOF
+ fi
+ fi
+ setup_shadow_set_hash "$setup_etc/shadow" root "$setup_root_password_hash"
+ if [ -n "$setup_root_password_hash" ]; then
+ mkdir -p "$setup_root/root"
+ if [ -f "$setup_root/root/sps-user.txt" ]; then
+ printf '%s\n' 'The root password was set during setup.' \
+ >>"$setup_root/root/sps-user.txt"
+ else
+ printf '%s\n' 'The root password was set during setup.' \
+ >"$setup_root/root/sps-user.txt"
+ fi
+ elif [ ! -f "$setup_root/root/sps-user.txt" ]; then
+ mkdir -p "$setup_root/root"
+ printf '%s\n' 'The root password is locked. Run passwd after boot.' \
+ >"$setup_root/root/sps-user.txt"
fi
cat >"$setup_root/root/sps-bootloader.txt" <<EOF
@@ -1807,7 +2012,7 @@ if [ "$setup_services" = yes ] &&
then
case $setup_init in
systemd|openrc)
- printf '%s: enabling services for init %s\n' \
+ printf '%s: enabling services after user configuration, init %s\n' \
"$sps_prog" "$setup_init" >&2
setup_svc_list=$(setup_service_enable_present \
"$setup_root" "$setup_init") || setup_svc_list=
diff --git a/lib/mkiso/initrd-init b/lib/mkiso/initrd-init
index ed7f968..4ff8478 100755
--- a/lib/mkiso/initrd-init
+++ b/lib/mkiso/initrd-init
@@ -45,11 +45,25 @@ for mod in loop squashfs overlay isofs vfat nls_cp437 nls_iso8859-1 \
usb-storage xhci-pci ehci-pci uhci-hcd sr_mod sd_mod ahci nvme \
virtio_blk virtio_pci virtio_scsi virtio_net virtio_console \
sdhci sdhci-pci mmc_block \
- drm drm_kms_helper drm_ttm_helper ttm virtio_dma_buf virtio_gpu \
- bochs simpledrm
+ drm drm_kms_helper drm_shmem_helper drm_ttm_helper ttm virtio_dma_buf \
+ virtio_gpu bochs qxl vmwgfx
do
"$bb" modprobe "$mod" 2>/dev/null || :
done
+# simpledrm on a firmware framebuffer next to virtio_gpu is a black KWin
+# with an instant cursor. Only load it when no real KMS node appeared.
+n=0
+while [ "$n" -lt 5 ]
+do
+ if [ -e /dev/dri/card0 ] || [ -e /dev/dri/card1 ]; then
+ break
+ fi
+ n=$((n + 1))
+ "$bb" sleep 1
+done
+if [ ! -e /dev/dri/card0 ] && [ ! -e /dev/dri/card1 ]; then
+ "$bb" modprobe simpledrm 2>/dev/null || :
+fi
found=
i=0
diff --git a/lib/mkiso/live-bins b/lib/mkiso/live-bins
index 92b2375..6afe773 100644
--- a/lib/mkiso/live-bins
+++ b/lib/mkiso/live-bins
@@ -32,3 +32,5 @@
/usr/sbin/partprobe
/sbin/dhcpcd
/usr/sbin/dhcpcd
+/usr/bin/openssl
+/bin/openssl
diff --git a/lib/mkiso/live-plasma b/lib/mkiso/live-plasma
index a5207b4..bcf066b 100644
--- a/lib/mkiso/live-plasma
+++ b/lib/mkiso/live-plasma
@@ -63,9 +63,16 @@ if [ ! -s /root/.config/baloofilerc ]; then
printf '%s\n' '[Basic Settings]' 'Indexing-Enabled=false' \
>/root/.config/baloofilerc
fi
+# startplasma-wayland prefers systemd --user. The live disc is busybox
+# PID 1, so force the classic plasma_session path if we fall back to it.
+if [ ! -s /root/.config/startkderc ]; then
+ printf '%s\n' '[General]' 'systemdBoot=false' >/root/.config/startkderc
+fi
if command -v fc-cache >/dev/null 2>&1; then
fc-cache >/dev/null 2>&1 || :
fi
+export LIBSEAT_BACKEND=seatd
+export SEATD_SOCK=/run/seatd.sock
# Live NVIDIA is nouveau. Keep the proprietary stack unloaded.
modprobe nouveau 2>/dev/null || :
@@ -151,6 +158,33 @@ if [ ! -e /dev/dri/card0 ] && [ ! -e /dev/dri/card1 ]; then
printf '%s\n' "SPS: use tty2 (Ctrl-Alt-F2) for a shell."
ls /dev/dri 2>/dev/null || :
fi
+# Prefer a real KMS device. simpledrm plus virtio_gpu leaves KWin on the
+# firmware framebuffer: black screen with a cursor that appears immediately.
+sps_drm=
+for sps_card in /sys/class/drm/card[0-9] /sys/class/drm/card[1-9][0-9]
+do
+ [ -e "$sps_card" ] || continue
+ sps_node=/dev/dri/${sps_card##*/}
+ [ -e "$sps_node" ] || continue
+ sps_drv=
+ if [ -L "$sps_card/device/driver" ]; then
+ sps_drv=$(readlink "$sps_card/device/driver")
+ sps_drv=${sps_drv##*/}
+ fi
+ case $sps_drv in
+ simple-framebuffer|simpledrm) continue ;;
+ esac
+ sps_drm=$sps_node
+ break
+done
+if [ -n "$sps_drm" ]; then
+ export KWIN_DRM_DEVICES=$sps_drm
+fi
+for sps_vt in /sys/class/vtconsole/vtcon*
+do
+ [ -w "$sps_vt/bind" ] || continue
+ printf '%s\n' 0 >"$sps_vt/bind" 2>/dev/null || :
+done
if [ ! -e /usr/lib64/gbm/dri_gbm.so ] && [ ! -e /usr/lib/gbm/dri_gbm.so ]
then
printf '%s\n' "SPS: Mesa GBM backend dri_gbm.so is missing; KWin cannot use DRM."
@@ -160,9 +194,25 @@ then
printf '%s\n' "SPS: libSPIRV-Tools.so is missing; Mesa GBM will fail to open DRI."
fi
+# Give the user a console they can see if Wayland dies.
+chvt 1 2>/dev/null || :
+# startplasma-wayland starts kwin, then waits on kded/ksmserver before
+# plasmashell. That hang is a black compositor with an instant cursor.
+# Drive KWin ourselves and exec plasmashell as the session.
+if command -v kwin_wayland >/dev/null 2>&1 &&
+ [ -x /etc/sps/live-plasma-session ]
+then
+ if command -v dbus-run-session >/dev/null 2>&1; then
+ exec dbus-run-session -- kwin_wayland --drm --xwayland \
+ --no-lockscreen \
+ --exit-with-session /etc/sps/live-plasma-session \
+ >>/run/plasma.log 2>&1
+ fi
+ exec kwin_wayland --drm --xwayland --no-lockscreen \
+ --exit-with-session /etc/sps/live-plasma-session \
+ >>/run/plasma.log 2>&1
+fi
if command -v startplasma-wayland >/dev/null 2>&1; then
- # Give the user a console they can see if Wayland dies.
- chvt 1 2>/dev/null || :
# startplasma needs a *session* bus. The system bus above is not that.
if command -v dbus-run-session >/dev/null 2>&1; then
exec dbus-run-session -- startplasma-wayland \
diff --git a/lib/mkiso/live-plasma-session b/lib/mkiso/live-plasma-session
new file mode 100644
index 0000000..1d245c6
--- /dev/null
+++ b/lib/mkiso/live-plasma-session
@@ -0,0 +1,49 @@
+#!/bin/busybox sh
+# Session started by kwin_wayland --exit-with-session. When this script
+# (or plasmashell) exits, KWin exits and busybox init respawns live-plasma.
+
+PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
+PATH=/usr/lib64/qt6/bin:/usr/lib/qt6/bin:$PATH
+export PATH
+export HOME=${HOME:-/root}
+export XDG_RUNTIME_DIR=${XDG_RUNTIME_DIR:-/run/user/0}
+export XDG_SESSION_TYPE=wayland
+export XDG_SESSION_CLASS=user
+export XDG_SESSION_DESKTOP=KDE
+export XDG_CURRENT_DESKTOP=KDE
+export DESKTOP_SESSION=plasma
+
+if command -v kded6 >/dev/null 2>&1; then
+ kded6 >/run/kded6.log 2>&1 &
+fi
+if command -v kglobalacceld >/dev/null 2>&1; then
+ kglobalacceld >/run/kglobalaccel.log 2>&1 &
+elif [ -x /usr/libexec/kglobalacceld ]; then
+ /usr/libexec/kglobalacceld >/run/kglobalaccel.log 2>&1 &
+fi
+if [ -x /usr/libexec/kactivitymanagerd ]; then
+ /usr/libexec/kactivitymanagerd >/run/kactivity.log 2>&1 &
+fi
+
+if command -v plasma-welcome >/dev/null 2>&1; then
+ (
+ n=0
+ while [ "$n" -lt 20 ]; do
+ if [ -n "${WAYLAND_DISPLAY:-}" ] ||
+ [ -S "$XDG_RUNTIME_DIR/wayland-0" ]
+ then
+ break
+ fi
+ n=$((n + 1))
+ sleep 1
+ done
+ sleep 3
+ plasma-welcome
+ ) >/run/plasma-welcome.log 2>&1 &
+fi
+
+if command -v plasmashell >/dev/null 2>&1; then
+ exec plasmashell
+fi
+printf '%s\n' "SPS: plasmashell is not on this image."
+exec /bin/sh
diff --git a/lib/mkiso/live-rc b/lib/mkiso/live-rc
index 576a980..77e8449 100644
--- a/lib/mkiso/live-rc
+++ b/lib/mkiso/live-rc
@@ -69,11 +69,23 @@ fi
# or cirrus. KWin needs a DRM node; nouveau/bochs alone is not enough.
for mod in e1000 e1000e r8169 virtio_net virtio_blk virtio_pci virtio_scsi \
usbnet hid-generic usbhid atkbd \
- drm drm_kms_helper drm_ttm_helper ttm virtio_dma_buf virtio_gpu \
- nouveau bochs cirrus_qemu qxl vmwgfx i915 amdgpu
+ drm drm_kms_helper drm_shmem_helper drm_ttm_helper ttm virtio_dma_buf \
+ virtio_gpu nouveau bochs cirrus_qemu qxl vmwgfx i915 amdgpu
do
modprobe "$mod" 2>/dev/null || :
done
+n=0
+while [ "$n" -lt 5 ]
+do
+ if [ -e /dev/dri/card0 ] || [ -e /dev/dri/card1 ]; then
+ break
+ fi
+ n=$((n + 1))
+ sleep 1
+done
+if [ ! -e /dev/dri/card0 ] && [ ! -e /dev/dri/card1 ]; then
+ modprobe simpledrm 2>/dev/null || :
+fi
# udev first so virtio_net and friends have device nodes before DHCP.
if command -v udevd >/dev/null 2>&1; then
diff --git a/lib/mkiso/plasma-bins b/lib/mkiso/plasma-bins
index a5f09fe..412ab0c 100644
--- a/lib/mkiso/plasma-bins
+++ b/lib/mkiso/plasma-bins
@@ -11,6 +11,7 @@
/usr/libexec/kwin_killer_helper
/usr/bin/plasmashell
/usr/bin/plasma_session
+/usr/bin/plasma-welcome
/usr/bin/plasma_waitforname
/usr/bin/plasma-shutdown
/usr/bin/ksplashqml
diff --git a/lib/setup/services b/lib/setup/services
index 2a2b96f..333d263 100644
--- a/lib/setup/services
+++ b/lib/setup/services
@@ -15,3 +15,5 @@ bluetooth bluetooth.service multi-user.target bluetooth default
sshd sshd.service multi-user.target sshd default
cupsd cups.service multi-user.target cupsd default
NetworkManager NetworkManager.service multi-user.target NetworkManager default
+pipewire pipewire.service default.target pipewire default
+wireplumber wireplumber.service default.target wireplumber default
diff --git a/man/setup.1 b/man/setup.1
index b4a0f78..ad95db7 100644
--- a/man/setup.1
+++ b/man/setup.1
@@ -158,7 +158,31 @@ Enable the OpenSSH set.
Enable CUPS and related printing packages.
.TP
.BI --user " name"
-First unprivileged account (uid 1000). The password is left locked; see
+First unprivileged account (uid 1000). Interactive setup asks for a
+user password and a root password (empty keeps the account locked).
+Scripts should pass a file or an already-hashed shadow field; plaintext
+passwords are not accepted on the command line.
+.TP
+.BI --user-password-file " file"
+.TP
+.BI --root-password-file " file"
+Hash the first line of
+.I file
+with
+.B openssl passwd -6
+(or python3 crypt / busybox cryptpw) and store it in shadow.
+.TP
+.BI --user-password-hash " hash"
+.TP
+.BI --root-password-hash " hash"
+Already-hashed shadow field. Answers files may use
+.B user-password-hash
+and
+.B root-password-hash
+the same way. Those hashes are not written back to
+.I /etc/sps/setup-answers .
+Without a password flag, non-interactive setup leaves the account locked;
+see
.I /root/sps-user.txt .
.TP
.B --flatpak yes|no
diff --git a/tests/test_mkiso.sh b/tests/test_mkiso.sh
index 10311e6..0f36e58 100755
--- a/tests/test_mkiso.sh
+++ b/tests/test_mkiso.sh
@@ -102,6 +102,24 @@ grep -q 'Engine=none' "$layout/etc/sps/live-plasma" ||
fail 'live-plasma must disable KSplash so plasma_waitforname cannot hang'
grep -q LockOnStart "$layout/etc/sps/live-plasma" ||
fail 'live-plasma must disable kscreenlocker LockOnStart'
+grep -q 'kwin_wayland --drm' "$layout/etc/sps/live-plasma" ||
+ fail 'live-plasma must drive kwin_wayland --drm'
+[ -x "$layout/etc/sps/live-plasma-session" ] ||
+ fail 'live-plasma-session missing from layout'
+grep -q LIBSEAT_BACKEND=seatd "$layout/etc/sps/live-plasma" ||
+ fail 'live-plasma must set LIBSEAT_BACKEND=seatd'
+welcome=$layout/usr/share/plasma/plasma-welcome/intro-customization.desktop
+[ -f "$welcome" ] || fail 'plasma-welcome intro-customization missing'
+grep -qx 'Name=Welcome to the SPS Linux operating system running KDE Plasma' \
+ "$welcome" || fail 'plasma-welcome Name must be the SPS Linux sentence'
+name_line=$(grep '^Name=' "$welcome")
+case $name_line in
+ *[Ss]plux*) fail 'plasma-welcome Name must not fall back to os-release Splux' ;;
+esac
+grep -q plasma-welcome "$project_dir/lib/mkiso/plasma-bins" ||
+ fail 'plasma-bins must include plasma-welcome'
+grep -q openssl "$project_dir/lib/mkiso/live-bins" ||
+ fail 'live-bins must include openssl for setup password hashes'
grep -q virtio_gpu "$project_dir/lib/mkiso/initrd-init" ||
fail 'initrd must load virtio_gpu so Plasma has DRM before switch_root'
if grep -q 'export QT_QPA_PLATFORM=wayland' "$layout/etc/sps/live-plasma"
@@ -114,6 +132,13 @@ grep -q virtio_gpu "$layout/etc/sps/live-rc" ||
fail 'live-rc must load virtio_gpu for QEMU/virt-manager'
grep -q cirrus_qemu "$layout/etc/sps/live-rc" ||
fail 'live-rc must load cirrus_qemu for QEMU stdvga fallbacks'
+# simpledrm only after a wait; loading it beside virtio_gpu blacks KWin.
+live_rc=$layout/etc/sps/live-rc
+wait_line=$(grep -n 'sleep 1' "$live_rc" | head -1 | cut -d: -f1)
+sdrm_line=$(grep -n 'modprobe simpledrm' "$live_rc" | head -1 | cut -d: -f1)
+[ -n "$wait_line" ] && [ -n "$sdrm_line" ] &&
+ [ "$wait_line" -lt "$sdrm_line" ] ||
+ fail 'live-rc must wait for a DRM node before simpledrm'
grep -q /dev/dri/card0 "$layout/etc/sps/live-plasma" ||
fail 'live-plasma must wait for a DRM node'
grep -q /usr/share/kwin "$project_dir/lib/mkiso/plasma-trees" ||
@@ -252,6 +277,8 @@ grep -q 'using .* for headers, libraries, and pkg-config' "$project_dir/bin/mkpk
fail 'mkpkg must compile against SPS_ROOT so setup can link target libs'
grep -q write_cc_wrapper "$project_dir/bin/mkpkg" ||
fail 'mkpkg must wrap gcc so autoconf finds libraries in SPS_ROOT'
+grep -q CONFIG_SITE "$project_dir/bin/mkpkg" ||
+ fail 'mkpkg must export CONFIG_SITE so autoconf sees SPS_ROOT LDFLAGS'
grep -q 'sps_say "unpacking' "$project_dir/bin/pkin" ||
fail 'pkin must log unpacking'
grep -q sps_verbose_say "$project_dir/bin/pkin" ||
diff --git a/tests/test_setup.sh b/tests/test_setup.sh
index a433783..a7d8607 100755
--- a/tests/test_setup.sh
+++ b/tests/test_setup.sh
@@ -145,6 +145,13 @@ src update >/dev/null
[ "$(cat "$root/etc/locale.conf")" = 'LANG=C.UTF-8' ] || fail 'locale not written'
grep -q '^tester:x:1000:1000:Test User:/home/tester:/bin/sh$' \
"$root/etc/passwd" || fail 'user not recorded'
+awk -F: '$1=="tester" && $2=="!" { found=1 } END { exit !found }' \
+ "$root/etc/shadow" || fail 'user without password flags must stay locked'
+awk -F: '$1=="root" && $2=="!" { found=1 } END { exit !found }' \
+ "$root/etc/shadow" || fail 'root without password flags must stay locked'
+if grep -q 'password-hash' "$root/etc/sps/setup-answers"; then
+ fail 'setup-answers must not store password hashes'
+fi
[ -f "$root/etc/sps/setup-answers" ] || fail 'setup-answers missing'
[ -f "$root/root/sps-bootloader.txt" ] || fail 'bootloader notes missing'
[ -f "$root/root/sps-network.txt" ] || fail 'network notes missing'
@@ -155,6 +162,28 @@ grep -q 'Init is none' "$root/root/sps-services.txt" ||
grep -q 'grub-install' "$root/root/sps-bootloader.txt" ||
fail 'bootloader notes omitted grub-install warning'
+printf '%s\n' 'test-user-pw' >"$tmp/user.pw"
+printf '%s\n' 'test-root-pw' >"$tmp/root.pw"
+chmod 600 "$tmp/user.pw" "$tmp/root.pw"
+root_pw=$tmp/root-pw
+mkdir -p "$root_pw"
+"$setup" --non-interactive --no-update --target "$root_pw" --profile tiny \
+ --hostname pwtest --timezone UTC --keymap us --user tester \
+ --enable extra --user-gecos 'PW User' \
+ --user-password-file "$tmp/user.pw" \
+ --root-password-file "$tmp/root.pw" >/dev/null
+awk -F: '$1=="tester" && $2!="" && $2!="!" && $2!="*" { found=1 }
+ END { exit !found }' "$root_pw/etc/shadow" ||
+ fail 'user password file did not update shadow'
+awk -F: '$1=="root" && $2!="" && $2!="!" && $2!="*" { found=1 }
+ END { exit !found }' "$root_pw/etc/shadow" ||
+ fail 'root password file did not update shadow'
+if grep -q 'password-hash' "$root_pw/etc/sps/setup-answers"; then
+ fail 'setup-answers must not store password hashes'
+fi
+grep -q 'wheel:.*tester' "$root_pw/etc/group" ||
+ fail 'first user should be in wheel when that group exists'
+
printf '%s\n' 'setup fixture tests passed'
# Bundled live recipes: copy into the target and index locally (no Git clone).