From b86d10bbe6c251a1857f4b27896009ffcbe4c3d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ladislav=20Slez=C3=A1k?= Date: Wed, 19 Jun 2024 13:08:10 +0200 Subject: [PATCH 1/4] fix(Live): Improve interactive root password services Properly disable the systemd console output --- .../system/agama-certificate-issue.service | 1 - .../system/live-password-dialog.service | 19 +++++++++++++------ .../system/live-password-systemd.service | 14 ++++++++++---- live/root/usr/bin/live-password | 8 ++++---- 4 files changed, 27 insertions(+), 15 deletions(-) diff --git a/live/root/etc/systemd/system/agama-certificate-issue.service b/live/root/etc/systemd/system/agama-certificate-issue.service index 5623d363de..245aba998f 100644 --- a/live/root/etc/systemd/system/agama-certificate-issue.service +++ b/live/root/etc/systemd/system/agama-certificate-issue.service @@ -1,6 +1,5 @@ [Unit] Description=Generate issue file for Agama SSL certificate -Before=systemd-user-sessions.service [Service] Type=oneshot diff --git a/live/root/etc/systemd/system/live-password-dialog.service b/live/root/etc/systemd/system/live-password-dialog.service index 2e120642ca..f463684ce1 100644 --- a/live/root/etc/systemd/system/live-password-dialog.service +++ b/live/root/etc/systemd/system/live-password-dialog.service @@ -22,21 +22,28 @@ Before=serial-getty@ttyS1.service Before=serial-getty@ttyS2.service Before=serial-getty@ttysclp0.service -# start at the end to avoid overwriting the screen with systemd messages -After=agama.service -After=modprobe@drm.service - # kernel command line option ConditionKernelCommandLine=live.password_dialog [Service] Type=oneshot Environment=TERM=linux + +# disable the kernel output on the console ExecStartPre=dmesg --console-off +# disable the systemd status messages on the console +ExecStartPre=kill -SIGRTMIN+21 1 + ExecStart=live-password --dialog + +# reset the console state after closing the dialog otherwise the dialog +# content would stay on the screen +ExecStartPost=reset +# enable back the kernel output on the console ExecStartPost=dmesg --console-on -TTYReset=yes -TTYVHangup=yes +# enable back the systemd status messages on the console +ExecStartPost=kill -SIGRTMIN+20 1 + StandardInput=tty RemainAfterExit=true TimeoutSec=0 diff --git a/live/root/etc/systemd/system/live-password-systemd.service b/live/root/etc/systemd/system/live-password-systemd.service index 444d1ce9c1..eb81bae6c3 100644 --- a/live/root/etc/systemd/system/live-password-systemd.service +++ b/live/root/etc/systemd/system/live-password-systemd.service @@ -22,18 +22,24 @@ Before=serial-getty@ttyS1.service Before=serial-getty@ttyS2.service Before=serial-getty@ttysclp0.service -# start at the end to avoid overwriting the screen with systemd messages -After=agama.service -After=modprobe@drm.service - # kernel command line option ConditionKernelCommandLine=live.password_systemd [Service] Type=oneshot + +# disable the kernel output on the console ExecStartPre=dmesg --console-off +# disable the systemd status messages on the console +ExecStartPre=kill -SIGRTMIN+21 1 + ExecStart=live-password --systemd + +# enable back the kernel output on the console ExecStartPost=dmesg --console-on +# enable back the systemd status messages on the console +ExecStartPost=kill -SIGRTMIN+20 1 + StandardOutput=tty RemainAfterExit=true TimeoutSec=0 diff --git a/live/root/usr/bin/live-password b/live/root/usr/bin/live-password index 1d3f0b4ae6..1a74179717 100755 --- a/live/root/usr/bin/live-password +++ b/live/root/usr/bin/live-password @@ -16,22 +16,22 @@ TITLE="Set Login Password" # functions for entering the password in an interactive dialog confirm_exit() { - if dialog --backtitle "$BTITLE" --defaultno --yesno "Are you sure you want to cancel?" 5 40; then + if dialog --keep-tite --backtitle "$BTITLE" --defaultno --yesno "Are you sure you want to cancel?" 5 40; then exit 1 fi } msg_box() { - dialog --backtitle "$BTITLE" --msgbox "$1" 6 30 + dialog --keep-tite --backtitle "$BTITLE" --msgbox "$1" 6 30 } ask_password() { - if ! PWD1=$(dialog --title "$TITLE" --backtitle "$BTITLE" --stdout --insecure --passwordbox "Password:" 8 40); then + if ! PWD1=$(dialog --keep-tite --title "$TITLE" --backtitle "$BTITLE" --stdout --insecure --passwordbox "Password:" 8 40); then confirm_exit ask_password fi - if ! PWD2=$(dialog --title "$TITLE" --backtitle "$BTITLE" --stdout --insecure --passwordbox "Verify Password:" 8 40); then + if ! PWD2=$(dialog --keep-tite --title "$TITLE" --backtitle "$BTITLE" --stdout --insecure --passwordbox "Verify Password:" 8 40); then confirm_exit ask_password fi From 3578a594ce5e856c5a4b9852ee25d64f0ab9c945 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ladislav=20Slez=C3=A1k?= Date: Wed, 19 Jun 2024 14:32:27 +0200 Subject: [PATCH 2/4] Update live/root/usr/bin/live-password Co-authored-by: Martin Vidner --- live/root/usr/bin/live-password | 1 + 1 file changed, 1 insertion(+) diff --git a/live/root/usr/bin/live-password b/live/root/usr/bin/live-password index 1a74179717..76ebf390d9 100755 --- a/live/root/usr/bin/live-password +++ b/live/root/usr/bin/live-password @@ -16,6 +16,7 @@ TITLE="Set Login Password" # functions for entering the password in an interactive dialog confirm_exit() { + # --keep-tite is not a misspelling of "title" if dialog --keep-tite --backtitle "$BTITLE" --defaultno --yesno "Are you sure you want to cancel?" 5 40; then exit 1 fi From 3a77a147aa4f0653b57ba2d73297e1360100808b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ladislav=20Slez=C3=A1k?= Date: Wed, 19 Jun 2024 14:42:32 +0200 Subject: [PATCH 3/4] fix(service): Copy the libzypp caches and credentials to the installed system (#1357) ## Problem - The libzypp caches and credentials are not copied to the installed system - Installing any package or even simple `zypper refresh` triggers full repository reload ## Solution - Just copy the files from the Live ISO zypp repository to the installed system ## Testing - Added a new unit test - Tested manually, after installation all caches are copied and `zypper refresh` does nothing --- service/lib/agama/software/manager.rb | 46 ++++++++++++++-- service/package/rubygem-agama-yast.changes | 8 +++ service/test/agama/software/manager_test.rb | 59 +++++++++++++++++++++ 3 files changed, 109 insertions(+), 4 deletions(-) diff --git a/service/lib/agama/software/manager.rb b/service/lib/agama/software/manager.rb index 9de17bdf1b..ddb08989fd 100644 --- a/service/lib/agama/software/manager.rb +++ b/service/lib/agama/software/manager.rb @@ -201,10 +201,8 @@ def finish progress.step(_("Writing repositories to the target system")) do Yast::Pkg.SourceSaveAll Yast::Pkg.TargetFinish - # FIXME: Pkg.SourceCacheCopyTo works correctly only from the inst-sys - # (original target "/"), it does not work correctly when using - # "chroot" /run/agama/zypp, it needs to be reimplemented :-( - # Yast::Pkg.SourceCacheCopyTo(Yast::Installation.destdir) + # copy the libzypp caches to the target + copy_zypp_to_target registration.finish end end @@ -503,6 +501,46 @@ def pattern_exist?(pattern_name) !Y2Packager::Resolvable.find(kind: :pattern, name: pattern_name).empty? end + # this reimplements the Pkg.SourceCacheCopyTo call which works correctly + # only from the inst-sys (it copies the data from "/" where is actually + # the Live system package manager) + # @see https://github.com/yast/yast-pkg-bindings/blob/3d314480b70070299f90da4c6e87a5574e9c890c/src/Source_Installation.cc#L213-L267 + def copy_zypp_to_target + # copy the zypp "raw" cache + cache = File.join(TARGET_DIR, "/var/cache/zypp/raw") + if Dir.exist?(cache) + target_cache = File.join(Yast::Installation.destdir, "/var/cache/zypp") + FileUtils.mkdir_p(target_cache) + FileUtils.cp_r(cache, target_cache) + end + + # copy the "solv" cache but skip the "@System" directory because it + # contains empty installed packages (there were no installed packages + # before moving the target to "/mnt") + solv_cache = File.join(TARGET_DIR, "/var/cache/zypp/solv") + target_solv = File.join(Yast::Installation.destdir, "/var/cache/zypp/solv") + solvs = Dir.entries(solv_cache) - [".", "..", "@System"] + solvs.each do |s| + FileUtils.cp_r(File.join(solv_cache, s), target_solv) + end + + # copy the zypp credentials if present + credentials = File.join(TARGET_DIR, "/etc/zypp/credentials.d") + if Dir.exist?(credentials) + target_credentials = File.join(Yast::Installation.destdir, "/etc/zypp") + FileUtils.mkdir_p(target_credentials) + FileUtils.cp_r(credentials, target_credentials) + end + + # copy the global credentials if present + glob_credentials = File.join(TARGET_DIR, "/etc/zypp/credentials.cat") + return unless File.exist?(glob_credentials) + + target_dir = File.join(Yast::Installation.destdir, "/etc/zypp") + FileUtils.mkdir_p(target_dir) + FileUtils.copy(glob_credentials, target_dir) + end + # update the zypp repositories for the new product, either delete them # or keep them untouched # @param new_product [Agama::Software::Product] the new selected product diff --git a/service/package/rubygem-agama-yast.changes b/service/package/rubygem-agama-yast.changes index dfd5b661c7..89fa95ad10 100644 --- a/service/package/rubygem-agama-yast.changes +++ b/service/package/rubygem-agama-yast.changes @@ -1,3 +1,11 @@ +------------------------------------------------------------------- +Wed Jun 19 06:04:46 UTC 2024 - Ladislav Slezák + +- Use a different libzypp target for Agama, do not use the Live + system package management (gh#openSUSE/agama#1329) +- Properly delete the libzypp cache when changing the products + (gh#openSUSE/agama#1349) + ------------------------------------------------------------------- Thu Jun 13 10:53:27 UTC 2024 - Imobach Gonzalez Sosa diff --git a/service/test/agama/software/manager_test.rb b/service/test/agama/software/manager_test.rb index f5b7470829..ad2770c4ec 100644 --- a/service/test/agama/software/manager_test.rb +++ b/service/test/agama/software/manager_test.rb @@ -102,6 +102,10 @@ allow(Agama::Software::RepositoriesManager).to receive(:new).and_return(repositories) allow(Agama::Software::Proposal).to receive(:new).and_return(proposal) allow(Agama::ProductReader).to receive(:new).and_call_original + allow(FileUtils).to receive(:mkdir_p) + allow(FileUtils).to receive(:rm_rf) + allow(FileUtils).to receive(:cp_r) + allow(File).to receive(:exist?).and_call_original end after do @@ -359,11 +363,66 @@ describe "#finish" do it "releases the packaging system" do + allow(subject).to receive(:copy_zypp_to_target) expect(Yast::Pkg).to receive(:SourceSaveAll) expect(Yast::Pkg).to receive(:TargetFinish) subject.finish end + + it "copies the libzypp cache and credentials to the target system" do + allow(Dir).to receive(:exist?).and_call_original + allow(Dir).to receive(:entries).and_call_original + + # copying the raw cache + expect(Dir).to receive(:exist?).with( + File.join(target_dir, "/var/cache/zypp/raw") + ).and_return(true) + expect(FileUtils).to receive(:mkdir_p).with( + File.join(Yast::Installation.destdir, "/var/cache/zypp") + ) + expect(FileUtils).to receive(:cp_r).with( + File.join(target_dir, "/var/cache/zypp/raw"), + File.join(Yast::Installation.destdir, "/var/cache/zypp") + ) + + # copy the solv cache + repo_alias = "https-download.opensuse.org-94cc89aa" + expect(Dir).to receive(:entries) + .with(File.join(target_dir, "/var/cache/zypp/solv")) + .and_return([".", "..", "@System", repo_alias]) + expect(FileUtils).to receive(:cp_r).with( + File.join(target_dir, "/var/cache/zypp/solv/", repo_alias), + File.join(Yast::Installation.destdir, "/var/cache/zypp/solv") + ) + # ensure the @System cache is not copied + expect(FileUtils).to_not receive(:cp_r).with( + File.join(target_dir, "/var/cache/zypp/solv/@System"), + File.join(Yast::Installation.destdir, "/var/cache/zypp/solv") + ) + + # copying the credentials.d directory + expect(Dir).to receive(:exist?) + .with(File.join(target_dir, "/etc/zypp/credentials.d")) + .and_return(true) + expect(FileUtils).to receive(:mkdir_p) + .with(File.join(Yast::Installation.destdir, "/etc/zypp")) + expect(FileUtils).to receive(:cp_r).with( + File.join(target_dir, "/etc/zypp/credentials.d"), + File.join(Yast::Installation.destdir, "/etc/zypp") + ) + + # copying the global credentials file + expect(File).to receive(:exist?) + .with(File.join(target_dir, "/etc/zypp/credentials.cat")) + .and_return(true) + expect(FileUtils).to receive(:copy).with( + File.join(target_dir, "/etc/zypp/credentials.cat"), + File.join(Yast::Installation.destdir, "/etc/zypp") + ) + + subject.finish + end end describe "#package_installed?" do From 98475b07489f57e74c8b6d0d03c4966b1fef1b0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ladislav=20Slez=C3=A1k?= Date: Wed, 19 Jun 2024 15:45:09 +0200 Subject: [PATCH 4/4] fix(Live): Fixed console messages displayed twice --- .../system/agama-certificate-wait.service | 27 +++++++++++++++++++ live/root/usr/bin/agama-issue-generator | 10 +++++++ live/src/config.sh | 1 + 3 files changed, 38 insertions(+) create mode 100644 live/root/etc/systemd/system/agama-certificate-wait.service diff --git a/live/root/etc/systemd/system/agama-certificate-wait.service b/live/root/etc/systemd/system/agama-certificate-wait.service new file mode 100644 index 0000000000..7eddd03474 --- /dev/null +++ b/live/root/etc/systemd/system/agama-certificate-wait.service @@ -0,0 +1,27 @@ +[Unit] +Description=Postpone login prompt after the SSL fingerprint issue is generated + +After=agama-web-server.service + +# copied from YaST2-Second-Stage.service +Before=getty@tty1.service +Before=getty@tty2.service +Before=getty@tty3.service +Before=getty@tty4.service +Before=getty@tty5.service +Before=getty@tty6.service +Before=serial-getty@hvc0.service +Before=serial-getty@sclp_line0.service +Before=serial-getty@ttyAMA0.service +Before=serial-getty@ttyS0.service +Before=serial-getty@ttyS1.service +Before=serial-getty@ttyS2.service +Before=serial-getty@ttysclp0.service + +[Service] +Type=oneshot +# wait at most 15 seconds to not block +ExecStart=agama-issue-generator --wait-for-ssl 15 + +[Install] +WantedBy=default.target diff --git a/live/root/usr/bin/agama-issue-generator b/live/root/usr/bin/agama-issue-generator index 107b47de58..0f4b8dc357 100755 --- a/live/root/usr/bin/agama-issue-generator +++ b/live/root/usr/bin/agama-issue-generator @@ -149,6 +149,14 @@ generate_network_url() { fi } +# wait until the SSL fingreprint issue is create, but at most 10 seconds +wait_for_ssl_issue() { + for i in $(seq 1 "$1"); do + [ -f "$CERT_ISSUE" ] && exit 0 + sleep 1 + done +} + # make sure the parent directory for the issues exists mkdir -p /run/issue.d @@ -159,6 +167,8 @@ elif [ "$1" = "--ssh" ]; then generate_ssh_fingerprints elif [ "$1" = "--ssl" ]; then generate_certificate_fingerprints +elif [ "$1" = "--wait-for-ssl" ]; then + wait_for_ssl_issue "$2" elif [ "$1" = "--network" ]; then generate_network_url "$2" "$3" elif [ "$1" = "--watch-avahi" ]; then diff --git a/live/src/config.sh b/live/src/config.sh index 69ff8b0d06..4286912cf7 100644 --- a/live/src/config.sh +++ b/live/src/config.sh @@ -29,6 +29,7 @@ systemctl enable agama-auto.service systemctl enable agama-hostname.service systemctl enable agama-proxy-setup.service systemctl enable agama-certificate-issue.path +systemctl enable agama-certificate-wait.service systemctl enable agama-welcome-issue.service systemctl enable agama-avahi-issue.service systemctl enable agama-ssh-issue.service