How to pause an update indefinitely for a power cycle: State Script vs DBus?

Hello everyone,

I am trying to implement an update that completes its installation but waits for a manual power cycle to reboot and commit the new image. I have found two possible methods but see some conflicting information and would appreciate some clarification.

State Script Approach

The documentation for state scripts suggests this is possible. It contains a tip that stalling in the ArtifactReboot_Enter state script will wait for a power cycle without failing the update.

Tip: Since a power loss in ArtifactReboot_Enter will not be marked as a failure, this enables the user to stall the daemon in ArtifactReboot_Enter state forever, and thus an install will only be installed on a power cycle of the device.

My plan was to use an ArtifactReboot_Enter script with a simple infinite loop, like this:

#!/bin/sh
while true; do
    sleep 3600
done
exit 0

However, another forum post indicates this will not work as intended. It says the Mender client will eventually time out the script, to prevent loops bricking the device. I think this timeout is set by the StateScriptTimeoutSeconds parameter, which defaults to one hour. This seems to contradict the documentation’s tip about stalling “forever”.

Could you clarify if the script timeout will indeed prevent this approach from working? Is the documentation tip perhaps outdated or applicable only in specific configurations?

Update Control Framework Approach

The same forum post suggested using the DBus API and the Update Control framework as an alternative to pause the update flow indefinitely. The documentation points to a Python example for this.

My main concern here is that the GitHub repository containing this example (mender-client-python-example) is archived. This makes me unsure if this is still a relevant or recommended solution.

Is using the DBus API still the correct way to achieve an indefinite pause? If so, are there any current examples or documentation available that I should be looking at?

Question: What is the current best practice for having a Mender update wait indefinitely for a user to power cycle the device before it reboots? Should I be using the state script method, and if so, how do I correctly configure it to avoid a timeout? Or is the DBus API the recommended path?

Thank you for your help!

After further investigation, I’ve confirmed that the D-Bus API for synchronized updates is deprecated as of Mender 3.6.

While the documentation was somewhat unclear, the Mender 3.6 release notes are explicit. They state that the synchronized updates feature is being used when “Setting the control map on the device using the DBus API.”

Several other findings support this conclusion:

  1. Running the gdbus call to set the update control map returns (0,), which is an unexpected result for a refresh_timeout.
    sudo gdbus call --system --dest io.mender.UpdateManager --object-path /io/mender/UpdateManager --method io.mender.Update1.SetUpdateControlMap '{"priority": 1, "states": {"ArtifactInstall_Enter": {"action": "pause"}}, "id": "01234567-89ab-cdef-0123-456789abcdef"}'
    
  2. The official mender-client-python-example repository on GitHub is archived and produces an error when run.
  3. The forum post I was referencing was created before the Mender 3.6 release, when the feature was still active.

With the D-Bus option unavailable, the only viable solution seems to be to increase StateScriptTimeoutSeconds in /etc/mender/mender.conf to something like 12 hours and use the state script outlined in my initial post.

On a related note, I feel that a limitation of the Mender client is the lack of a simple interface for observing its current status on the device side. While state scripts are effective for reacting to state changes and they can export these changes to other applications on the system, they do not seem to provide a mechanism for tracking other useful metrics, such as real-time download progress. Maybe a unix socket that outputs state information would already be enough. Something to think about!

Hey, I would suggest taking a look at retry later, StateScriptRetryTimeoutSeconds and StateScriptRetryIntervalSeconds.