• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mendersoftware / mender / 1996238187

21 Aug 2025 12:14PM UTC coverage: 75.933%. Remained the same
1996238187

push

gitlab-ci

danielskinstad
fix: add explicit check and handling for aborted deployments

Ticket: ME-527
Changelog: Add a new state event, `DeploymentAborted`, which is posted and
checked whenever we send status updates to the server. Each state that
sends status now explicitly checks if the deployment is aborted and
handles it accordingly. Previously it was only handled in the commit
state, which meant that if you aborted a deployment during e.g. download,
the device could still reboot.

Signed-off-by: Daniel Skinstad Drabitzius <daniel.drabitzius@northern.tech>

9 of 10 new or added lines in 3 files covered. (90.0%)

36 existing lines in 3 files now uncovered.

7383 of 9723 relevant lines covered (75.93%)

13941.02 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.86
/src/mender-update/daemon/state_machine/state_machine.cpp
1
// Copyright 2023 Northern.tech AS
2
//
3
//    Licensed under the Apache License, Version 2.0 (the "License");
4
//    you may not use this file except in compliance with the License.
5
//    You may obtain a copy of the License at
6
//
7
//        http://www.apache.org/licenses/LICENSE-2.0
8
//
9
//    Unless required by applicable law or agreed to in writing, software
10
//    distributed under the License is distributed on an "AS IS" BASIS,
11
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
//    See the License for the specific language governing permissions and
13
//    limitations under the License.
14

15
#include <mender-update/daemon/state_machine.hpp>
16

17
#include <client_shared/conf.hpp>
18
#include <common/key_value_database.hpp>
19
#include <common/log.hpp>
20

21
#include <mender-update/daemon/states.hpp>
22

