Current Behavior
On a dnf5 transaction with a system that has both the libdnf5 actions plugin
and the RPM plugin installed (the default after dnf install atomic-rollback),
the snapshot hook fires twice and produces two messages. Observed during a
dnf remove transaction:
Snapshot 'root.pre-update' already exists.
Snapshot 'root.pre-update' already exists.
Both messages come from the same Command::Snapshot code path in
src/main.rs:180. Each is emitted by a separate invocation of
/usr/bin/atomic-rollback snapshot: one from the libdnf5 actions plugin at
pre_transaction, and one from the RPM plugin's tsm_pre hook triggered when
libdnf5 invokes librpm later in the same transaction. On a transaction where
no prior snapshot exists, the first invocation would emit "created" and the
second "already exists", still producing two messages for one logical
transaction.
Expected Behavior
A single dnf5 transaction should produce at most one snapshot-related message
from the atomic-rollback integration, regardless of how many plugin layers the
tool hooks into. The user should not see the tool reporting snapshot actions
twice for the same transaction.
Context
The libdnf5 actions plugin and the RPM plugin target pre-transaction hooks at
different layers of the stack. libdnf5 calls into librpm to execute the actual
transaction, so when a dnf5 transaction runs, both layers' pre-transaction
hooks fire and each invokes /usr/bin/atomic-rollback snapshot. This is not
visible on pure-rpm or non-dnf5 transactions (only the RPM plugin fires) or
on dnf5 transactions with only one of the two plugins installed. It is only
observable when both plugins are registered, which is the default state after
installing the package.
There is no correctness impact because the snapshot command is idempotent:
the first invocation creates the snapshot (or no-ops if it already exists),
and the second sees it already exists and reports that. But the duplicate
output is confusing. It suggests something is running twice, which it is, and
it is unclear to the user whether one of the plugins failed, whether the
snapshot was taken correctly, or whether the tool is in a bad state.
Discovered on atomic-rollback-0.3.7-1.fc43 during a dnf5 package removal.
Technical Details
Reproduction
With atomic-rollback 0.3.7 installed via dnf (which installs both plugins),
run any dnf5 transaction that modifies packages:
$ sudo dnf install <any-package>
Observe two snapshot-related messages from the atomic-rollback hook for the
single transaction.
Relevant Code
Both plugins invoke /usr/bin/atomic-rollback snapshot at pre-transaction
time.
plugins/atomic-rollback.actions installed at
/etc/dnf/libdnf5-plugins/actions.d/atomic-rollback.actions:
pre_transaction:::raise_error=1:atomic-rollback snapshot
plugins/atomic_rollback.c installed at
/usr/lib64/rpm-plugins/atomic_rollback.so:
static rpmRC atomic_rollback_tsm_pre(rpmPlugin plugin, rpmts ts)
{
if (rpmtsFlags(ts) & (RPMTRANS_FLAG_TEST|RPMTRANS_FLAG_BUILD_PROBS))
return RPMRC_OK;
if (access(BINARY, X_OK) != 0)
return RPMRC_OK;
int rc = system(BINARY " snapshot");
if (rc != 0) {
rpmlog(RPMLOG_ERR, "atomic-rollback: snapshot failed, aborting transaction\n");
return RPMRC_FAIL;
}
return RPMRC_OK;
}
Both call the same Command::Snapshot entrypoint in src/main.rs:175-184,
which emits the user-visible message:
Command::Snapshot(sub) => match sub {
SnapshotCommand::Create { name } => {
match snapshot::snapshot(name.as_deref()) {
Ok(snapshot::SnapshotResult::Created(name)) =>
eprintln!("Snapshot '{name}' created."),
Ok(snapshot::SnapshotResult::Existed(name)) =>
eprintln!("Snapshot '{name}' already exists."),
...
}
}
Root Cause
Neither plugin is aware of the other. Each plugin's hook fires independently
at its respective layer (libdnf5 pre_transaction, librpm tsm_pre) and
each unconditionally invokes atomic-rollback snapshot. When libdnf5 drives
a transaction, it calls through to librpm, so both layers fire for the same
user-initiated transaction. The tool has no mechanism to detect that a
sibling plugin has already performed the snapshot for this transaction.
Current Behavior
On a dnf5 transaction with a system that has both the libdnf5 actions plugin
and the RPM plugin installed (the default after
dnf install atomic-rollback),the snapshot hook fires twice and produces two messages. Observed during a
dnf removetransaction:Both messages come from the same
Command::Snapshotcode path insrc/main.rs:180. Each is emitted by a separate invocation of/usr/bin/atomic-rollback snapshot: one from the libdnf5 actions plugin atpre_transaction, and one from the RPM plugin'stsm_prehook triggered whenlibdnf5 invokes librpm later in the same transaction. On a transaction where
no prior snapshot exists, the first invocation would emit "created" and the
second "already exists", still producing two messages for one logical
transaction.
Expected Behavior
A single dnf5 transaction should produce at most one snapshot-related message
from the atomic-rollback integration, regardless of how many plugin layers the
tool hooks into. The user should not see the tool reporting snapshot actions
twice for the same transaction.
Context
The libdnf5 actions plugin and the RPM plugin target pre-transaction hooks at
different layers of the stack. libdnf5 calls into librpm to execute the actual
transaction, so when a dnf5 transaction runs, both layers' pre-transaction
hooks fire and each invokes
/usr/bin/atomic-rollback snapshot. This is notvisible on pure-rpm or non-dnf5 transactions (only the RPM plugin fires) or
on dnf5 transactions with only one of the two plugins installed. It is only
observable when both plugins are registered, which is the default state after
installing the package.
There is no correctness impact because the
snapshotcommand is idempotent:the first invocation creates the snapshot (or no-ops if it already exists),
and the second sees it already exists and reports that. But the duplicate
output is confusing. It suggests something is running twice, which it is, and
it is unclear to the user whether one of the plugins failed, whether the
snapshot was taken correctly, or whether the tool is in a bad state.
Discovered on
atomic-rollback-0.3.7-1.fc43during a dnf5 package removal.Technical Details
Reproduction
With atomic-rollback 0.3.7 installed via dnf (which installs both plugins),
run any dnf5 transaction that modifies packages:
Observe two snapshot-related messages from the atomic-rollback hook for the
single transaction.
Relevant Code
Both plugins invoke
/usr/bin/atomic-rollback snapshotat pre-transactiontime.
plugins/atomic-rollback.actionsinstalled at/etc/dnf/libdnf5-plugins/actions.d/atomic-rollback.actions:plugins/atomic_rollback.cinstalled at/usr/lib64/rpm-plugins/atomic_rollback.so:Both call the same
Command::Snapshotentrypoint insrc/main.rs:175-184,which emits the user-visible message:
Root Cause
Neither plugin is aware of the other. Each plugin's hook fires independently
at its respective layer (libdnf5
pre_transaction, librpmtsm_pre) andeach unconditionally invokes
atomic-rollback snapshot. When libdnf5 drivesa transaction, it calls through to librpm, so both layers fire for the same
user-initiated transaction. The tool has no mechanism to detect that a
sibling plugin has already performed the snapshot for this transaction.