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;