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

mendersoftware / mender / 1641906300

27 Jan 2025 05:31AM UTC coverage: 75.952% (-0.002%) from 75.954%
1641906300

push

gitlab-ci

web-flow
Merge pull request #1734 from mendersoftware/cherry-5.0.x-MEN-7900-client-stuck-after-sync-error

[Cherry 5.0.x]: MEN-7900: Fix Mender client getting stuck after failure in sync state

24 of 40 new or added lines in 3 files covered. (60.0%)

100 existing lines in 4 files now uncovered.

7381 of 9718 relevant lines covered (75.95%)

11129.08 hits per line

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

95.77
/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) :
94✔
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,
94✔
39
                "inventory submission",
40
                StateEvent::InventoryPollingTriggered,
41
                ctx.mender_context.GetConfig().inventory_poll_interval_seconds),
94✔
42
        schedule_poll_for_deployment_state_(
43
                ctx.deployment_timer,
94✔
44
                "deployment check",
45
                StateEvent::DeploymentPollingTriggered,
46
                ctx.mender_context.GetConfig().update_poll_interval_seconds),
94✔
47
        submit_inventory_state_(
48
                ctx.mender_context.GetConfig().retry_poll_interval_seconds,
49
                ctx.mender_context.GetConfig().retry_poll_count),
94✔
50
        poll_for_deployment_state_(
51
                ctx.mender_context.GetConfig().retry_poll_interval_seconds,
52
                ctx.mender_context.GetConfig().retry_poll_count),
94✔
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),
94✔
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),
94✔
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},
94✔
74
                ctx_.mender_context.GetConfig().paths.GetArtScriptsPath(),
188✔
75
                ctx_.mender_context.GetConfig().paths.GetRootfsScriptsPath()),
188✔
76
        runner_(ctx) {
470✔
77
        runner_.AddStateMachine(deployment_tracking_.states_);
94✔
78
        runner_.AddStateMachine(main_states_);
94✔
79
        runner_.AttachToEventLoop(event_loop_);
94✔
UNCOV
80
        ctx.authenticator.RegisterTokenReceivedCallback([&ctx]() {
×
UNCOV
81
                if (ctx.inventory_client->has_submitted_inventory) {
×
UNCOV
82
                        log::Debug("Client has re-authenticated - clear inventory data cache");
×
UNCOV
83
                        ctx.inventory_client->ClearDataCache();
×
UNCOV
84
                        ctx.inventory_client->has_submitted_inventory = false;
×
85
                }
UNCOV
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);
94✔
98

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

166
        // Cannot fail due to FailureMode::Ignore.
167
        main_states_.AddTransition(send_install_status_state_,              se::Success,                     ss.install_enter_,                       tf::Immediate);
94✔
168

169
        main_states_.AddTransition(update_install_state_,                   se::Success,                     ss.install_leave_,                       tf::Immediate);
94✔
170
        main_states_.AddTransition(update_install_state_,                   se::Failure,                     ss.install_error_rollback_,              tf::Immediate);
94✔
171
        main_states_.AddTransition(update_install_state_,                   se::StateLoopDetected,           state_loop_state_,                       tf::Immediate);
94✔
172

173
        main_states_.AddTransition(ss.install_leave_,                       se::Success,                     update_check_reboot_state_,              tf::Immediate);
94✔
174
        main_states_.AddTransition(ss.install_leave_,                       se::Failure,                     ss.install_error_rollback_,              tf::Immediate);
94✔
175
        main_states_.AddTransition(ss.install_error_rollback_,              se::Success,                     update_check_rollback_state_,            tf::Immediate);
94✔
176
        main_states_.AddTransition(ss.install_error_rollback_,              se::Failure,                     update_check_rollback_state_,            tf::Immediate);
94✔
177

178
        main_states_.AddTransition(ss.failure_enter_,                       se::Success,                     update_failure_state_,                   tf::Immediate);
94✔
179
        main_states_.AddTransition(ss.failure_enter_,                       se::Failure,                     update_failure_state_,                   tf::Immediate);
94✔
180
        main_states_.AddTransition(ss.failure_enter_,                       se::StateLoopDetected,           state_loop_state_,                       tf::Immediate);
94✔
181

182

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

188
        // Cannot fail due to FailureMode::Ignore.
189
        main_states_.AddTransition(send_reboot_status_state_,               se::Success,                     ss.reboot_enter_,                        tf::Immediate);
94✔
190

191
        main_states_.AddTransition(ss.reboot_enter_,                        se::Success,                     update_reboot_state_,                    tf::Immediate);
94✔
192
        main_states_.AddTransition(ss.reboot_enter_,                        se::Failure,                     ss.reboot_error_,                        tf::Immediate);
94✔
193

194
        main_states_.AddTransition(update_reboot_state_,                    se::Success,                     update_verify_reboot_state_,             tf::Immediate);
94✔
195
        main_states_.AddTransition(update_reboot_state_,                    se::Failure,                     ss.reboot_error_,                        tf::Immediate);
94✔
196
        main_states_.AddTransition(update_reboot_state_,                    se::StateLoopDetected,           state_loop_state_,                       tf::Immediate);
94✔
197

198
        main_states_.AddTransition(ss.reboot_error_,                        se::Success,                     update_check_rollback_state_,            tf::Immediate);
94✔
199
        main_states_.AddTransition(ss.reboot_error_,                        se::Failure,                     update_check_rollback_state_,            tf::Immediate);
94✔
200

201
        main_states_.AddTransition(update_verify_reboot_state_,             se::Success,                     ss.reboot_leave_,                        tf::Immediate);
94✔
202
        main_states_.AddTransition(update_verify_reboot_state_,             se::Failure,                     ss.reboot_error_,                        tf::Immediate);
94✔
203
        main_states_.AddTransition(update_verify_reboot_state_,             se::StateLoopDetected,           state_loop_state_,                       tf::Immediate);
94✔
204

205
        main_states_.AddTransition(ss.reboot_leave_,                        se::Success,                     update_before_commit_state_,             tf::Immediate);
94✔
206
        main_states_.AddTransition(ss.reboot_leave_,                        se::Failure,                     ss.reboot_error_,                        tf::Immediate);
94✔
207

208
        // Cannot fail.
209
        main_states_.AddTransition(update_before_commit_state_,             se::Success,                     send_commit_status_state_,               tf::Immediate);
94✔
210

211
        main_states_.AddTransition(send_commit_status_state_,               se::Success,                     ss.commit_enter_,                        tf::Immediate);
94✔
212
        main_states_.AddTransition(send_commit_status_state_,               se::Failure,                     update_check_rollback_state_,            tf::Immediate);
94✔
213

214
        main_states_.AddTransition(ss.commit_enter_,                        se::Success,                     update_commit_state_,                    tf::Immediate);
94✔
215
        main_states_.AddTransition(ss.commit_enter_,                        se::Failure,                     ss.commit_error_,                        tf::Immediate);
94✔
216

217
        main_states_.AddTransition(ss.commit_error_,                        se::Success,                     update_check_rollback_state_,            tf::Immediate);
94✔
218
        main_states_.AddTransition(ss.commit_error_,                        se::Failure,                     update_check_rollback_state_,            tf::Immediate);
94✔
219

220
        main_states_.AddTransition(update_commit_state_,                    se::Success,                     update_after_commit_state_,              tf::Immediate);
94✔
221
        main_states_.AddTransition(update_commit_state_,                    se::Failure,                     ss.commit_error_,                        tf::Immediate);
94✔
222
        main_states_.AddTransition(update_commit_state_,                    se::StateLoopDetected,           state_loop_state_,                       tf::Immediate);
94✔
223

224
        main_states_.AddTransition(update_after_commit_state_,              se::Success,                     ss.commit_leave_,                        tf::Immediate);
94✔
225
        main_states_.AddTransition(update_after_commit_state_,              se::Failure,                     ss.commit_error_save_provides_,          tf::Immediate);
94✔
226
        main_states_.AddTransition(update_after_commit_state_,              se::StateLoopDetected,           state_loop_state_,                       tf::Immediate);
94✔
227

228
        main_states_.AddTransition(ss.commit_leave_,                        se::Success,                     update_save_provides_state_,             tf::Immediate);
94✔
229
        main_states_.AddTransition(ss.commit_leave_,                        se::Failure,                     ss.commit_error_save_provides_,          tf::Immediate);
94✔
230

231
        main_states_.AddTransition(ss.commit_error_save_provides_,          se::Success,                     update_save_provides_state_,             tf::Immediate);
94✔
232
        main_states_.AddTransition(ss.commit_error_save_provides_,          se::Failure,                     update_save_provides_state_,             tf::Immediate);
94✔
233

234
        main_states_.AddTransition(update_check_rollback_state_,            se::Success,                     ss.rollback_enter_,                      tf::Immediate);
94✔
235
        main_states_.AddTransition(update_check_rollback_state_,            se::NothingToDo,                 ss.failure_enter_,                       tf::Immediate);
94✔
236
        main_states_.AddTransition(update_check_rollback_state_,            se::Failure,                     ss.failure_enter_,                       tf::Immediate);
94✔
237
        main_states_.AddTransition(update_check_rollback_state_,            se::StateLoopDetected,           state_loop_state_,                       tf::Immediate);
94✔
238

239
        main_states_.AddTransition(ss.rollback_enter_,                      se::Success,                     update_rollback_state_,                  tf::Immediate);
94✔
240
        main_states_.AddTransition(ss.rollback_enter_,                      se::Failure,                     update_rollback_state_,                  tf::Immediate);
94✔
241

242
        main_states_.AddTransition(update_rollback_state_,                  se::Success,                     ss.rollback_leave_,                      tf::Immediate);
94✔
243
        main_states_.AddTransition(update_rollback_state_,                  se::Failure,                     ss.rollback_leave_error_,                tf::Immediate);
94✔
244
        main_states_.AddTransition(update_rollback_state_,                  se::StateLoopDetected,           state_loop_state_,                       tf::Immediate);
94✔
245

246
        main_states_.AddTransition(ss.rollback_leave_,                      se::Success,                     update_check_rollback_reboot_state_,     tf::Immediate);
94✔
247
        main_states_.AddTransition(ss.rollback_leave_,                      se::Failure,                     update_check_rollback_reboot_state_,     tf::Immediate);
94✔
248

249
        main_states_.AddTransition(ss.rollback_leave_error_,                se::Success,                     ss.failure_enter_,                       tf::Immediate);
94✔
250
        main_states_.AddTransition(ss.rollback_leave_error_,                se::Failure,                     ss.failure_enter_,                       tf::Immediate);
94✔
251

252
        main_states_.AddTransition(update_check_rollback_reboot_state_,     se::Success,                     ss.rollback_reboot_enter_,               tf::Immediate);
94✔
253
        main_states_.AddTransition(update_check_rollback_reboot_state_,     se::NothingToDo,                 update_rollback_successful_state_,       tf::Immediate);
94✔
254
        main_states_.AddTransition(update_check_rollback_reboot_state_,     se::Failure,                     ss.failure_enter_,                       tf::Immediate);
94✔
255
        main_states_.AddTransition(update_check_rollback_reboot_state_,     se::StateLoopDetected,           state_loop_state_,                       tf::Immediate);
94✔
256

257
        main_states_.AddTransition(ss.rollback_reboot_enter_,               se::Success,                     update_rollback_reboot_state_,           tf::Immediate);
94✔
258
        main_states_.AddTransition(ss.rollback_reboot_enter_,               se::Failure,                     update_rollback_reboot_state_,           tf::Immediate);
94✔
259

260
        main_states_.AddTransition(ss.rollback_reboot_error_,               se::Success,                     ss.failure_enter_,                       tf::Immediate);
94✔
261
        main_states_.AddTransition(ss.rollback_reboot_error_,               se::Failure,                     ss.failure_enter_,                       tf::Immediate);
94✔
262

263
        // No Failure transition for this state see comments in handler.
264
        main_states_.AddTransition(update_rollback_reboot_state_,           se::Success,                     update_verify_rollback_reboot_state_,    tf::Immediate);
94✔
265
        main_states_.AddTransition(update_rollback_reboot_state_,           se::StateLoopDetected,           state_loop_state_,                       tf::Immediate);
94✔
266

267
        main_states_.AddTransition(update_verify_rollback_reboot_state_,    se::Success,                     ss.rollback_reboot_leave_,               tf::Immediate);
94✔
268
        main_states_.AddTransition(update_verify_rollback_reboot_state_,    se::Retry,                       update_rollback_reboot_state_,           tf::Immediate);
94✔
269
        main_states_.AddTransition(update_verify_rollback_reboot_state_,    se::StateLoopDetected,           state_loop_state_,                       tf::Immediate);
94✔
270

271
        main_states_.AddTransition(ss.rollback_reboot_leave_,               se::Success,                     update_rollback_successful_state_,       tf::Immediate);
94✔
272
        main_states_.AddTransition(ss.rollback_reboot_leave_,               se::Failure,                     ss.rollback_reboot_error_,               tf::Immediate);
94✔
273

274
        main_states_.AddTransition(update_rollback_successful_state_,       se::Success,                     ss.failure_enter_,                       tf::Immediate);
94✔
275

276
        main_states_.AddTransition(update_failure_state_,                   se::Success,                     ss.failure_leave_update_save_provides_,  tf::Immediate);
94✔
277
        main_states_.AddTransition(update_failure_state_,                   se::Failure,                     ss.failure_leave_update_save_provides_,  tf::Immediate);
94✔
278
        main_states_.AddTransition(update_failure_state_,                   se::StateLoopDetected,           state_loop_state_,                       tf::Immediate);
94✔
279

280
        main_states_.AddTransition(ss.failure_leave_update_save_provides_,  se::Success,                     update_save_provides_state_,             tf::Immediate);
94✔
281
        main_states_.AddTransition(ss.failure_leave_update_save_provides_,  se::Failure,                     update_save_provides_state_,             tf::Immediate);
94✔
282

283
        // Even if this fails there is nothing we can do at this point.
284
        main_states_.AddTransition(update_save_provides_state_,             se::Success,                     update_cleanup_state_,                   tf::Immediate);
94✔
285
        main_states_.AddTransition(update_save_provides_state_,             se::Failure,                     update_cleanup_state_,                   tf::Immediate);
94✔
286
        main_states_.AddTransition(update_save_provides_state_,             se::StateLoopDetected,           state_loop_state_,                       tf::Immediate);
94✔
287

288
        main_states_.AddTransition(update_rollback_not_needed_state_,       se::Success,                     update_cleanup_state_,                   tf::Immediate);
94✔
289

290
        main_states_.AddTransition(update_cleanup_state_,                   se::Success,                     send_final_status_state_,                tf::Immediate);
94✔
291
        main_states_.AddTransition(update_cleanup_state_,                   se::Failure,                     send_final_status_state_,                tf::Immediate);
94✔
292
        main_states_.AddTransition(update_cleanup_state_,                   se::StateLoopDetected,           state_loop_state_,                       tf::Immediate);
94✔
293

294
        main_states_.AddTransition(state_loop_state_,                       se::Success,                     send_final_status_state_,                tf::Immediate);
94✔
295
        main_states_.AddTransition(state_loop_state_,                       se::Failure,                     send_final_status_state_,                tf::Immediate);
94✔
296

297
        main_states_.AddTransition(send_final_status_state_,                se::Success,                     clear_artifact_data_state_,              tf::Immediate);
94✔
298
        main_states_.AddTransition(send_final_status_state_,                se::Failure,                     clear_artifact_data_state_,              tf::Immediate);
94✔
299

300
        main_states_.AddTransition(clear_artifact_data_state_,              se::Success,                     end_of_deployment_state_,                tf::Immediate);
94✔
301
        main_states_.AddTransition(clear_artifact_data_state_,              se::Failure,                     end_of_deployment_state_,                tf::Immediate);
94✔
302

303
        main_states_.AddTransition(end_of_deployment_state_,                se::Success,                     ss.idle_enter_,                          tf::Immediate);
94✔
304

305
        auto &dt = deployment_tracking_;
306

307
        dt.states_.AddTransition(dt.idle_state_,                            se::DeploymentStarted,           dt.no_failures_state_,                   tf::Immediate);
94✔
308

309
        dt.states_.AddTransition(dt.no_failures_state_,                     se::Failure,                     dt.failure_state_,                       tf::Immediate);
94✔
310
        dt.states_.AddTransition(dt.no_failures_state_,                     se::DeploymentEnded,             dt.idle_state_,                          tf::Immediate);
94✔
311

312
        dt.states_.AddTransition(dt.failure_state_,                         se::RollbackStarted,             dt.rollback_attempted_state_,            tf::Immediate);
94✔
313
        dt.states_.AddTransition(dt.failure_state_,                         se::DeploymentEnded,             dt.idle_state_,                          tf::Immediate);
94✔
314

315
        dt.states_.AddTransition(dt.rollback_attempted_state_,              se::Failure,                     dt.rollback_failed_state_,               tf::Immediate);
94✔
316
        dt.states_.AddTransition(dt.rollback_attempted_state_,              se::DeploymentEnded,             dt.idle_state_,                          tf::Immediate);
94✔
317

318
        dt.states_.AddTransition(dt.rollback_failed_state_,                 se::DeploymentEnded,             dt.idle_state_,                          tf::Immediate);
94✔
319
        // clang-format on
320
}
94✔
321

322
StateMachine::StateMachine(
89✔
323
        Context &ctx, events::EventLoop &event_loop, chrono::milliseconds minimum_wait_time) :
89✔
324
        StateMachine(ctx, event_loop) {
89✔
325
        send_commit_status_state_.SetSmallestWaitInterval(minimum_wait_time);
89✔
326
        send_final_status_state_.SetSmallestWaitInterval(minimum_wait_time);
89✔
327
}
89✔
328

329
StateMachine::DeploymentTracking::DeploymentTracking() :
94✔
330
        states_(idle_state_) {
94✔
331
}
94✔
332

333
void StateMachine::LoadStateFromDb() {
90✔
334
        unique_ptr<StateData> state_data(new StateData);
124✔
335
        auto exp_loaded = ctx_.LoadDeploymentStateData(*state_data);
90✔
336
        if (!exp_loaded) {
90✔
337
                if (exp_loaded.error().code
2✔
338
                        == context::MakeError(context::StateDataStoreCountExceededError, "").code) {
2✔
339
                        log::Error("State loop detected. Forcefully aborting update.");
2✔
340

341
                        // This particular error code also fills in state_data.
342
                        ctx_.deployment.state_data = std::move(state_data);
1✔
343

344
                        ctx_.BeginDeploymentLogging();
1✔
345

346
                        main_states_.SetState(state_loop_state_);
1✔
347
                        deployment_tracking_.states_.SetState(deployment_tracking_.rollback_failed_state_);
1✔
348
                } else {
349
                        log::Error(
1✔
350
                                "Unable to load deployment data from database: " + exp_loaded.error().String());
2✔
351
                        log::Error("Starting from initial state");
2✔
352
                }
353
                return;
2✔
354
        }
355

356
        auto &store = ctx_.mender_context.GetMenderStoreDB();
88✔
357

358
        if (!exp_loaded.value()) {
88✔
359
                log::Debug("No existing deployment data, starting from initial state");
106✔
360

361
                auto err = store.Remove(ctx_.mender_context.update_control_maps);
53✔
362
                if (err != error::NoError) {
53✔
UNCOV
363
                        log::Error(
×
UNCOV
364
                                "Error removing " + ctx_.mender_context.update_control_maps
×
UNCOV
365
                                + " key from database: " + err.String());
×
366
                        // Nothing we can do about it.
367
                }
368

369
                return;
370
        }
371

372
        // We have state data, move it to the context.
373
        ctx_.deployment.state_data = std::move(state_data);
35✔
374

375
        ctx_.BeginDeploymentLogging();
35✔
376

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

389
        auto &state = ctx_.deployment.state_data->state;
35✔
390

391
        if (state == ctx_.kUpdateStateDownload) {
35✔
392
                main_states_.SetState(update_cleanup_state_);
3✔
393
                // "rollback_attempted_state" because Download in its nature makes no system
394
                // changes, so a rollback is a no-op.
395
                deployment_tracking_.states_.SetState(deployment_tracking_.rollback_attempted_state_);
3✔
396

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

409
        } else if (state == ctx_.kUpdateStateArtifactRollback) {
27✔
410
                // Installation failed, but rollback could still succeed.
411
                main_states_.SetState(state_scripts_.rollback_enter_);
3✔
412
                deployment_tracking_.states_.SetState(deployment_tracking_.rollback_attempted_state_);
3✔
413

414
        } else if (
415
                state == ctx_.kUpdateStateArtifactRollbackReboot
24✔
416
                || state == ctx_.kUpdateStateArtifactVerifyRollbackReboot
22✔
417
                || state == ctx_.kUpdateStateVerifyRollbackReboot) {
44✔
418
                // Normal flow for a rebooting rollback.
419
                main_states_.SetState(update_verify_rollback_reboot_state_);
4✔
420
                deployment_tracking_.states_.SetState(deployment_tracking_.rollback_attempted_state_);
4✔
421

422
        } else if (
423
                state == ctx_.kUpdateStateAfterArtifactCommit
20✔
424
                || state == ctx_.kUpdateStateUpdateAfterFirstCommit) {
20✔
425
                // Re-run commit Leave scripts if spontaneously rebooted after commit.
426
                main_states_.SetState(update_after_commit_state_);
2✔
427
                deployment_tracking_.states_.SetState(deployment_tracking_.no_failures_state_);
2✔
428

429
        } else if (state == ctx_.kUpdateStateArtifactFailure) {
18✔
430
                // Re-run ArtifactFailure if spontaneously rebooted before finishing.
431
                main_states_.SetState(state_scripts_.failure_enter_);
4✔
432
                if (ctx_.deployment.state_data->update_info.all_rollbacks_successful) {
4✔
433
                        deployment_tracking_.states_.SetState(deployment_tracking_.rollback_attempted_state_);
3✔
434
                } else {
435
                        deployment_tracking_.states_.SetState(deployment_tracking_.failure_state_);
1✔
436
                }
437

438
        } else if (state == ctx_.kUpdateStateCleanup) {
14✔
439
                // Re-run Cleanup if spontaneously rebooted before finishing.
440
                main_states_.SetState(update_cleanup_state_);
2✔
441
                if (ctx_.deployment.state_data->update_info.all_rollbacks_successful) {
2✔
442
                        deployment_tracking_.states_.SetState(deployment_tracking_.rollback_attempted_state_);
1✔
443
                } else {
444
                        deployment_tracking_.states_.SetState(deployment_tracking_.failure_state_);
1✔
445
                }
446

447
        } else {
448
                // All other states trigger a rollback.
449
                main_states_.SetState(update_check_rollback_state_);
12✔
450
                deployment_tracking_.states_.SetState(deployment_tracking_.failure_state_);
12✔
451
        }
452

453
        auto &payload_types = ctx_.deployment.state_data->update_info.artifact.payload_types;
35✔
454
        if (payload_types.size() == 0) {
35✔
455
                ctx_.deployment.update_module.reset();
456
                return;
457
        }
458
        assert(payload_types.size() == 1);
459
        ctx_.deployment.update_module.reset(
460
                new update_module::UpdateModule(ctx_.mender_context, payload_types[0]));
34✔
461
}
462

463
error::Error StateMachine::Run() {
93✔
464
        // Client is supposed to do one handling of each on startup.
465
        runner_.PostEvent(StateEvent::InventoryPollingTriggered);
93✔
466
        runner_.PostEvent(StateEvent::DeploymentPollingTriggered);
93✔
467

468
        auto err = RegisterSignalHandlers();
93✔
469
        if (err != error::NoError) {
93✔
UNCOV
470
                return err;
×
471
        }
472

473
        log::Info("Running Mender client " + conf::kMenderVersion);
93✔
474

475
        event_loop_.Run();
93✔
476
        return exit_state_.exit_error;
93✔
477
}
478

479
void StateMachine::StopAfterDeployment() {
91✔
480
        main_states_.AddTransition(
91✔
481
                end_of_deployment_state_,
482
                StateEvent::DeploymentEnded,
483
                exit_state_,
484
                sm::TransitionFlag::Immediate);
485
}
91✔
486

487
#ifndef NDEBUG
488
void StateMachine::StopAfterDeployments(int number) {
489
        exit_state_.ExitAfter(number);
490
        main_states_.AddTransition(
491
                end_of_deployment_state_, StateEvent::Success, exit_state_, sm::TransitionFlag::Immediate);
492
        main_states_.AddTransition(
493
                exit_state_,
494
                StateEvent::Success,
495
                state_scripts_.idle_enter_,
496
                sm::TransitionFlag::Immediate);
497
}
498
#endif
499

500
} // namespace daemon
501
} // namespace update
502
} // 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

© 2026 Coveralls, Inc