Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package transactional-update for 
openSUSE:Factory checked in at 2026-01-09 17:02:03
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/transactional-update (Old)
 and      /work/SRC/openSUSE:Factory/.transactional-update.new.1928 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "transactional-update"

Fri Jan  9 17:02:03 2026 rev:123 rq:1326341 version:6.0.3

Changes:
--------
--- 
/work/SRC/openSUSE:Factory/transactional-update/transactional-update.changes    
    2025-08-21 16:52:41.621320065 +0200
+++ 
/work/SRC/openSUSE:Factory/.transactional-update.new.1928/transactional-update.changes
      2026-01-09 17:02:09.028586720 +0100
@@ -1,0 +2,88 @@
+Fri Jan  9 13:14:51 UTC 2026 - Ignaz Forster <[email protected]>
+
+- Version 6.0.3
+  - Only try logging to journald when socket exists; would lead to
+    messages about missing stream fds otherwise, which happened
+    when running in combustion's chroot environment
+
+-------------------------------------------------------------------
+Wed Jan  7 17:11:33 UTC 2026 - Ignaz Forster <[email protected]>
+
+- Version 6.0.2
+  Don't print reboot hints as errors to prevent failed openQA test
+  - t-u: Add new "warning" log level
+  - t-u: Adjust log level of reboot hints
+
+-------------------------------------------------------------------
+Fri Jan  2 15:58:12 UTC 2026 - Ignaz Forster <[email protected]>
+
+- Version 6.0.1
+  Adjustments to avoid logging with error log level to the journal
+  when there is no actual error
+  - tukit: Adjust selinux_restorecon log levels
+  - t-u: Print grub-mkconfig output to stdout
+
+-------------------------------------------------------------------
+Wed Dec 10 14:32:49 UTC 2025 - Ignaz Forster <[email protected]>
+
+- Temporarily disable default soft-reboot again to unblock other
+  projects while systemd 258 (required for correct console
+  switching used during the openQA tests) isn't ready yet.
+
+-------------------------------------------------------------------
+Sat Nov 22 08:51:56 UTC 2025 - Ignaz Forster <[email protected]>
+
+- Version 6.0.0
+  - Bump major version number to fix self-update due to new .so
+    library version in 5.5.0
+  - t-u: Fix syntax error introduced in 5.5.1 [boo#1254253]
+
+-------------------------------------------------------------------
+Tue Nov 18 10:33:33 UTC 2025 - Ignaz Forster <[email protected]>
+
+- Fix dbus dependency
+
+-------------------------------------------------------------------
+Wed Nov 12 00:50:23 UTC 2025 - Ignaz Forster <[email protected]>
+
+- Version 5.5.1
+  - t-u: Fix hanging t-u process when files were changed in /var
+  - t-u: Don't ask for key import during self-update [bsc#1239721]
+  - t-u: Fix stray output of "/etc"
+
+-------------------------------------------------------------------
+Mon Sep 29 22:37:08 UTC 2025 - Ignaz Forster <[email protected]>
+
+- Version 5.5.0
+  - t-u: The "run" command will return the exit status of the
+    command now and if it is non-zero, the snapshot will be
+    discarded. The --keep option or wrapping the command into a
+    bash script can be used if the command is expected to return
+    with a specific non-zero return code.
+    [bsc#1216504] [gh#openSUSE/transactional-update#134]
+    [gh#openSUSE/transactional-update#135]
+  - t-u / tukit: The --keep option will close the snapshot now also
+    in error case, but not set it as default or remove the
+    in-progress attribute; with this change the snapshot can be
+    used as a base for another snapshot when the changes are
+    verified as good.
+  - C API: rename loglevel to tukit_loglevel (API change)
+  - C API: removed the experimental status
+  - Rework logging to support syslog / journald:
+    - Also log to journald when calling t-u manually
+    - Introduce logging for libtukit: By default it will log
+      syslog / journald. This will finally make it possible to see
+      what happened during manual tukit calls or via the API
+    - Add option to specify desired logging mechanisms ("console",
+      "syslog") via tukit and API
+  - Fixed --discard option on rw systems
+  - Support SELinux outside /var/lib/selinux [jsc#PED-12492]
+  - sync-etc-state: Detect changes in xattrs with null bytes
+  - tukit: Added forgotten short option -k for tukit --keep
+  - t-u: Fix shellcheck hints
+  - README.md: Updated link to Salt executor
+- Enable soft-reboot by default again
+- Increase so version number because of the incompatible changes
+  from above
+
+-------------------------------------------------------------------

Old:
----
  transactional-update-5.1.0.tar.gz

New:
----
  transactional-update-6.0.3.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ transactional-update.spec ++++++
--- /var/tmp/diff_new_pack.2TtJqI/_old  2026-01-09 17:02:13.208760466 +0100
+++ /var/tmp/diff_new_pack.2TtJqI/_new  2026-01-09 17:02:13.224761131 +0100
@@ -1,7 +1,7 @@
 #
 # spec file for package transactional-update
 #
-# Copyright (c) 2025 SUSE LLC and contributors
+# Copyright (c) 2026 SUSE LLC and contributors
 # Copyright (c) 2021 Neal Gompa
 #
 # All modifications and additions to the file contributed by third parties
@@ -17,7 +17,7 @@
 #
 
 
-%global somajor 4
+%global somajor 8
 %global libprefix libtukit
 %global libname %{libprefix}%{somajor}
 %global devname %{libprefix}-devel
@@ -26,7 +26,7 @@
 %{!?_distconfdir: %global _distconfdir %{_prefix}%{_sysconfdir}}
 
 Name:           transactional-update
-Version:        5.1.0
+Version:        6.0.3
 Release:        0
 Summary:        Transactional Updates with btrfs and snapshots
 License:        GPL-2.0-or-later AND LGPL-2.1-or-later
@@ -149,7 +149,7 @@
 License:        GPL-2.0-or-later
 Group:          System/Libraries
 Requires:       %{libname} = %{version}-%{release}
-Requires:       dbus-1
+Requires:       dbus-service
 
 %description -n tukitd
 This package provedes the D-Bus service to access %{libname}'s

++++++ transactional-update-5.1.0.tar.gz -> transactional-update-6.0.3.tar.gz 
++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/transactional-update-5.1.0/NEWS 
new/transactional-update-6.0.3/NEWS
--- old/transactional-update-5.1.0/NEWS 2025-08-18 15:54:13.000000000 +0200
+++ new/transactional-update-6.0.3/NEWS 2026-01-09 14:08:38.000000000 +0100
@@ -2,6 +2,55 @@
 
 Copyright (C) 2016-2025 Thorsten Kukuk, Ignaz Forster et al.
 
+Version 6.0.3 (2026-01-09)
+* t-u: Only try logging to journald when the socket exists
+
+Version 6.0.2 (2026-01-07)
+* t-u: Add new "warning" log level
+* t-u: Adjust log level of reboot hints
+
+Version 6.0.1 (2026-01-02)
+Adjustments to avoid logging with error log level to the journal when
+there is no actual error
+* tukit: Adjust selinux_restorecon log levels
+* t-u: Print grub-mkconfig output to stdout
+
+Version 6.0.0 (2025-11-19)
+* Increase major version number to not break self-updates due to increased
+  library version introduced in 5.5.0
+* t-u: Fix syntax error introduced in 5.5.1 [boo#1254253]
+
+Version 5.5.1 (2025-11-12)
+* t-u: Fix hanging t-u process when files were changed in /var
+* t-u: Don't ask for key import during self-update [bsc#1239721]
+* t-u: Fix stray output of "/etc"
+
+Version 5.5.0 (2025-09-29)
+This release contains changes incompatible with previous versions.
+* t-u: The "run" command will return the exit status of the command now and if
+  it is non-zero, the snapshot will be discarded. The --keep option or
+  wrapping the command into a bash script can be used if the command is
+  expected to return with a specific non-zero return code.
+* t-u / tukit: The --keep option will close the snapshot now also in error
+  case, but not set it as default or remove the in-progress attribute; with
+  this change the snapshot can be used as a base for another snapshot when
+  the changes are verified as good.
+* C API: rename loglevel to tukit_loglevel (API change)
+* C API: removed the experimental status
+* Rework logging:
+  * Also log to journald when calling t-u manually
+  * Introduce logging for libtukit: By default it will log syslog / journald.
+    This will finally make it possible to see what happened during manual
+    tukit calls or via the API
+  * Add option to specify desired logging mechanisms ("console", "syslog") via
+    tukit and API
+* Fixed --discard option on rw systems
+* Support SELinux outside /var/lib/selinux [jsc#PED-12492]
+* sync-etc-state: Detect changes in xattrs with null bytes
+* tukit: Added forgotten short option -k for tukit --keep
+* t-u: Fix shellcheck hints
+* README.md: Updated link to Salt executor
+
 Version 5.1.0 (2025-08-18)
 * tukit: signalize errors from plugins; transactions will be aborted by
   default now
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/transactional-update-5.1.0/README.md 
new/transactional-update-6.0.3/README.md
--- old/transactional-update-5.1.0/README.md    2025-08-18 15:54:13.000000000 
+0200
+++ new/transactional-update-6.0.3/README.md    2026-01-09 14:08:38.000000000 
+0100
@@ -47,7 +47,7 @@
 Additionally the following components support transactional-update directly:
 * **dnf**, Fedora's package management system, supports transactional systems 
directly via the 
[libdnf-plugin-txnupd](https://code.opensuse.org/microos/libdnf-plugin-txnupd) 
plugin (libtukit).
 * **Cockpit** can update transactional systems via the 
[cockpit-tukit](https://github.com/openSUSE/cockpit-tukit) plugin (tukitd).
-* **Salt** contains the [salt.modules.transactional\_update 
module](https://docs.saltproject.io/en/3004/ref/modules/all/salt.modules.transactional_update.html)
 module (transactional-update).
+* **Salt** contains the 
[salt.executors.transactional\_update](https://docs.saltproject.io/en/latest/ref/executors/all/salt.executors.transactional_update.html)
 executor (transactional-update).
 * **Ansible** also supports transactional-update via the the 
[community.general.zypper](https://docs.ansible.com/ansible/latest/collections/community/general/zypper_module.html)
 module (transactional-update).
 
 ## Caveats
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/transactional-update-5.1.0/configure.ac 
new/transactional-update-6.0.3/configure.ac
--- old/transactional-update-5.1.0/configure.ac 2025-08-18 15:54:13.000000000 
+0200
+++ new/transactional-update-6.0.3/configure.ac 2026-01-09 14:08:38.000000000 
+0100
@@ -1,11 +1,12 @@
 dnl Process this file with autoconf to produce a configure script.
-AC_INIT([transactional-update],[5.0.7])
+# Semantic versioning, increase major version on incompatible interface change
+AC_INIT([transactional-update],[6.0.3])
 # Increase on any interface change and reset revision
-LIBTOOL_CURRENT=7
+LIBTOOL_CURRENT=8
 # On interface change increase if backwards compatible, reset otherwise
-LIBTOOL_AGE=3
+LIBTOOL_AGE=0
 # Increase on *any* C/C++ library code change, reset at interface change
-LIBTOOL_REVISION=0
+LIBTOOL_REVISION=1
 AC_CANONICAL_TARGET
 AM_INIT_AUTOMAKE([foreign])
 AC_CONFIG_FILES([tukit.pc])
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/transactional-update-5.1.0/dbus/tukitd.c 
new/transactional-update-6.0.3/dbus/tukitd.c
--- old/transactional-update-5.1.0/dbus/tukitd.c        2025-08-18 
15:54:13.000000000 +0200
+++ new/transactional-update-6.0.3/dbus/tukitd.c        2026-01-09 
14:08:38.000000000 +0100
@@ -860,7 +860,14 @@
     sd_bus_slot *slot_snap = NULL;
     sd_bus *bus = NULL;
     sd_event *event = NULL;
-    int ret = 1;
+    int ret;
+
+    tukit_set_loglevel(Info);
+    ret = tukit_set_logoutput("syslog");
+    if (ret < 0) {
+        fprintf(stderr, "Failed to start: %s\n", tukit_get_errmsg());
+        return EXIT_FAILURE;
+    }
 
     TransactionEntry* activeTransactions = (TransactionEntry*) 
malloc(sizeof(TransactionEntry));
     if (activeTransactions == NULL) {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/transactional-update-5.1.0/dracut/sync-etc-state.cpp 
new/transactional-update-6.0.3/dracut/sync-etc-state.cpp
--- old/transactional-update-5.1.0/dracut/sync-etc-state.cpp    2025-08-18 
15:54:13.000000000 +0200
+++ new/transactional-update-6.0.3/dracut/sync-etc-state.cpp    2026-01-09 
14:08:38.000000000 +0100
@@ -129,7 +129,7 @@
                 perror("lgetxattr cmp get");
                 return false;
             }
-            if (string{val_ref.get()} != string{val_cmp.get()}) {
+            if (memcmp(val_ref.get(), val_cmp.get(), vallen_ref) != 0) {
                 cout << "Extended attribute value changed: " << cmp << endl;
                 return true;
             }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/transactional-update-5.1.0/lib/Bindings/CBindings.cpp 
new/transactional-update-6.0.3/lib/Bindings/CBindings.cpp
--- old/transactional-update-5.1.0/lib/Bindings/CBindings.cpp   2025-08-18 
15:54:13.000000000 +0200
+++ new/transactional-update-6.0.3/lib/Bindings/CBindings.cpp   2026-01-09 
14:08:38.000000000 +0100
@@ -18,9 +18,19 @@
 const char* tukit_get_errmsg() {
     return errmsg.c_str();
 }
-void tukit_set_loglevel(loglevel lv) {
+void tukit_set_loglevel(tukit_loglevel lv) {
     tulog.level = static_cast<TULogLevel>(lv);
 }
+int tukit_set_logoutput(char* fields) {
+    try {
+        tulog.setLogOutput(fields);
+    } catch (const std::exception &e) {
+        fprintf(stderr, "ERROR: %s\n", e.what());
+        errmsg = e.what();
+        return -1;
+    }
+    return 0;
+}
 tukit_tx tukit_new_tx() {
     Transaction* transaction = nullptr;
     try {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/transactional-update-5.1.0/lib/Bindings/libtukit.h 
new/transactional-update-6.0.3/lib/Bindings/libtukit.h
--- old/transactional-update-5.1.0/lib/Bindings/libtukit.h      2025-08-18 
15:54:13.000000000 +0200
+++ new/transactional-update-6.0.3/lib/Bindings/libtukit.h      2026-01-09 
14:08:38.000000000 +0100
@@ -2,8 +2,7 @@
 /* SPDX-FileCopyrightText: Copyright SUSE LLC */
 
 /*
-  This is the EXPERIMENTAL C API for tukit. For the moment it is only inteded
-  for internal use.
+  This is the C API for libtukit.
   For documentation please see the corresponding classes in the C++ header
   files.
  */
@@ -18,10 +17,11 @@
 
 typedef enum {
     None=0, Error, Info, Debug
-} loglevel;
+} tukit_loglevel;
 
 const char* tukit_get_errmsg();
-void tukit_set_loglevel(loglevel lv);
+void tukit_set_loglevel(tukit_loglevel lv);
+int tukit_set_logoutput(char *fields);
 typedef void* tukit_tx;
 tukit_tx tukit_new_tx();
 void tukit_free_tx(tukit_tx tx);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/transactional-update-5.1.0/lib/Log.hpp 
new/transactional-update-6.0.3/lib/Log.hpp
--- old/transactional-update-5.1.0/lib/Log.hpp  2025-08-18 15:54:13.000000000 
+0200
+++ new/transactional-update-6.0.3/lib/Log.hpp  2026-01-09 14:08:38.000000000 
+0100
@@ -8,35 +8,87 @@
 #ifndef T_U_LOG_H
 #define T_U_LOG_H
 
+#include <exception>
 #include <iomanip>
 #include <iostream>
+#include <syslog.h>
 
 enum class TULogLevel {
     None=0, Error, Info, Debug
 };
+struct TULogOutput {
+    bool console = true;
+    bool syslog = true;
+};
 
 // There's no threading in this application, so no locking is implemented
 class TULog {
 public:
     TULogLevel level = TULogLevel::Error;
+    TULogOutput output{};
 
     template<typename... T> void error(const T&... args) {
-        if (level >=TULogLevel::Error)
-            log(args...);
+        if (level >=TULogLevel::Error) {
+            print_to_output(LOG_ERR, args...);
+        }
+        print_to_syslog(LOG_ERR, args...);
     }
     template<typename... T> void info(const T&... args) {
-        if (level >= TULogLevel::Info)
-            log(args...);
+        if (level >= TULogLevel::Info) {
+            print_to_output(LOG_INFO, args...);
+        }
+        print_to_syslog(LOG_INFO, args...);
     }
     template<typename... T> void debug(const T&... args) {
-        if (level >= TULogLevel::Debug)
-            log(args...);
+        if (level >= TULogLevel::Debug) {
+            print_to_output(LOG_DEBUG, args...);
+            print_to_syslog(LOG_DEBUG, args...);
+        }
     }
 
     template<typename... T> void log(const T&... args) {
-        std::time_t now = std::time(0);
-        std::cout << std::put_time(std::localtime(&now), "%F %T ");
-        ((std::cout << args),...) << std::endl;
+        print_to_output(LOG_INFO, args...);
+        print_to_syslog(LOG_INFO, args...);
+    }
+
+    void setLogOutput(std::string outputs) {
+        output.console = false;
+        output.syslog = false;
+        std::string field;
+        std::stringstream ss(outputs);
+        while (getline(ss, field, ',')) {
+            if (field == "console") {
+                output.console = true;
+                continue;
+            }
+            if (field == "syslog") {
+                output.syslog = true;
+                continue;
+            }
+            throw std::invalid_argument{"Invalid log output."};
+        }
+    }
+
+private:
+    template<typename... T> void print_to_output(int loglevel, const T&... 
args) {
+        if (output.console) {
+            std::stringstream ss;
+            ((ss << args),...);
+            std::string s = ss.str();
+            if (loglevel <= LOG_ERR) {
+                std::cerr << s << std::endl;
+            } else {
+                std::cout << s << std::endl;
+            }
+        }
+    }
+    template<typename... T> void print_to_syslog(int loglevel, const T&... 
args) {
+        if (output.syslog) {
+            std::stringstream ss;
+            ((ss << args),...);
+            std::string s = ss.str();
+            syslog(loglevel, "%s", s.c_str());
+        }
     }
 };
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/transactional-update-5.1.0/lib/Snapshot/Snapper.cpp 
new/transactional-update-6.0.3/lib/Snapshot/Snapper.cpp
--- old/transactional-update-5.1.0/lib/Snapshot/Snapper.cpp     2025-08-18 
15:54:13.000000000 +0200
+++ new/transactional-update-6.0.3/lib/Snapshot/Snapper.cpp     2026-01-09 
14:08:38.000000000 +0100
@@ -92,7 +92,7 @@
 /* Snapshot methods */
 
 void Snapper::close() {
-    callSnapper("modify -u 'transactional-update-in-progress=' " + snapshotId);
+    callSnapper("modify --userdata 'transactional-update-in-progress=' " + 
snapshotId);
 }
 
 void Snapper::abort() {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/transactional-update-5.1.0/lib/Transaction.cpp 
new/transactional-update-6.0.3/lib/Transaction.cpp
--- old/transactional-update-5.1.0/lib/Transaction.cpp  2025-08-18 
15:54:13.000000000 +0200
+++ new/transactional-update-6.0.3/lib/Transaction.cpp  2026-01-09 
14:08:38.000000000 +0100
@@ -42,8 +42,10 @@
 public:
     void addSupplements();
     void snapMount();
+    void closeSnapshot(bool aborted=false);
     int runCommand(char* argv[], bool inChroot, std::string* buffer);
     static int inotifyAdd(const char *pathname, const struct stat *sbuf, int 
type, struct FTW *ftwb);
+    static int selinux_logging_callback(int type, const char *fmt, ...);
     int inotifyRead();
     std::unique_ptr<SnapshotManager> snapshotMgr;
     std::unique_ptr<Snapshot> snapshot;
@@ -80,7 +82,11 @@
     try {
         if (isInitialized() && !getSnapshot().empty() && 
fs::exists(getRoot())) {
             tulog.info("Discarding snapshot ", pImpl->snapshot->getUid(), ".");
-            pImpl->snapshot->abort();
+            if (pImpl->keepIfError) {
+                pImpl->closeSnapshot(true);
+            } else {
+                pImpl->snapshot->abort();
+            }
             TransactionalUpdate::Plugins plugins{nullptr, pImpl->keepIfError};
             plugins.run("abort-post", pImpl->snapshot->getUid());
         }
@@ -105,6 +111,28 @@
     return pImpl->bindDir;
 }
 
+int Transaction::impl::selinux_logging_callback(int type, const char *fmt, 
...) {
+    va_list ap, ap_copy;
+    va_start(ap, fmt);
+    va_copy(ap_copy, ap);
+    size_t size = vsnprintf(nullptr, 0, fmt, ap);
+    va_end(ap);
+    std::vector<char> se_buffer(size + 1);
+    vsnprintf(se_buffer.data(), size, fmt, ap_copy);
+
+    switch (type) {
+        case SELINUX_ERROR:
+            tulog.error("SELinux: ", se_buffer.data());
+            break;
+        default:
+            tulog.info("SELinux: ", se_buffer.data());
+            break;
+    }
+    va_end(ap_copy);
+
+    return 0;
+}
+
 void Transaction::impl::snapMount() {
     if (unshare(CLONE_NEWNS) < 0) {
         throw std::runtime_error{"Creating new mount namespace failed: " + 
std::string(strerror(errno))};
@@ -135,8 +163,10 @@
             // up in the root file system, but will always be shadowed by the 
real /var mount. Due to that they
             // also won't be relabelled at any time. During updates this may 
cause problems if packages try to
             // access those leftover directories with wrong permissions, so 
they have to be relabelled manually...
-            BindMount selinuxVar("/var/lib/selinux", 0, true);
-            selinuxVar.mount(bindDir);
+            if (fs::is_directory("/var/lib/selinux")) {
+                BindMount selinuxVar("/var/lib/selinux", 0, true);
+                selinuxVar.mount(bindDir);
+            }
             BindMount selinuxEtc("/etc/selinux", 0, true);
             selinuxEtc.mount(bindDir);
 
@@ -149,6 +179,10 @@
                     tulog.error("Chrooting to " + bindDir.native() + " for 
SELinux relabelling failed: " + std::string(strerror(errno)));
                     _exit(errno);
                 }
+
+                union selinux_callback se_callback;
+                se_callback.func_log = selinux_logging_callback;
+                selinux_set_callback(SELINUX_CB_LOG, se_callback);
                 unsigned int restoreconOptions = SELINUX_RESTORECON_RECURSE | 
SELINUX_RESTORECON_IGNORE_DIGEST;
                 if (tulog.level >= TULogLevel::Info)
                     restoreconOptions |= SELINUX_RESTORECON_VERBOSE;
@@ -277,7 +311,7 @@
         std::unique_ptr<Snapshot> prevSnap = pImpl->snapshotMgr->open(base);
         std::unique_ptr<Mount> oldEtc{new Mount{"/etc"}};
         oldEtc->setTabSource(prevSnap->getRoot() / "etc" / "fstab");
-        if (oldEtc->getFilesystem() == "overlayfs") {
+        if (oldEtc->isMount() && oldEtc->getFilesystem() == "overlayfs") {
             tulog.info("Can not merge back changes in /etc into old overlayfs 
system - ignoring 'discardIfNoChange'.");
         } else {
             // Flag file to indicate this snapshot was initialized with 
discard flag
@@ -347,7 +381,10 @@
 
         // Recursively register all directories of the root file system
         inotifyExcludes = MountList::getList(snapshot->getRoot());
-        inotifyExcludes.push_back(snapshot->getRoot() / "etc");
+        std::unique_ptr<Mount> mntEtc{new Mount{"/etc"}};
+        if (mntEtc->isMount()) {
+            inotifyExcludes.push_back(snapshot->getRoot() / "etc");
+        }
         nftw(snapshot->getRoot().c_str(), inotifyAdd, 20, FTW_MOUNT | 
FTW_PHYS);
     }
 
@@ -498,14 +535,11 @@
     }
 }
 
-void Transaction::finalize() {
-    TransactionalUpdate::Plugins plugins{this, pImpl->keepIfError};
-    plugins.run("finalize-pre", nullptr);
-
+void Transaction::impl::closeSnapshot(bool aborted) {
     sync();
-    if (pImpl->discardIfNoChange &&
-            ((inotifyFd != 0 && pImpl->inotifyRead() == 0) ||
-            (inotifyFd == 0 && fs::exists(getRoot() / "discardIfNoChange")))) {
+    if (discardIfNoChange &&
+            ((inotifyFd != 0 && inotifyRead() == 0) ||
+            (inotifyFd == 0 && fs::exists(snapshot->getRoot() / 
"discardIfNoChange")))) {
         tulog.info("No changes to the root file system - discarding 
snapshot.");
 
         // Even if the snapshot itself does not contain any changes, /etc may 
do so. If the new snapshot is a
@@ -517,41 +551,52 @@
             std::filesystem::path targetRoot = "/";
             std::string base;
 
-            std::ifstream input(getRoot() / "discardIfNoChange");
+            std::ifstream input(snapshot->getRoot() / "discardIfNoChange");
             input >> base;
             input.close();
 
             std::unique_ptr<Mount> previousEtc{new Mount("/etc", 0, true)};
-            if (pImpl->snapshotMgr->getCurrent() == base) {
+            if (snapshotMgr->getCurrent() == base) {
                 tulog.info("Merging changes in /etc into the running system.");
             } else {
                 tulog.info("Merging changes in /etc into the previous 
snapshot.");
-                targetRoot = pImpl->snapshotMgr->open(base)->getRoot();
+                targetRoot = snapshotMgr->open(base)->getRoot();
             }
-            Util::exec("rsync --archive --inplace --xattrs --acls --exclude 
'fstab' --exclude 'etc.syncpoint' --delete --quiet '" + 
this->pImpl->bindDir.native() + "/etc/' " + targetRoot.native() + "/etc");
+            Util::exec("rsync --archive --inplace --xattrs --acls --exclude 
'fstab' --exclude 'etc.syncpoint' --delete --quiet '" + bindDir.native() + 
"/etc/' " + targetRoot.native() + "/etc");
         }
 
-        TransactionalUpdate::Plugins plugins_without_transaction{nullptr, 
pImpl->keepIfError};
-        plugins_without_transaction.run("finalize-post", 
pImpl->snapshot->getUid() + " " + "discarded");
+        TransactionalUpdate::Plugins plugins_without_transaction{nullptr, 
keepIfError};
+        plugins_without_transaction.run("finalize-post", snapshot->getUid() + 
" " + "discarded");
         return;
     }
-    if (fs::exists(getRoot() / "discardIfNoChange")) {
-        fs::remove(getRoot() / "discardIfNoChange");
+    if (fs::exists(snapshot->getRoot() / "discardIfNoChange")) {
+        fs::remove(snapshot->getRoot() / "discardIfNoChange");
     }
 
     // Update /usr timestamp to support system offline update mechanism
-    if (utime((pImpl->snapshot->getRoot() / "usr").c_str(), nullptr) != 0)
+    if (utime((snapshot->getRoot() / "usr").c_str(), nullptr) != 0)
         throw std::runtime_error{"Updating /usr timestamp failed: " + 
std::string(strerror(errno))};
 
-    pImpl->snapshot->close();
-    pImpl->supplements.cleanup();
-    pImpl->dirsToMount.clear();
+    if (! aborted) {
+        snapshot->close();
+    }
+    supplements.cleanup();
+    dirsToMount.clear();
 
-    std::unique_ptr<Snapshot> defaultSnap = 
pImpl->snapshotMgr->open(pImpl->snapshotMgr->getDefault());
+    std::unique_ptr<Snapshot> defaultSnap = 
snapshotMgr->open(snapshotMgr->getDefault());
     if (defaultSnap->isReadOnly())
-        pImpl->snapshot->setReadOnly(true);
-    pImpl->snapshot->setDefault();
-    tulog.info("New default snapshot is #" + pImpl->snapshot->getUid() + " (" 
+ std::string(pImpl->snapshot->getRoot()) + ").");
+        snapshot->setReadOnly(true);
+    if (! aborted) {
+        snapshot->setDefault();
+        tulog.info("New default snapshot is #" + snapshot->getUid() + " (" + 
std::string(snapshot->getRoot()) + ").");
+    }
+}
+
+void Transaction::finalize() {
+    TransactionalUpdate::Plugins plugins{this, pImpl->keepIfError};
+    plugins.run("finalize-pre", nullptr);
+
+    this->pImpl->closeSnapshot();
 
     std::string id = pImpl->snapshot->getUid();
     pImpl->snapshot.reset();
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/transactional-update-5.1.0/man/transactional-update.8.xml 
new/transactional-update-6.0.3/man/transactional-update.8.xml
--- old/transactional-update-5.1.0/man/transactional-update.8.xml       
2025-08-18 15:54:13.000000000 +0200
+++ new/transactional-update-6.0.3/man/transactional-update.8.xml       
2026-01-09 14:08:38.000000000 +0100
@@ -244,10 +244,22 @@
     <term><option>run</option> <replaceable>cmd</replaceable></term>
     <listitem>
       <para>
-       Execute the the command <replaceable>cmd</replaceable> inside
-       a new snapshot. By default this snaphot will remain, but if
-       <option>--drop-if-no-change</option> is set, the new snapshot
-       will be dropped if there is no change in the file system.
+       Execute the command <replaceable>cmd</replaceable> inside
+       a new snapshot and return the command's exit code. If the command
+       returns with a non-zero exit status, the snapshot will be discarded
+       again.
+      </para>
+      <para>
+       If a non-zero exit status is expected, then the command may either be
+       wrapped inside a Bash script (see the program listing below) or it can
+       be combinded with the <option>--keep</option> option and checking the
+       exit code of <command>transactional-update</command> itself.
+      </para>
+      <para>
+       Note that a snapshot will be created for each call, so if the command
+       only performs read-only operations it may be worth to combine it with
+       <option>--drop-if-no-change</option> to discard the snapshot again when
+       there are not changes in the snapshot.
       </para>
       <para>
        This command consumes all the remaining parameters, so should
@@ -573,8 +585,11 @@
     <term><option>--keep</option></term>
     <term><option>-k</option></term>
     <listitem>
-      <para>Do not delete the snapshot in case of errors. This option is only 
for debugging or recovery if a tukit
-      plugin failed - do not use this option for production snapshots!</para>
+      <para>Do not delete the snapshot in case of errors. This option is mainly
+      meant for debugging or recovery if a tukit plugin failed, the resulting
+      snapshot should be used only after verifying the contents are as
+      expected.</para>
+      <para>The default snapshot will not be changed.</para>
     </listitem>
   </varlistentry>
   <varlistentry>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/transactional-update-5.1.0/sbin/transactional-update.in 
new/transactional-update-6.0.3/sbin/transactional-update.in
--- old/transactional-update-5.1.0/sbin/transactional-update.in 2025-08-18 
15:54:13.000000000 +0200
+++ new/transactional-update-6.0.3/sbin/transactional-update.in 2026-01-09 
14:08:38.000000000 +0100
@@ -59,7 +59,9 @@
 OCI_RSYNC_EXCLUDES="/etc /var /usr/local /tmp /root /home /srv /opt /sys /dev 
/proc /run"
 OCI_TARGET=""
 DRACUT_OPTS=""
-TUKIT_OPTS=""
+TUKIT_OPTS="--log=console"
+FORCE_REFRESH=0
+ZYPPER_FORCE_REFRESH=0
 IS_BLS=""
 
 TMPDIR=${TMPDIR:-/tmp}
@@ -95,7 +97,18 @@
     fi
 
     log_info "Checking for newer version."
-    if zypper --non-interactive info transactional-update | grep -q '^Status 
*: out-of-date'; then
+    # Tell zypper that we are in a read-only system, do not import keys using 
rpm
+    export ZYPP_READONLY_HACK=1
+    local zypper_log_tmpfile
+    zypper_log_tmpfile=$(mktemp "${TMPDIR}/transactional-update.XXXXXXXXXX")
+    zypper --xmlout --non-interactive info transactional-update > 
"$zypper_log_tmpfile"
+    if grep -q "can not be imported. (READONLY MODE)" "$zypper_log_tmpfile"; 
then
+        export ZYPPER_FORCE_REFRESH=1
+    fi
+    grep -q '^Status *: out-of-date' "$zypper_log_tmpfile"
+    out_of_date="$?"
+    rm "$zypper_log_tmpfile"
+    if [ $out_of_date -eq 0 ]; then
         log_info "New version found - updating..."
         if ! TA_UPDATE_TMPFILE="$(mktemp -d 
"${TMPDIR}"/transactional-update.XXXXXXXXXX)"; then
             log_error "ERROR: Couldn't create temporary directory for 
self-update."
@@ -107,6 +120,7 @@
             log_error "ERROR: Couldn't extract the update."
             quit 1
         fi
+        unset ZYPP_READONLY_HACK
         # Reset CWD before restart
         if ! popd >/dev/null; then
             log_error "popd failed during self-update"
@@ -121,6 +135,7 @@
         export TA_UPDATE_TMPFILE
         exec "${TA_UPDATE_TMPFILE}/usr/sbin/transactional-update" "$@" 
1>&${origstdout} 2>&${origstderr}
     fi
+    unset ZYPP_READONLY_HACK
 }
 
 usage() {
@@ -179,23 +194,64 @@
     exit 0
 }
 
+log_to_journal() {
+    if [ -S /run/systemd/journal/stdout ]; then
+        systemd-cat -t transactional-update "$@"
+    fi
+}
+
 log_to_file() {
-    echo -e "$(date "+%Y-%m-%d %H:%M")" "$@" >> ${LOGFILE}
+    if [ $# -gt 0 ]; then
+        echo -e "$(date "+%Y-%m-%d %T")" "$@" >> ${LOGFILE}
+    else # input via pipe
+        while read -r line; do
+            echo -e "$(date "+%Y-%m-%d %T")" "${line}" >> ${LOGFILE}
+        done
+    fi
 }
 
 log_to_stdout() {
-    if [ ${VERBOSITY} -ge 2 ]; then
+    if [ ${VERBOSITY} -ge 2 ] && [ -z "${SYSTEMD_EXEC_PID}" ]; then
         echo -e "$@"
     fi
 }
 
+log_to_stderr() {
+    echo -e "$@" 1>&${origstderr}
+}
+
+log_common() {
+    local log_to_orig=0
+    local loglevel=$1
+    shift
+
+    if [ "$1" == "-o" ]; then
+        log_to_orig=1
+        shift
+    fi
+
+    if [ $# -gt 0 ]; then
+        log_to_stdout "$@"
+        log_to_file "$@"
+        log_to_journal -p "$loglevel" echo "$@"
+    else # input via pipe
+        if [ "$log_to_orig" == 1 ]; then
+            cat | tee -a >(log_to_journal -p "$loglevel") >(log_to_file) 
1>&${origstdout}
+        else
+            cat | tee -a >(log_to_journal -p "$loglevel") >(log_to_file)
+        fi
+    fi
+}
+
 log_info() {
-    log_to_stdout "$@"
-    log_to_file "$@"
+    log_common info "$@"
+}
+
+log_warn() {
+    log_common warning "$@"
 }
 
 log_error() {
-    log_to_file "$@"
     echo -e "$@" 1>&2
 }
 
@@ -237,6 +293,8 @@
         echo "REBOOT_ASSOCS[${key}]=${REBOOT_ASSOCS[${key}]}" >> ${STATE_FILE}
     done
 
+    echo "FORCE_REFRESH=\"${FORCE_REFRESH}\"" >> ${STATE_FILE}
+
     if [ "$1" -ne 0 ] && [ ${HAS_SEPARATE_VAR} -eq 0 ]; then
         # If /var/lib/misc is not a seperate partition / subvolume, copy the
         # state file into the new snapshot as it will contain an outdated
@@ -257,8 +315,8 @@
 }
 
 rebuild_kdump_initrd() {
-    if tukit -q call "$1" systemctl is-enabled --quiet kdump.service; then
-        tukit ${TUKIT_OPTS} call "$1" /sbin/mkdumprd |& tee -a ${LOGFILE} 
1>&${origstdout}
+    if tukit ${TUKIT_OPTS} --quiet call "$1" systemctl is-enabled --quiet 
kdump.service; then
+        tukit ${TUKIT_OPTS} call "$1" /sbin/mkdumprd | log_info -o
         return "${PIPESTATUS[0]}"
     elif [ ${SETUP_KDUMP} -ge 1 ]; then
         log_info "INFO: Requested rebuild of kdump initrd, but kdump is not 
enabled."
@@ -278,9 +336,13 @@
         echo -n "${REBOOT_LEVEL_PREV}" > "${NEEDS_RESTARTING_FILE}"
     fi
 
-    if [ -n "${SNAPSHOT_ID}" ] && [ "$KEEP_SNAPSHOT" -eq 0 ]; then
-        log_error "Removing snapshot #${SNAPSHOT_ID}..."
-        tukit ${TUKIT_OPTS} abort "${SNAPSHOT_ID}" |& tee -a ${LOGFILE}
+    if [ -n "${SNAPSHOT_ID}" ]; then
+        if [ "$KEEP_SNAPSHOT" -eq 1 ]; then
+            log_info "WARNING: Keeping snapshot as requested, but not setting 
as default."
+        else
+            log_error "Removing snapshot #${SNAPSHOT_ID}..."
+        fi
+        tukit ${TUKIT_OPTS} abort "${SNAPSHOT_ID}" | log_info
     fi
     log_info "transactional-update finished"
     exit "$1"
@@ -369,7 +431,7 @@
             quit 2
         fi
 
-        if [ "$(systemd-sysext | tail -n +2 | grep " none " | wc -l)" -ne 2 ]; 
then
+        if [ "$(systemd-sysext | tail -n +2 | grep -c " none ")" -ne 2 ]; then
             REFRESH_SYSEXTS=1
         fi
 
@@ -575,15 +637,6 @@
             shell)
                 RUN_SHELL=1
                 shift
-                if [ "$1" = "-c" ]; then
-                    if [ -z "$2" ]; then
-                        SHELL_CMD="-"
-                    else
-                        SHELL_CMD="$2"
-                        shift
-                    fi
-                    shift
-                fi
                 ;;
             initrd)
                 REWRITE_INITRD=1
@@ -757,7 +810,7 @@
 exec {origstderr}>&2
 
 # Log stderr to log file in case anything goes wrong within 
transactional-update
-exec 2> >(exec tee -i -a "${LOGFILE}" >&2)
+exec 2> >(exec tee -i -a "${LOGFILE}" >(log_to_journal -p err) >&2)
 if [ "${VERBOSITY}" -eq 1 ]; then
     exec 1>/dev/null
     if [ -n "${ZYPPER_ARG}" ]; then
@@ -889,6 +942,14 @@
 if [ -f ${STATE_FILE} ]; then
     . ${STATE_FILE}
 fi
+# If we need to refresh the repositories, save this var into the state
+# as t-u might not create/save a snapshot in this call
+# Save it only if not already present
+if [ "$ZYPPER_FORCE_REFRESH" -gt "$FORCE_REFRESH" ]; then
+    FORCE_REFRESH=1
+    save_state_file 0
+fi
+unset ZYPPER_FORCE_REFRESH
 
 log_info "transactional-update ${VERSION} started"
 log_info "Options: ${ORIG_ARGS[*]}"
@@ -953,7 +1014,7 @@
 
     log_info "Rollback to snapshot ${ROLLBACK_SNAPSHOT}..."
 
-    if ! tukit rollback "${ROLLBACK_SNAPSHOT}"; then
+    if ! tukit ${TUKIT_OPTS} rollback "${ROLLBACK_SNAPSHOT}"; then
         log_error "ERROR: Rollback to snapshot $ROLLBACK_SNAPSHOT failed!"
         quit 1
     fi
@@ -980,7 +1041,7 @@
         if [ ${DO_APPLY} -eq 1 ]; then
             do_apply
         else
-            log_error "Please reboot to finish rollback!"
+            log_warn "Please reboot to finish rollback!"
         fi
     fi
     log_info "transactional-update finished"
@@ -998,7 +1059,7 @@
         for snap in ${LAST_WORKING_SNAPSHOTS}; do
             if [ "${CURRENT_SNAPSHOT_ID}" -ne "${snap}" ] && [ 
"${BOOTED_SNAPSHOT_ID}" -ne "${snap}" ]; then
                 log_info "Adding cleanup algorithm to snapshot #${snap}"
-                snapper modify -c number "${snap}" |& tee -a ${LOGFILE}
+                snapper modify -c number "${snap}" | log_info
                 if [ "${PIPESTATUS[0]}" -ne 0 ]; then
                     log_error "ERROR: cannot set cleanup algorithm for 
snapshot #${snap}"
                 fi
@@ -1006,7 +1067,7 @@
                 # earlier. If not, mark is as important, so that it will not 
get deleted too fast.
                 if [ "${RO_ROOT}" == "true" ]; then
                     log_info "Adding \"important=yes\" to snapshot #${snap}"
-                    snapper modify -u "important=yes" "${snap}" |& tee -a 
${LOGFILE}
+                    snapper modify -u "important=yes" "${snap}" | log_info
                     if [ "${PIPESTATUS[0]}" -ne 0 ]; then
                         log_error "ERROR: cannot set \"important=yes for 
snapshot\" #${snap}"
                     fi
@@ -1038,7 +1099,7 @@
                 [ "${snap}" -ne "${BOOTED_SNAPSHOT_ID}" ] && \
                 [ "${snap}" -ne "${DEFAULT_SNAPSHOT_ID}" ]; then
                 log_info "Mark unused snapshot #${snap} for deletion"
-                snapper modify -c number "${snap}" |& tee -a ${LOGFILE}
+                snapper modify -c number "${snap}" | log_info
                 if [ "${PIPESTATUS[0]}" -ne 0 ]; then
                     log_error "ERROR: cannot set cleanup algorithm for 
snapshot #${snap}"
                     # Is the snapshot still available at all?
@@ -1089,8 +1150,8 @@
         fi
     fi
 
-    output="$(tukit ${TUKIT_OPTS} -c"${BASE_SNAPSHOT_ID}" open |& tee -a 
${LOGFILE})"
-    log_info "$output"
+    output="$(tukit ${TUKIT_OPTS} -c"${BASE_SNAPSHOT_ID}" open)"
+    echo "${output}" | log_info
     SNAPSHOT_ID=$(echo "${output}" | grep -e "^ID:" | cut -d " " -f 2-)
     if [ -z "${SNAPSHOT_ID}" ]; then
         quit 1
@@ -1104,7 +1165,7 @@
     UNUSED_SNAPSHOTS="${SNAPSHOT_ID} ${UNUSED_SNAPSHOTS}"
 
     if [ ${DO_REGISTRATION} -eq 1 ]; then
-        tukit ${TUKIT_OPTS} callext "${SNAPSHOT_ID}" SUSEConnect --root {} 
${REGISTRATION_ARGS} |& tee -a ${LOGFILE} 1>&${origstdout}
+        tukit ${TUKIT_OPTS} callext "${SNAPSHOT_ID}" SUSEConnect --root {} 
${REGISTRATION_ARGS} | log_info -o
         if [ "${PIPESTATUS[0]}" -ne 0 ]; then
             EXITCODE=1
         fi
@@ -1126,11 +1187,11 @@
         for i in ${OCI_RSYNC_EXCLUDES}; do
             OCI_RSYNC_EXCLUDES_LIST+=("--exclude $i ")
         done
-        rsync --delete ${OCI_RSYNC_ARGS} ${OCI_RSYNC_EXCLUDES_LIST[@]} 
${OCI_MOUNT}/ ${SNAPSHOT_DIR}/ |& tee -a ${LOGFILE} 1>&${origstdout}
+        rsync --delete ${OCI_RSYNC_ARGS} ${OCI_RSYNC_EXCLUDES_LIST[@]} 
${OCI_MOUNT}/ ${SNAPSHOT_DIR}/ | log_info -o
 
         # Merge contents of /etc from container image but preserve existing 
configuration
         log_info "INFO: Merging /etc from container image into existing 
snapshot, preserving existing configuration..."
-        tukit ${TUKIT_OPTS} callext "${SNAPSHOT_ID}" rsync --ignore-existing 
${OCI_RSYNC_ARGS} "${OCI_MOUNT}/etc/" "${SNAPSHOT_DIR}/etc/" |& tee -a 
${LOGFILE} 1>&${origstdout}
+        tukit ${TUKIT_OPTS} callext "${SNAPSHOT_ID}" rsync --ignore-existing 
${OCI_RSYNC_ARGS} "${OCI_MOUNT}/etc/" "${SNAPSHOT_DIR}/etc/" | log_info -o
 
         # Unmount the container image
         podman image unmount "${OCI_TARGET}"
@@ -1138,7 +1199,19 @@
         log_info "Forcing SELinux relabel at next reboot."
         touch "${SNAPSHOT_DIR}/.autorelabel"
         # Ensure RPM database is using the desired system backend
-        tukit ${TUKIT_OPTS} call "${SNAPSHOT_ID}" rpm --rebuilddb |& tee -a 
${LOGFILE} 1>&${origstdout}
+        tukit ${TUKIT_OPTS} call "${SNAPSHOT_ID}" rpm --rebuilddb | log_info -o
+    fi
+
+    # A new key was being imported while t-u was looking for a self-update, 
but the system was read-only
+    # Force refresh of the repositories to import it
+    if [ "$FORCE_REFRESH" -eq 1 ]; then
+        if tukit ${TUKIT_OPTS} call ${SNAPSHOT_ID} zypper refresh -f; then
+
+            # The refresh succeeded. It's okay to disable it here, because
+            # this is included in t-u state, and it is only saved when
+            # the transaction is saved
+            FORCE_REFRESH=0
+        fi
     fi
 
     if [ -n "${ZYPPER_ARG}" ]; then
@@ -1147,7 +1220,7 @@
         if [ ${DO_MIGRATION} -eq 1 ]; then
             # transactional-update migration
             export DISABLE_RESTART_ON_UPDATE=yes
-            tukit ${TUKIT_OPTS} callext "${SNAPSHOT_ID}" zypper ${ZYPPER_ARG} 
--root {} ${ZYPPER_NONINTERACTIVE} "${ZYPPER_ARG_EXTRA[@]}" |& tee -a 
${LOGFILE} 1>&${origstdout}
+            tukit ${TUKIT_OPTS} callext "${SNAPSHOT_ID}" zypper ${ZYPPER_ARG} 
--root {} ${ZYPPER_NONINTERACTIVE} "${ZYPPER_ARG_EXTRA[@]}" | log_info -o
             RETVAL=${PIPESTATUS[0]}
         else
             if [ ${DO_CALLEXT} -eq 1 ]; then
@@ -1174,10 +1247,10 @@
             fi
 
             export DISABLE_RESTART_ON_UPDATE=yes
-            ${zypper_cmd} ${ZYPPER_ARG} ${ZYPPER_NONINTERACTIVE} 
"${ZYPPER_ARG_EXTRA[@]}" |& tee -a ${LOGFILE} 1>&${origstdout}
+            ${zypper_cmd} ${ZYPPER_ARG} ${ZYPPER_NONINTERACTIVE} 
"${ZYPPER_ARG_EXTRA[@]}" | log_info -o
             RETVAL=${PIPESTATUS[0]}
             if { [ "${RETVAL}" -eq 0 ] || [ "${RETVAL}" -eq 102 ] || [ 
"${RETVAL}" -eq 103 ]; } && [ -n "${INCLUDES_KERNEL_PACKAGES}" ]; then
-                ${zypper_cmd} -n purge-kernels |& tee -a ${LOGFILE}
+                ${zypper_cmd} -n purge-kernels | log_info
             fi
         fi
         # in case of migration, we need to do a little bit more:
@@ -1237,7 +1310,7 @@
             REWRITE_GRUB_CFG=1
         fi
         if [ -x "${SNAPSHOT_DIR}/usr/bin/update-crypto-policies" ]; then
-            tukit ${TUKIT_OPTS} call "${SNAPSHOT_ID}" update-crypto-policies 
--set FIPS |& tee -a ${LOGFILE} 1>&${origstdout}
+            tukit ${TUKIT_OPTS} call "${SNAPSHOT_ID}" update-crypto-policies 
--set FIPS | log_info -o
         fi
     fi
     if [ ${SETUP_SELINUX} -eq 1 ]; then
@@ -1287,10 +1360,10 @@
 
     if [ ${REWRITE_INITRD} -eq 1 ]; then
         log_info "Creating new initrd"
-        if is_bls && ! sdbootutil mkinitrd "${SNAPSHOT_ID}" |& tee -a 
${LOGFILE} 1>&${origstdout}; then
+        if is_bls && ! sdbootutil mkinitrd "${SNAPSHOT_ID}" | log_info -o; then
             log_error "ERROR: sdbootutil mkinitrd failed!"
             EXITCODE=1;
-        elif ! is_bls && ! tukit ${TUKIT_OPTS} call "${SNAPSHOT_ID}" dracut 
${DRACUT_OPTS} --force --regenerate-all |& tee -a ${LOGFILE} 1>&${origstdout}; 
then
+        elif ! is_bls && ! tukit ${TUKIT_OPTS} call "${SNAPSHOT_ID}" dracut 
${DRACUT_OPTS} --force --regenerate-all | log_info -o; then
             log_error "ERROR: initrd creation failed!"
             EXITCODE=1
         else
@@ -1313,7 +1386,7 @@
         set_reboot_level "soft-reboot"
     fi
 
-    if is_bls && ([ ${REWRITE_GRUB_CFG} = 1 ] || [ 
${REWRITE_GRUB_CFG_NO_REBOOT} = 1 ]); then
+    if is_bls && { [ ${REWRITE_GRUB_CFG} = 1 ] || [ 
${REWRITE_GRUB_CFG_NO_REBOOT} = 1 ]; }; then
         # The first GRUB configuration file in grub2bls is embedded in
         # the EFI file, and the second one that contains the menu
         # entries is generated dynamically by the new `blscfg` GRUB2
@@ -1324,7 +1397,7 @@
     fi
     if [ ${REWRITE_GRUB_CFG} -eq 1 ] || [ ${REWRITE_GRUB_CFG_NO_REBOOT} -eq 1 
]; then
         log_info "Creating a new grub2 config"
-        if ! tukit ${TUKIT_OPTS} call "${SNAPSHOT_ID}" bash -c 
"/usr/sbin/grub2-mkconfig > /boot/grub2/grub.cfg" |& tee -a ${LOGFILE} 
1>&${origstdout}; then
+        if ! tukit ${TUKIT_OPTS} call "${SNAPSHOT_ID}" bash -c 
"/usr/sbin/grub2-mkconfig --output=/boot/grub2/grub.cfg" 2>&1 | log_info -o; 
then
             log_error "ERROR: grub2-mkconfig failed!"
             EXITCODE=1;
         else
@@ -1342,10 +1415,10 @@
         # NOTE: pbl has partial support of BLS. It will replace the
         # shim bootloader, so for now we make a call of sdbootutil
         # directly (bsc#1228864)
-        if is_bls && ! sdbootutil update "${SNAPSHOT_ID}" |& tee -a ${LOGFILE} 
1>&${origstdout}; then
+        if is_bls && ! sdbootutil update "${SNAPSHOT_ID}" | log_info -o; then
             log_error "ERROR: sdbootutil update failed!"
             EXITCODE=1;
-        elif ! is_bls && ! tukit ${TUKIT_OPTS} call "${SNAPSHOT_ID}" /sbin/pbl 
--install |& tee -a ${LOGFILE} 1>&${origstdout}; then
+        elif ! is_bls && ! tukit ${TUKIT_OPTS} call "${SNAPSHOT_ID}" /sbin/pbl 
--install | log_info -o; then
             log_error "ERROR: /sbin/pbl --install failed!"
             EXITCODE=1;
         fi
@@ -1353,7 +1426,8 @@
     fi
 
     if [ ${DO_RUN} -eq 1 ]; then
-        tukit ${TUKIT_OPTS} call "${SNAPSHOT_ID}" "${RUN_CMD[@]}" |& tee -a 
${LOGFILE} 1>&${origstdout}
+        tukit ${TUKIT_OPTS} call "${SNAPSHOT_ID}" "${RUN_CMD[@]}" | log_info -o
+        EXITCODE=${PIPESTATUS[0]}
         set_reboot_level "reboot"
     fi
 
@@ -1371,7 +1445,7 @@
         # Save the old snapshot or else it will get lost.
         add_unique_id "${CURRENT_SNAPSHOT_ID}"
         save_state_file "${SNAPSHOT_ID}"
-        tukit ${TUKIT_OPTS} close "${SNAPSHOT_ID}" |& tee -a ${LOGFILE}
+        tukit ${TUKIT_OPTS} close "${SNAPSHOT_ID}" | log_info
     fi
 
     # If --drop-if-no-change is used, then the snapshot may not exist any more;
@@ -1400,7 +1474,7 @@
         filelist="$(echo "$filelist" | grep -v "^\(${whitelist::-2}\)")"
 
         if [ -n "$filelist" ]; then
-            log_info
+            log_info ""
             log_info "Warning: The following files were changed in the 
snapshot, but are shadowed by"
             log_info "other mounts and will not be visible to the system:"
             log_info "${filelist}"
@@ -1414,7 +1488,7 @@
     write_needs_restarting
     if [ "${REBOOT_AFTERWARDS}" -eq 0 ] && [ "${DO_APPLY}" -eq 0 ]; then
         log_info ""
-        log_info "Please reboot your machine to activate the changes and avoid 
data loss."
+        log_warn "Please reboot your machine to activate the changes and avoid 
data loss."
     fi
 
     if [ "${DEFAULT_SNAPSHOT_ID}" -ne "${BASE_SNAPSHOT_ID}" ]; then
@@ -1444,30 +1518,30 @@
         trap '-' HUP INT QUIT TERM
         case "$REBOOT_METHOD" in
             auto)
-                tukit reboot auto |& tee -a ${LOGFILE}
+                tukit ${TUKIT_OPTS} reboot auto | log_info
                 ;;
             kured)
-                tukit reboot kured |& tee -a ${LOGFILE}
+                tukit ${TUKIT_OPTS} reboot kured | log_info
                 ;;
             rebootmgr)
-                tukit reboot rebootmgr |& tee -a ${LOGFILE}
+                tukit ${TUKIT_OPTS} reboot rebootmgr | log_info
                 ;;
             notify)
-                tukit reboot notify |& tee -a ${LOGFILE}
+                tukit ${TUKIT_OPTS} reboot notify | log_info
                 ;;
             systemd)
-                tukit reboot systemd |& tee -a ${LOGFILE}
+                tukit ${TUKIT_OPTS} reboot systemd | log_info
                 ;;
             kexec)
-                tukit reboot kexec |& tee -a ${LOGFILE}
+                tukit ${TUKIT_OPTS} reboot kexec | log_info
                 ;;
             none)
-                tukit reboot none |& tee -a ${LOGFILE}
+                tukit ${TUKIT_OPTS} reboot none | log_info
                 ;;
             *)
                 log_info "Unsupported reboot method, falling back to 'auto'; 
please"
                 log_info "check your configuration in ${CONFFILE}."
-                tukit reboot auto |& tee -a ${LOGFILE}
+                tukit ${TUKIT_OPTS} reboot auto | log_info
                 ;;
         esac
     fi
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/transactional-update-5.1.0/snapper/50-etc 
new/transactional-update-6.0.3/snapper/50-etc
--- old/transactional-update-5.1.0/snapper/50-etc       2025-08-18 
15:54:13.000000000 +0200
+++ new/transactional-update-6.0.3/snapper/50-etc       2026-01-09 
14:08:38.000000000 +0100
@@ -105,7 +105,7 @@
 if [ "$2" != "/" ]; then
   exit 0
 fi
-if [ -e "/.snapshots/${4}/snapshot/etc/fstab" ] && ! findmnt --mountpoint /etc 
--noheadings --output TARGET --tab-file "/.snapshots/${4}/snapshot/etc/fstab"; 
then
+if [ -e "/.snapshots/${4}/snapshot/etc/fstab" ] && ! findmnt --mountpoint /etc 
--noheadings --output TARGET --tab-file "/.snapshots/${4}/snapshot/etc/fstab" 
>/dev/null; then
   echo "/etc is not a mount point - skipping..."
   exit 0
 fi
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/transactional-update-5.1.0/tukit/tukit.cpp 
new/transactional-update-6.0.3/tukit/tukit.cpp
--- old/transactional-update-5.1.0/tukit/tukit.cpp      2025-08-18 
15:54:13.000000000 +0200
+++ new/transactional-update-6.0.3/tukit/tukit.cpp      2026-01-09 
14:08:38.000000000 +0100
@@ -72,13 +72,16 @@
     cout << "\n";
     cout << "Generic Options:\n";
     cout << "--help, -h                   Display this help and exit\n";
+    cout << "--log=<output1>[,<output2>,...], -l<...>\n";
+    cout << "                             Restrict output channels to the 
given ones\n";
+    cout << "                             Possible values: \"console\", 
\"syslog\"\n";
     cout << "--quiet, -q                  Decrease verbosity\n";
     cout << "--verbose, -v                Increase verbosity\n";
     cout << "--version, -V                Display version and exit\n" << endl;
 }
 
 int TUKit::parseOptions(int argc, char *argv[]) {
-    static const char optstring[] = "+c::df:hqvV";
+    static const char optstring[] = "+c::dkf:hl:qvV";
     static const struct option longopts[] = {
         { "continue", optional_argument, nullptr, 'c' },
         { "description", required_argument, nullptr, 0 },
@@ -86,6 +89,7 @@
         { "discard", no_argument, nullptr, 'd' },
         { "fields", required_argument, nullptr, 'f' },
         { "help", no_argument, nullptr, 'h' },
+        { "log", required_argument, nullptr, 'l' },
         { "quiet", no_argument, nullptr, 'q' },
         { "verbose", no_argument, nullptr, 'v' },
         { "version", no_argument, nullptr, 'V' },
@@ -118,6 +122,9 @@
         case 'h':
             displayHelp();
             return 0;
+        case 'l':
+            tulog.setLogOutput(optarg);
+            break;
         case 'q':
             tulog.level = TULogLevel::Error;
             break;

Reply via email to