23
namespace mender {
24
namespace update {
25
namespace daemon {
26

27
namespace conf = mender::client_shared::conf;
28
namespace kvdb = mender::common::key_value_database;
29
namespace log = mender::common::log;
30

31
StateMachine::StateMachine(Context &ctx, events::EventLoop &event_loop) :
96✔
32
        ctx_(ctx),
33
        event_loop_(event_loop),
34
        check_update_handler_(event_loop),
35
        inventory_update_handler_(event_loop),
36
        termination_handler_(event_loop),
37
        schedule_submit_inventory_state_(
38
                ctx.inventory_timer,
96✔
39
                "inventory submission",
40
                StateEvent::InventoryPollingTriggered,
41
                ctx.mender_context.GetConfig().inventory_poll_interval_seconds),
96✔
42
        schedule_poll_for_deployment_state_(
43
                ctx.deployment_timer,
96✔
44
                "deployment check",
45
                StateEvent::DeploymentPollingTriggered,
46
                ctx.mender_context.GetConfig().update_poll_interval_seconds),
96✔
47
        submit_inventory_state_(
48
                ctx.mender_context.GetConfig().retry_poll_interval_seconds,
49
                ctx.mender_context.GetConfig().retry_poll_count),
96✔
50
        poll_for_deployment_state_(
51
                ctx.mender_context.GetConfig().retry_poll_interval_seconds,
52
                ctx.mender_context.GetConfig().retry_poll_count),
96✔
53
        send_download_status_state_(deployments::DeploymentStatus::Downloading),
54
        send_install_status_state_(deployments::DeploymentStatus::Installing),
55
        send_reboot_status_state_(deployments::DeploymentStatus::Rebooting),
56
        send_commit_status_state_(
57
                deployments::DeploymentStatus::Installing,
58
                event_loop,
59
                ctx.mender_context.GetConfig().retry_poll_interval_seconds,
60
                ctx.mender_context.GetConfig().retry_poll_count),
96✔
61
        // nullopt means: Fetch success/failure status from deployment context
62
        send_final_status_state_(
63
                nullopt,
64
                event_loop,
65
                ctx.mender_context.GetConfig().retry_poll_interval_seconds,
66
                ctx.mender_context.GetConfig().retry_poll_count),
96✔
67
        exit_state_(event_loop),
68
        main_states_(init_state_),
69
        state_scripts_(
70
                event_loop,
71
                chrono::seconds {ctx.mender_context.GetConfig().state_script_timeout_seconds},
72
                chrono::seconds {ctx.mender_context.GetConfig().state_script_retry_interval_seconds},
73
                chrono::seconds {ctx.mender_context.GetConfig().state_script_retry_timeout_seconds},
96✔
74
                ctx_.mender_context.GetConfig().paths.GetArtScriptsPath(),
192✔
75
                ctx_.mender_context.GetConfig().paths.GetRootfsScriptsPath()),
192✔
76
        runner_(ctx) {
480✔
77
        runner_.AddStateMachine(deployment_tracking_.states_);
96✔
78
        runner_.AddStateMachine(main_states_);
96✔
79
        runner_.AttachToEventLoop(event_loop_);
96✔
80
        ctx.authenticator.RegisterTokenReceivedCallback([&ctx]() {
×
81
                if (ctx.inventory_client->has_submitted_inventory) {
×
82
                        log::Debug("Client has re-authenticated - clear inventory data cache");
×
83
                        ctx.inventory_client->ClearDataCache();
×
84
                        ctx.inventory_client->has_submitted_inventory = false;
×
85
                }
86
        });
×
87

88
        using se = StateEvent;
89
        using tf = sm::TransitionFlag;
90

91
        auto &ss = state_scripts_;
92

93
        // When updating the table below, make sure that the initial states are in sync as well, in
94
        // LoadStateFromDb().
95

96
        // clang-format off
97
        main_states_.AddTransition(init_state_,                             se::Started,                     ss.idle_enter_,                          tf::Immediate);
96✔
98

99
        main_states_.AddTransition(ss.idle_enter_,                          se::Success,                     idle_state_,                             tf::Immediate);
96✔
100
        main_states_.AddTransition(ss.idle_enter_,                          se::Failure,                     idle_state_,                             tf::Immediate);
96✔
101

102
        main_states_.AddTransition(idle_state_,                             se::DeploymentPollingTriggered,  schedule_poll_for_deployment_state_,     tf::Deferred);
96✔
103
        main_states_.AddTransition(idle_state_,                             se::InventoryPollingTriggered,   schedule_submit_inventory_state_,        tf::Deferred);
96✔
104

105
        // The schedule states cannot fail
106
        main_states_.AddTransition(schedule_poll_for_deployment_state_,     se::Success,                     ss.idle_leave_deploy_,                   tf::Immediate);
96✔
107
        main_states_.AddTransition(schedule_submit_inventory_state_,        se::Success,                     ss.idle_leave_inv_,                      tf::Immediate);
96✔
108

109
        main_states_.AddTransition(ss.idle_leave_deploy_,                   se::Success,                     ss.sync_enter_deployment_,               tf::Immediate);
96✔
110
        main_states_.AddTransition(ss.idle_leave_deploy_,                   se::Failure,                     ss.sync_enter_deployment_,               tf::Immediate);
96✔
111

112
        main_states_.AddTransition(ss.sync_enter_deployment_,               se::Success,                     poll_for_deployment_state_,              tf::Immediate);
96✔
113
        main_states_.AddTransition(ss.sync_enter_deployment_,               se::Failure,                     ss.sync_error_,                          tf::Immediate);
96✔
114

115
        main_states_.AddTransition(ss.sync_error_,                          se::Success,                     ss.idle_enter_,                          tf::Immediate);
96✔
116
        main_states_.AddTransition(ss.sync_error_,                          se::Failure,                     ss.idle_enter_,                          tf::Immediate);
96✔
117

118
        main_states_.AddTransition(ss.idle_leave_inv_,                      se::Success,                     ss.sync_enter_inventory_,                tf::Immediate);
96✔
119
        main_states_.AddTransition(ss.idle_leave_inv_,                      se::Failure,                     ss.sync_enter_inventory_,                tf::Immediate);
96✔
120

121
        main_states_.AddTransition(ss.sync_enter_inventory_,                se::Success,                     submit_inventory_state_,                 tf::Immediate);
96✔
122
        main_states_.AddTransition(ss.sync_enter_inventory_,                se::Failure,                     ss.sync_error_,                          tf::Immediate);
96✔
123

124
        main_states_.AddTransition(submit_inventory_state_,                 se::Success,                     ss.sync_leave_,                          tf::Immediate);
96✔
125
        main_states_.AddTransition(submit_inventory_state_,                 se::Failure,                     ss.sync_error_,                          tf::Immediate);
96✔
126

127
        main_states_.AddTransition(poll_for_deployment_state_,              se::Success,                     ss.sync_leave_download_,                 tf::Immediate);
96✔
128
        main_states_.AddTransition(poll_for_deployment_state_,              se::NothingToDo,                 ss.sync_leave_,                          tf::Immediate);
96✔
129
        main_states_.AddTransition(poll_for_deployment_state_,              se::Failure,                     ss.sync_error_,                          tf::Immediate);
96✔
130

131
        main_states_.AddTransition(ss.sync_leave_,                          se::Success,                     ss.idle_enter_,                          tf::Immediate);
96✔
132
        main_states_.AddTransition(ss.sync_leave_,                          se::Failure,                     ss.sync_error_,                          tf::Immediate);
96✔
133

134
        main_states_.AddTransition(ss.sync_leave_download_,                 se::Success,                     send_download_status_state_,             tf::Immediate);
96✔
135
        main_states_.AddTransition(ss.sync_leave_download_,                 se::Failure,                     ss.sync_error_download_,                 tf::Immediate);
96✔
136

137
        main_states_.AddTransition(ss.sync_error_download_,                 se::Success,                     end_of_deployment_state_,                tf::Immediate);
96✔
138
        main_states_.AddTransition(ss.sync_error_download_,                 se::Failure,                     end_of_deployment_state_,                tf::Immediate);
96✔
139

140
        // Cannot fail due to FailureMode::Ignore.
141
        main_states_.AddTransition(send_download_status_state_,             se::Success,                     ss.download_enter_,                      tf::Immediate);
96✔
142
        main_states_.AddTransition(send_download_status_state_,             se::DeploymentAborted,           update_cleanup_state_,                   tf::Immediate);
96✔
143

144
        main_states_.AddTransition(ss.download_enter_,                      se::Success,                     update_download_state_,                  tf::Immediate);
96✔
145
        main_states_.AddTransition(ss.download_enter_,                      se::Failure,                     ss.download_error_,                      tf::Immediate);
96✔
146
        main_states_.AddTransition(ss.download_enter_,                      se::StateLoopDetected,           state_loop_state_,                       tf::Immediate);
96✔
147
        main_states_.AddTransition(ss.download_error_,                      se::Success,                     update_rollback_not_needed_state_,       tf::Immediate);
96✔
148
        main_states_.AddTransition(ss.download_error_,                      se::Failure,                     update_rollback_not_needed_state_,       tf::Immediate);
96✔
149

150
        main_states_.AddTransition(update_download_state_,                  se::Success,                     ss.download_leave_,                      tf::Immediate);
96✔
151
        main_states_.AddTransition(update_download_state_,                  se::StateLoopDetected,           state_loop_state_,                       tf::Immediate);
96✔
152
        main_states_.AddTransition(update_download_state_,                  se::Failure,                     update_download_cancel_state_,           tf::Immediate);
96✔
153
        main_states_.AddTransition(update_download_state_,                  se::NothingToDo,                 ss.download_leave_save_provides,         tf::Immediate);
96✔
154

155
        // Cannot fail because download cancellation is a void function as there's nothing to do if it fails, anyway.
156
        main_states_.AddTransition(update_download_cancel_state_,           se::Success,                     ss.download_error_,                      tf::Immediate);
96✔
157

158
        main_states_.AddTransition(ss.download_leave_,                      se::Success,                     send_install_status_state_,              tf::Immediate);
96✔
159
        main_states_.AddTransition(ss.download_leave_,                      se::Failure,                     ss.download_error_,                      tf::Immediate);
96✔
160

161
        main_states_.AddTransition(ss.download_leave_save_provides,         se::Success,                     update_save_provides_state_,             tf::Immediate);
96✔
162
        main_states_.AddTransition(ss.download_leave_save_provides,         se::Failure,                     ss.download_error_,                      tf::Immediate);
96✔
163

164
        main_states_.AddTransition(ss.install_enter_,                       se::Success,                     update_install_state_,                   tf::Immediate);
96✔
165
        main_states_.AddTransition(ss.install_enter_,                       se::Failure,                     ss.install_error_rollback_,              tf::Immediate);
96✔
166

167
        // Cannot fail due to FailureMode::Ignore.
168
        main_states_.AddTransition(send_install_status_state_,              se::Success,                     ss.install_enter_,                       tf::Immediate);
96✔
169
        main_states_.AddTransition(send_install_status_state_,              se::DeploymentAborted,           update_cleanup_state_,                   tf::Immediate);
96✔
170

171
        main_states_.AddTransition(update_install_state_,                   se::Success,                     ss.install_leave_,                       tf::Immediate);
96✔
172
        main_states_.AddTransition(update_install_state_,                   se::Failure,                     ss.install_error_rollback_,              tf::Immediate);
96✔
173
        main_states_.AddTransition(update_install_state_,                   se::StateLoopDetected,           state_loop_state_,                       tf::Immediate);
96✔
174

175
        main_states_.AddTransition(ss.install_leave_,                       se::Success,                     update_check_reboot_state_,              tf::Immediate);
96✔
176
        main_states_.AddTransition(ss.install_leave_,                       se::Failure,                     ss.install_error_rollback_,              tf::Immediate);
96✔
177
        main_states_.AddTransition(ss.install_error_rollback_,              se::Success,                     update_check_rollback_state_,            tf::Immediate);
96✔
178
        main_states_.AddTransition(ss.install_error_rollback_,              se::Failure,                     update_check_rollback_state_,            tf::Immediate);
96✔
179

180
        main_states_.AddTransition(ss.failure_enter_,                       se::Success,                     update_failure_state_,                   tf::Immediate);
96✔
181
        main_states_.AddTransition(ss.failure_enter_,                       se::Failure,                     update_failure_state_,                   tf::Immediate);
96✔
182
        main_states_.AddTransition(ss.failure_enter_,                       se::StateLoopDetected,           state_loop_state_,                       tf::Immediate);
96✔
183

184

185
        main_states_.AddTransition(update_check_reboot_state_,              se::Success,                     send_reboot_status_state_,               tf::Immediate);
96✔
186
        main_states_.AddTransition(update_check_reboot_state_,              se::NothingToDo,                 update_before_commit_state_,             tf::Immediate);
96✔
187
        main_states_.AddTransition(update_check_reboot_state_,              se::Failure,                     update_check_rollback_state_,            tf::Immediate);
96✔
188
        main_states_.AddTransition(update_check_reboot_state_,              se::StateLoopDetected,           state_loop_state_,                       tf::Immediate);
96✔
189

190
        // Cannot fail due to FailureMode::Ignore.
191
        main_states_.AddTransition(send_reboot_status_state_,               se::Success,                     ss.reboot_enter_,                        tf::Immediate);
96✔
192
        main_states_.AddTransition(send_reboot_status_state_,               se::DeploymentAborted,           update_check_rollback_state_,            tf::Immediate);
96✔
193

194
        main_states_.AddTransition(ss.reboot_enter_,                        se::Success,                     update_reboot_state_,                    tf::Immediate);
96✔
195
        main_states_.AddTransition(ss.reboot_enter_,                        se::Failure,                     ss.reboot_error_,                        tf::Immediate);
96✔
196

197
        main_states_.AddTransition(update_reboot_state_,                    se::Success,                     update_verify_reboot_state_,             tf::Immediate);
96✔
198
        main_states_.AddTransition(update_reboot_state_,                    se::Failure,                     ss.reboot_error_,                        tf::Immediate);
96✔
199
        main_states_.AddTransition(update_reboot_state_,                    se::StateLoopDetected,           state_loop_state_,                       tf::Immediate);
96✔
200

201
        main_states_.AddTransition(ss.reboot_error_,                        se::Success,                     update_check_rollback_state_,            tf::Immediate);
96✔
202
        main_states_.AddTransition(ss.reboot_error_,                        se::Failure,                     update_check_rollback_state_,            tf::Immediate);
96✔
203

204
        main_states_.AddTransition(update_verify_reboot_state_,             se::Success,                     ss.reboot_leave_,                        tf::Immediate);
96✔
205
        main_states_.AddTransition(update_verify_reboot_state_,             se::Failure,                     ss.reboot_error_,                        tf::Immediate);
96✔
206
        main_states_.AddTransition(update_verify_reboot_state_,             se::StateLoopDetected,           state_loop_state_,                       tf::Immediate);
96✔
207

208
        main_states_.AddTransition(ss.reboot_leave_,                        se::Success,                     update_before_commit_state_,             tf::Immediate);
96✔
209
        main_states_.AddTransition(ss.reboot_leave_,                        se::Failure,                     ss.reboot_error_,                        tf::Immediate);
96✔
210

211
        // Cannot fail.
212
        main_states_.AddTransition(update_before_commit_state_,             se::Success,                     send_commit_status_state_,               tf::Immediate);
96✔
213

214
        main_states_.AddTransition(send_commit_status_state_,               se::Success,                     ss.commit_enter_,                        tf::Immediate);
96✔
215
        main_states_.AddTransition(send_commit_status_state_,               se::Failure,                     update_check_rollback_state_,            tf::Immediate);
96✔
216
        main_states_.AddTransition(send_commit_status_state_,               se::DeploymentAborted,           update_check_rollback_state_,            tf::Immediate);
96✔
217

218
        main_states_.AddTransition(ss.commit_enter_,                        se::Success,                     update_commit_state_,                    tf::Immediate);
96✔
219
        main_states_.AddTransition(ss.commit_enter_,                        se::Failure,                     ss.commit_error_,                        tf::Immediate);
96✔
220

221
        main_states_.AddTransition(ss.commit_error_,                        se::Success,                     update_check_rollback_state_,            tf::Immediate);
96✔
222
        main_states_.AddTransition(ss.commit_error_,                        se::Failure,                     update_check_rollback_state_,            tf::Immediate);
96✔
223

224
        main_states_.AddTransition(update_commit_state_,                    se::Success,                     update_after_commit_state_,              tf::Immediate);
96✔
225
        main_states_.AddTransition(update_commit_state_,                    se::Failure,                     ss.commit_error_,                        tf::Immediate);
96✔
226
        main_states_.AddTransition(update_commit_state_,                    se::StateLoopDetected,           state_loop_state_,                       tf::Immediate);
96✔
227

228
        main_states_.AddTransition(update_after_commit_state_,              se::Success,                     ss.commit_leave_,                        tf::Immediate);
96✔
229
        main_states_.AddTransition(update_after_commit_state_,              se::Failure,                     ss.commit_error_save_provides_,          tf::Immediate);
96✔
230
        main_states_.AddTransition(update_after_commit_state_,              se::StateLoopDetected,           state_loop_state_,                       tf::Immediate);
96✔
231

232
        main_states_.AddTransition(ss.commit_leave_,                        se::Success,                     update_save_provides_state_,             tf::Immediate);
96✔
233
        main_states_.AddTransition(ss.commit_leave_,                        se::Failure,                     ss.commit_error_save_provides_,          tf::Immediate);
96✔
234

235
        main_states_.AddTransition(ss.commit_error_save_provides_,          se::Success,                     update_save_provides_state_,             tf::Immediate);
96✔
236
        main_states_.AddTransition(ss.commit_error_save_provides_,          se::Failure,                     update_save_provides_state_,             tf::Immediate);
96✔
237

238
        main_states_.AddTransition(update_check_rollback_state_,            se::Success,                     ss.rollback_enter_,                      tf::Immediate);
96✔
239
        main_states_.AddTransition(update_check_rollback_state_,            se::NothingToDo,                 ss.failure_enter_,                       tf::Immediate);
96✔
240
        main_states_.AddTransition(update_check_rollback_state_,            se::Failure,                     ss.failure_enter_,                       tf::Immediate);
96✔
241
        main_states_.AddTransition(update_check_rollback_state_,            se::StateLoopDetected,           state_loop_state_,                       tf::Immediate);
96✔
242

243
        main_states_.AddTransition(ss.rollback_enter_,                      se::Success,                     update_rollback_state_,                  tf::Immediate);
96✔
244
        main_states_.AddTransition(ss.rollback_enter_,                      se::Failure,                     update_rollback_state_,                  tf::Immediate);
96✔
245

246
        main_states_.AddTransition(update_rollback_state_,                  se::Success,                     ss.rollback_leave_,                      tf::Immediate);
96✔
247
        main_states_.AddTransition(update_rollback_state_,                  se::Failure,                     ss.rollback_leave_error_,                tf::Immediate);
96✔
248
        main_states_.AddTransition(update_rollback_state_,                  se::StateLoopDetected,           state_loop_state_,                       tf::Immediate);
96✔
249

250
        main_states_.AddTransition(ss.rollback_leave_,                      se::Success,                     update_check_rollback_reboot_state_,     tf::Immediate);
96✔
251
        main_states_.AddTransition(ss.rollback_leave_,                      se::Failure,                     update_check_rollback_reboot_state_,     tf::Immediate);
96✔
252

253
        main_states_.AddTransition(ss.rollback_leave_error_,                se::Success,                     ss.failure_enter_,                       tf::Immediate);
96✔
254
        main_states_.AddTransition(ss.rollback_leave_error_,                se::Failure,                     ss.failure_enter_,                       tf::Immediate);
96✔
255

256
        main_states_.AddTransition(update_check_rollback_reboot_state_,     se::Success,                     ss.rollback_reboot_enter_,               tf::Immediate);
96✔
257
        main_states_.AddTransition(update_check_rollback_reboot_state_,     se::NothingToDo,                 update_rollback_successful_state_,       tf::Immediate);
96✔
258
        main_states_.AddTransition(update_check_rollback_reboot_state_,     se::Failure,                     ss.failure_enter_,                       tf::Immediate);
96✔
259
        main_states_.AddTransition(update_check_rollback_reboot_state_,     se::StateLoopDetected,           state_loop_state_,                       tf::Immediate);
96✔
260

261
        main_states_.AddTransition(ss.rollback_reboot_enter_,               se::Success,                     update_rollback_reboot_state_,           tf::Immediate);
96✔
262
        main_states_.AddTransition(ss.rollback_reboot_enter_,               se::Failure,                     update_rollback_reboot_state_,           tf::Immediate);
96✔
263

264
        main_states_.AddTransition(ss.rollback_reboot_error_,               se::Success,                     ss.failure_enter_,                       tf::Immediate);
96✔
265
        main_states_.AddTransition(ss.rollback_reboot_error_,               se::Failure,                     ss.failure_enter_,                       tf::Immediate);
96✔
266

267
        // No Failure transition for this state see comments in handler.
268
        main_states_.AddTransition(update_rollback_reboot_state_,           se::Success,                     update_verify_rollback_reboot_state_,    tf::Immediate);
96✔
269
        main_states_.AddTransition(update_rollback_reboot_state_,           se::StateLoopDetected,           state_loop_state_,                       tf::Immediate);
96✔
270

271
        main_states_.AddTransition(update_verify_rollback_reboot_state_,    se::Success,                     ss.rollback_reboot_leave_,               tf::Immediate);
96✔
272
        main_states_.AddTransition(update_verify_rollback_reboot_state_,    se::Retry,                       update_rollback_reboot_state_,           tf::Immediate);
96✔
273
        main_states_.AddTransition(update_verify_rollback_reboot_state_,    se::StateLoopDetected,           state_loop_state_,                       tf::Immediate);
96✔
274

275
        main_states_.AddTransition(ss.rollback_reboot_leave_,               se::Success,                     update_rollback_successful_state_,       tf::Immediate);
96✔
276
        main_states_.AddTransition(ss.rollback_reboot_leave_,               se::Failure,                     ss.rollback_reboot_error_,               tf::Immediate);
96✔
277

278
        main_states_.AddTransition(update_rollback_successful_state_,       se::Success,                     ss.failure_enter_,                       tf::Immediate);
96✔
279

280
        main_states_.AddTransition(update_failure_state_,                   se::Success,                     ss.failure_leave_update_save_provides_,  tf::Immediate);
96✔
281
        main_states_.AddTransition(update_failure_state_,                   se::Failure,                     ss.failure_leave_update_save_provides_,  tf::Immediate);
96✔
282
        main_states_.AddTransition(update_failure_state_,                   se::StateLoopDetected,           state_loop_state_,                       tf::Immediate);
96✔
283

284
        main_states_.AddTransition(ss.failure_leave_update_save_provides_,  se::Success,                     update_save_provides_state_,             tf::Immediate);
96✔
285
        main_states_.AddTransition(ss.failure_leave_update_save_provides_,  se::Failure,                     update_save_provides_state_,             tf::Immediate);
96✔
286

287
        // Even if this fails there is nothing we can do at this point.
288
        main_states_.AddTransition(update_save_provides_state_,             se::Success,                     update_cleanup_state_,                   tf::Immediate);
96✔
289
        main_states_.AddTransition(update_save_provides_state_,             se::Failure,                     update_cleanup_state_,                   tf::Immediate);
96✔
290
        main_states_.AddTransition(update_save_provides_state_,             se::StateLoopDetected,           state_loop_state_,                       tf::Immediate);
96✔
291

292
        main_states_.AddTransition(update_rollback_not_needed_state_,       se::Success,                     update_cleanup_state_,                   tf::Immediate);
96✔
293

294
        main_states_.AddTransition(update_cleanup_state_,                   se::Success,                     send_final_status_state_,                tf::Immediate);
96✔
295
        main_states_.AddTransition(update_cleanup_state_,                   se::Failure,                     send_final_status_state_,                tf::Immediate);
96✔
296
        main_states_.AddTransition(update_cleanup_state_,                   se::StateLoopDetected,           state_loop_state_,                       tf::Immediate);
96✔
297

298
        main_states_.AddTransition(state_loop_state_,                       se::Success,                     send_final_status_state_,                tf::Immediate);
96✔
299
        main_states_.AddTransition(state_loop_state_,                       se::Failure,                     send_final_status_state_,                tf::Immediate);
96✔
300

301
        main_states_.AddTransition(send_final_status_state_,                se::Success,                     clear_artifact_data_state_,              tf::Immediate);
96✔
302
        main_states_.AddTransition(send_final_status_state_,                se::Failure,                     clear_artifact_data_state_,              tf::Immediate);
96✔
303
        main_states_.AddTransition(send_final_status_state_,                se::DeploymentAborted,           clear_artifact_data_state_,              tf::Immediate);
96✔
304

305
        main_states_.AddTransition(clear_artifact_data_state_,              se::Success,                     end_of_deployment_state_,                tf::Immediate);
96✔
306
        main_states_.AddTransition(clear_artifact_data_state_,              se::Failure,                     end_of_deployment_state_,                tf::Immediate);
96✔
307

308
        main_states_.AddTransition(end_of_deployment_state_,                se::Success,                     ss.idle_enter_,                          tf::Immediate);
96✔
309

310
        auto &dt = deployment_tracking_;
311

312
        dt.states_.AddTransition(dt.idle_state_,                            se::DeploymentStarted,           dt.no_failures_state_,                   tf::Immediate);
96✔
313

314
        dt.states_.AddTransition(dt.no_failures_state_,                     se::Failure,                     dt.failure_state_,                       tf::Immediate);
96✔
315
        dt.states_.AddTransition(dt.no_failures_state_,                     se::DeploymentAborted,           dt.failure_state_,                       tf::Immediate);
96✔
316
        dt.states_.AddTransition(dt.no_failures_state_,                     se::DeploymentEnded,             dt.idle_state_,                          tf::Immediate);
96✔
317

318
        dt.states_.AddTransition(dt.failure_state_,                         se::RollbackStarted,             dt.rollback_attempted_state_,            tf::Immediate);
96✔
319
        dt.states_.AddTransition(dt.failure_state_,                         se::DeploymentEnded,             dt.idle_state_,                          tf::Immediate);
96✔
320

321
        dt.states_.AddTransition(dt.rollback_attempted_state_,              se::Failure,                     dt.rollback_failed_state_,               tf::Immediate);
96✔
322
        dt.states_.AddTransition(dt.rollback_attempted_state_,              se::DeploymentEnded,             dt.idle_state_,                          tf::Immediate);
96✔
323

324
        dt.states_.AddTransition(dt.rollback_failed_state_,                 se::DeploymentEnded,             dt.idle_state_,                          tf::Immediate);
96✔
325
        // clang-format on
326
}
96✔
327

328
StateMachine::StateMachine(
91✔
329
        Context &ctx, events::EventLoop &event_loop, chrono::milliseconds minimum_wait_time) :
91✔
330
        StateMachine(ctx, event_loop) {
91✔
331
        send_commit_status_state_.SetSmallestWaitInterval(minimum_wait_time);
91✔
332
        send_final_status_state_.SetSmallestWaitInterval(minimum_wait_time);
91✔
333
}
91✔
334

335
StateMachine::DeploymentTracking::DeploymentTracking() :
96✔
336
        states_(idle_state_) {
96✔
337
}
96✔
338

339
void StateMachine::LoadStateFromDb() {
92✔
340
        unique_ptr<StateData> state_data(new StateData);
126✔
341
        auto exp_loaded = ctx_.LoadDeploymentStateData(*state_data);
92✔
342
        if (!exp_loaded) {
92✔
343
                if (exp_loaded.error().code
2✔
344
                        == context::MakeError(context::StateDataStoreCountExceededError, "").code) {
2✔
345
                        log::Error("State loop detected. Forcefully aborting update.");
2✔
346

347
                        // This particular error code also fills in state_data.
348
                        ctx_.deployment.state_data = std::move(state_data);
1✔
349

350
                        ctx_.BeginDeploymentLogging();
1✔
351

352
                        main_states_.SetState(state_loop_state_);
1✔
353
                        deployment_tracking_.states_.SetState(deployment_tracking_.rollback_failed_state_);
1✔
354
                } else {
355
                        log::Error(
1✔
356
                                "Unable to load deployment data from database: " + exp_loaded.error().String());
2✔
357
                        log::Error("Starting from initial state");
2✔
358
                }
359
                return;
2✔
360
        }
361

362
        auto &store = ctx_.mender_context.GetMenderStoreDB();
90✔
363

364
        if (!exp_loaded.value()) {
90✔
365
                log::Debug("No existing deployment data, starting from initial state");
110✔
366

367
                auto err = store.Remove(ctx_.mender_context.update_control_maps);
55✔
368
                if (err != error::NoError) {
55✔
UNCOV
369
                        log::Error(
×
UNCOV
370
                                "Error removing " + ctx_.mender_context.update_control_maps
×
UNCOV
371
                                + " key from database: " + err.String());
×
372
                        // Nothing we can do about it.
373
                }
374

375
                return;
376
        }
377

378
        // We have state data, move it to the context.
379
        ctx_.deployment.state_data = std::move(state_data);
35✔
380

381
        ctx_.BeginDeploymentLogging();
35✔
382

383
        bool update_control_enabled = false;
384
        auto exp_update_control_data = store.Read(ctx_.mender_context.update_control_maps);
35✔
385
        if (exp_update_control_data) {
35✔
386
                auto update_control_data = common::StringFromByteVector(exp_update_control_data.value());
2✔
387
                if (update_control_data != "" && update_control_data != "{}") {
2✔
388
                        update_control_enabled = true;
389
                }
390
        } else if (exp_update_control_data.error().code != kvdb::MakeError(kvdb::KeyError, "").code) {
33✔
UNCOV
391
                log::Error("Error while loading update control data from database");
×
392
                // Since we don't actually need it, continue anyway.
393
        }
394

395
        auto &state = ctx_.deployment.state_data->state;
35✔
396

397
        if (state == ctx_.kUpdateStateDownload) {
35✔
398
                main_states_.SetState(update_cleanup_state_);
3✔
399
                // "rollback_attempted_state" because Download in its nature makes no system
400
                // changes, so a rollback is a no-op.
401
                deployment_tracking_.states_.SetState(deployment_tracking_.rollback_attempted_state_);
3✔
402

403
        } else if (state == ctx_.kUpdateStateArtifactReboot) {
32✔
404
                // Normal update path with a reboot.
405
                if (update_control_enabled) {
5✔
406
                        log::Error(
1✔
407
                                "This deployment was done using Update Control, but this client does not support Update Control. Failing / rolling back deployment.");
2✔
408
                        main_states_.SetState(state_scripts_.reboot_error_);
1✔
409
                        deployment_tracking_.states_.SetState(deployment_tracking_.failure_state_);
1✔
410
                } else {
411
                        main_states_.SetState(update_verify_reboot_state_);
4✔
412
                        deployment_tracking_.states_.SetState(deployment_tracking_.no_failures_state_);
4✔
413
                }
414

415
        } else if (state == ctx_.kUpdateStateArtifactRollback) {
27✔
416
                // Installation failed, but rollback could still succeed.
417
                main_states_.SetState(state_scripts_.rollback_enter_);
3✔
418
                deployment_tracking_.states_.SetState(deployment_tracking_.rollback_attempted_state_);
3✔
419

420
        } else if (
421
                state == ctx_.kUpdateStateArtifactRollbackReboot
24✔
422
                || state == ctx_.kUpdateStateArtifactVerifyRollbackReboot
22✔
423
                || state == ctx_.kUpdateStateVerifyRollbackReboot) {
44✔
424
                // Normal flow for a rebooting rollback.
425
                main_states_.SetState(update_verify_rollback_reboot_state_);
4✔
426
                deployment_tracking_.states_.SetState(deployment_tracking_.rollback_attempted_state_);
4✔
427

428
        } else if (
429
                state == ctx_.kUpdateStateAfterArtifactCommit
20✔
430
                || state == ctx_.kUpdateStateUpdateAfterFirstCommit) {
20✔
431
                // Re-run commit Leave scripts if spontaneously rebooted after commit.
432
                main_states_.SetState(update_after_commit_state_);
2✔
433
                deployment_tracking_.states_.SetState(deployment_tracking_.no_failures_state_);
2✔
434

435
        } else if (state == ctx_.kUpdateStateArtifactFailure) {
18✔
436
                // Re-run ArtifactFailure if spontaneously rebooted before finishing.
437
                main_states_.SetState(state_scripts_.failure_enter_);
4✔
438
                if (ctx_.deployment.state_data->update_info.all_rollbacks_successful) {
4✔
439
                        deployment_tracking_.states_.SetState(deployment_tracking_.rollback_attempted_state_);
3✔
440
                } else {
441
                        deployment_tracking_.states_.SetState(deployment_tracking_.failure_state_);
1✔
442
                }
443

444
        } else if (state == ctx_.kUpdateStateCleanup) {
14✔
445
                // Re-run Cleanup if spontaneously rebooted before finishing.
446
                main_states_.SetState(update_cleanup_state_);
2✔
447
                if (ctx_.deployment.state_data->update_info.all_rollbacks_successful) {
2✔
448
                        deployment_tracking_.states_.SetState(deployment_tracking_.rollback_attempted_state_);
1✔
449
                } else {
450
                        deployment_tracking_.states_.SetState(deployment_tracking_.failure_state_);
1✔
451
                }
452

453
        } else {
454
                // All other states trigger a rollback.
455
                main_states_.SetState(update_check_rollback_state_);
12✔
456
                deployment_tracking_.states_.SetState(deployment_tracking_.failure_state_);
12✔
457
        }
458

459
        auto &payload_types = ctx_.deployment.state_data->update_info.artifact.payload_types;
35✔
460
        if (payload_types.size() == 0) {
35✔
461
                ctx_.deployment.update_module.reset();
462
                return;
463
        }
464
        assert(payload_types.size() == 1);
465
        ctx_.deployment.update_module.reset(
466
                new update_module::UpdateModule(ctx_.mender_context, payload_types[0]));
34✔
467
}
468

469
error::Error StateMachine::Run() {
95✔
470
        // Client is supposed to do one handling of each on startup.
471
        runner_.PostEvent(StateEvent::InventoryPollingTriggered);
95✔
472
        runner_.PostEvent(StateEvent::DeploymentPollingTriggered);
95✔
473

474
        auto err = RegisterSignalHandlers();
95✔
475
        if (err != error::NoError) {
95✔
UNCOV
476
                return err;
×
477
        }
478

479
        log::Info("Running Mender client " + conf::kMenderVersion);
95✔
480

481
        event_loop_.Run();
95✔
482
        return exit_state_.exit_error;
95✔
483
}
484

485
void StateMachine::StopAfterDeployment() {
93✔
486
        main_states_.AddTransition(
93✔
487
                end_of_deployment_state_,
488
                StateEvent::DeploymentEnded,
489
                exit_state_,
490
                sm::TransitionFlag::Immediate);
491
}
93✔
492

493
#ifndef NDEBUG
494
void StateMachine::StopAfterDeployments(int number) {
495
        exit_state_.ExitAfter(number);
496
        main_states_.AddTransition(
497
                end_of_deployment_state_, StateEvent::Success, exit_state_, sm::TransitionFlag::Immediate);
498
        main_states_.AddTransition(
499
                exit_state_,
500
                StateEvent::Success,
501
                state_scripts_.idle_enter_,
502
                sm::TransitionFlag::Immediate);
503
}
504
#endif
505

506
} // namespace daemon
507
} // namespace update
508
} // namespace mender
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc