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

mendersoftware / mender / 1073264463

15 Nov 2023 10:33AM UTC coverage: 80.186% (+0.1%) from 80.062%
1073264463

push

gitlab-ci

kacf
docs: Restore Update Control XML file for documentation purposes.

This is a partial restore of ee5dc24db79fc57, just to get the XML file
back. The feature is still removed, this is just to be able to keep it
in the documentation with a notice that says it's removed (already
merged to master in 426caf729c3191b).

Signed-off-by: Kristian Amlie <kristian.amlie@northern.tech>

6969 of 8691 relevant lines covered (80.19%)

9260.9 hits per line

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

82.91
/src/mender-update/standalone/standalone.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/standalone.hpp>
16

17
#include <common/common.hpp>
18
#include <common/events_io.hpp>
19
#include <common/http.hpp>
20
#include <common/log.hpp>
21

22
#include <artifact/v3/scripts/executor.hpp>
23

24
namespace mender {
25
namespace update {
26
namespace standalone {
27

28

29
namespace common = mender::common;
30
namespace events = mender::common::events;
31
namespace executor = mender::artifact::scripts::executor;
32
namespace http = mender::http;
33
namespace io = mender::common::io;
34
namespace log = mender::common::log;
35

36
const string StateDataKeys::version {"Version"};
37
const string StateDataKeys::artifact_name {"ArtifactName"};
38
const string StateDataKeys::artifact_group {"ArtifactGroup"};
39
const string StateDataKeys::artifact_provides {"ArtifactTypeInfoProvides"};
40
const string StateDataKeys::artifact_clears_provides {"ArtifactClearsProvides"};
41
const string StateDataKeys::payload_types {"PayloadTypes"};
42

43
ExpectedOptionalStateData LoadStateData(database::KeyValueDatabase &db) {
55✔
44
        StateDataKeys keys;
45
        StateData dst;
110✔
46

47
        auto exp_bytes = db.Read(context::MenderContext::standalone_state_key);
55✔
48
        if (!exp_bytes) {
55✔
49
                auto &err = exp_bytes.error();
50
                if (err.code == database::MakeError(database::KeyError, "").code) {
46✔
51
                        return optional<StateData>();
92✔
52
                } else {
53
                        return expected::unexpected(err);
×
54
                }
55
        }
56

57
        auto exp_json = json::Load(common::StringFromByteVector(exp_bytes.value()));
18✔
58
        if (!exp_json) {
9✔
59
                return expected::unexpected(exp_json.error());
×
60
        }
61
        auto &json = exp_json.value();
9✔
62

63
        auto exp_int = json::Get<int64_t>(json, keys.version, json::MissingOk::No);
9✔
64
        if (!exp_int) {
9✔
65
                return expected::unexpected(exp_int.error());
×
66
        }
67
        dst.version = exp_int.value();
9✔
68

69
        auto exp_string = json::Get<string>(json, keys.artifact_name, json::MissingOk::No);
9✔
70
        if (!exp_string) {
9✔
71
                return expected::unexpected(exp_string.error());
×
72
        }
73
        dst.artifact_name = exp_string.value();
9✔
74

75
        exp_string = json::Get<string>(json, keys.artifact_group, json::MissingOk::Yes);
18✔
76
        if (!exp_string) {
9✔
77
                return expected::unexpected(exp_string.error());
×
78
        }
79
        dst.artifact_group = exp_string.value();
9✔
80

81
        auto exp_map = json::Get<json::KeyValueMap>(json, keys.artifact_provides, json::MissingOk::No);
9✔
82
        if (exp_map) {
9✔
83
                dst.artifact_provides = exp_map.value();
8✔
84
        } else {
85
                dst.artifact_provides.reset();
86
        }
87

88
        auto exp_array =
89
                json::Get<vector<string>>(json, keys.artifact_clears_provides, json::MissingOk::No);
9✔
90
        if (exp_array) {
9✔
91
                dst.artifact_clears_provides = exp_array.value();
8✔
92
        } else {
93
                dst.artifact_clears_provides.reset();
94
        }
95

96
        exp_array = json::Get<vector<string>>(json, keys.payload_types, json::MissingOk::No);
18✔
97
        if (!exp_array) {
9✔
98
                return expected::unexpected(exp_array.error());
×
99
        }
100
        dst.payload_types = exp_array.value();
9✔
101

102
        if (dst.version != context::MenderContext::standalone_data_version) {
9✔
103
                return expected::unexpected(error::Error(
×
104
                        make_error_condition(errc::not_supported),
×
105
                        "State data has a version which is not supported by this client"));
×
106
        }
107

108
        if (dst.artifact_name == "") {
9✔
109
                return expected::unexpected(context::MakeError(
×
110
                        context::DatabaseValueError, "`" + keys.artifact_name + "` is empty"));
×
111
        }
112

113
        if (dst.payload_types.size() == 0) {
9✔
114
                return expected::unexpected(context::MakeError(
×
115
                        context::DatabaseValueError, "`" + keys.payload_types + "` is empty"));
×
116
        }
117
        if (dst.payload_types.size() >= 2) {
9✔
118
                return expected::unexpected(error::Error(
×
119
                        make_error_condition(errc::not_supported),
×
120
                        "`" + keys.payload_types + "` contains multiple payloads"));
×
121
        }
122

123
        return dst;
9✔
124
}
125

126
StateData StateDataFromPayloadHeaderView(const artifact::PayloadHeaderView &header) {
44✔
127
        StateData dst;
44✔
128
        dst.version = context::MenderContext::standalone_data_version;
44✔
129
        dst.artifact_name = header.header.artifact_name;
44✔
130
        dst.artifact_group = header.header.artifact_group;
44✔
131
        dst.artifact_provides = header.header.type_info.artifact_provides;
132
        dst.artifact_clears_provides = header.header.type_info.clears_artifact_provides;
133
        dst.payload_types.clear();
44✔
134
        dst.payload_types.push_back(header.header.payload_type);
44✔
135
        return dst;
44✔
136
}
137

138
error::Error SaveStateData(database::KeyValueDatabase &db, const StateData &data) {
42✔
139
        StateDataKeys keys;
140
        stringstream ss;
84✔
141
        ss << "{";
42✔
142
        ss << "\"" << keys.version << "\":" << data.version;
42✔
143

144
        ss << ",";
42✔
145
        ss << "\"" << keys.artifact_name << "\":\"" << data.artifact_name << "\"";
42✔
146

147
        ss << ",";
42✔
148
        ss << "\"" << keys.artifact_group << "\":\"" << data.artifact_group << "\"";
42✔
149

150
        ss << ",";
42✔
151
        ss << "\"" << keys.payload_types << "\": [";
42✔
152
        bool first = true;
153
        for (auto elem : data.payload_types) {
84✔
154
                if (!first) {
42✔
155
                        ss << ",";
×
156
                }
157
                ss << "\"" << elem << "\"";
42✔
158
                first = false;
159
        }
160
        ss << "]";
42✔
161

162
        if (data.artifact_provides) {
42✔
163
                ss << ",";
41✔
164
                ss << "\"" << keys.artifact_provides << "\": {";
41✔
165
                bool first = true;
166
                for (auto elem : data.artifact_provides.value()) {
246✔
167
                        if (!first) {
82✔
168
                                ss << ",";
41✔
169
                        }
170
                        ss << "\"" << elem.first << "\":\"" << elem.second << "\"";
82✔
171
                        first = false;
172
                }
173
                ss << "}";
41✔
174
        }
175

176
        if (data.artifact_clears_provides) {
42✔
177
                ss << ",";
41✔
178
                ss << "\"" << keys.artifact_clears_provides << "\": [";
41✔
179
                bool first = true;
180
                for (auto elem : data.artifact_clears_provides.value()) {
205✔
181
                        if (!first) {
123✔
182
                                ss << ",";
82✔
183
                        }
184
                        ss << "\"" << elem << "\"";
123✔
185
                        first = false;
186
                }
187
                ss << "]";
41✔
188
        }
189

190
        ss << "}";
42✔
191

192
        string strdata = ss.str();
193
        vector<uint8_t> bytedata(common::ByteVectorFromString(strdata));
42✔
194

195
        return db.Write(context::MenderContext::standalone_state_key, bytedata);
84✔
196
}
197

198
error::Error RemoveStateData(database::KeyValueDatabase &db) {
8✔
199
        return db.Remove(context::MenderContext::standalone_state_key);
8✔
200
}
201

202
static io::ExpectedReaderPtr ReaderFromUrl(
1✔
203
        events::EventLoop &loop, http::Client &http_client, const string &src) {
204
        auto req = make_shared<http::OutgoingRequest>();
1✔
205
        req->SetMethod(http::Method::GET);
1✔
206
        auto err = req->SetAddress(src);
1✔
207
        if (err != error::NoError) {
1✔
208
                return expected::unexpected(err);
×
209
        }
210
        error::Error inner_err;
1✔
211
        io::AsyncReaderPtr reader;
1✔
212
        err = http_client.AsyncCall(
1✔
213
                req,
214
                [&loop, &inner_err, &reader](http::ExpectedIncomingResponsePtr exp_resp) {
1✔
215
                        // No matter what happens, we will want to stop the loop after the headers
216
                        // are received.
217
                        loop.Stop();
1✔
218

219
                        if (!exp_resp) {
1✔
220
                                inner_err = exp_resp.error();
×
221
                                return;
×
222
                        }
223

224
                        auto resp = exp_resp.value();
1✔
225

226
                        if (resp->GetStatusCode() != http::StatusOK) {
1✔
227
                                inner_err = context::MakeError(
×
228
                                        context::UnexpectedHttpResponse,
229
                                        to_string(resp->GetStatusCode()) + ": " + resp->GetStatusMessage());
×
230
                                return;
×
231
                        }
232

233
                        auto exp_reader = resp->MakeBodyAsyncReader();
1✔
234
                        if (!exp_reader) {
1✔
235
                                inner_err = exp_reader.error();
×
236
                                return;
237
                        }
238
                        reader = exp_reader.value();
1✔
239
                },
240
                [](http::ExpectedIncomingResponsePtr exp_resp) {
×
241
                        if (!exp_resp) {
×
242
                                log::Warning("While reading HTTP body: " + exp_resp.error().String());
×
243
                        }
244
                });
3✔
245

246
        // Loop until the headers are received. Then we return and let the reader drive the
247
        // rest of the download.
248
        loop.Run();
1✔
249

250
        if (err != error::NoError) {
1✔
251
                return expected::unexpected(err);
×
252
        }
253

254
        if (inner_err != error::NoError) {
1✔
255
                return expected::unexpected(inner_err);
×
256
        }
257

258
        return make_shared<events::io::ReaderFromAsyncReader>(loop, reader);
2✔
259
}
260

261
ResultAndError Install(
45✔
262
        context::MenderContext &main_context,
263
        const string &src,
264
        const artifact::config::Signature verify_signature) {
265
        auto exp_in_progress = LoadStateData(main_context.GetMenderStoreDB());
45✔
266
        if (!exp_in_progress) {
45✔
267
                return {Result::FailedNothingDone, exp_in_progress.error()};
×
268
        }
269
        auto &in_progress = exp_in_progress.value();
45✔
270

271
        if (in_progress) {
45✔
272
                return {
273
                        Result::FailedNothingDone,
274
                        error::Error(
275
                                make_error_condition(errc::operation_in_progress),
2✔
276
                                "Update already in progress. Please commit or roll back first")};
2✔
277
        }
278

279
        io::ReaderPtr artifact_reader;
44✔
280

281
        shared_ptr<events::EventLoop> event_loop;
44✔
282
        http::ClientPtr http_client;
44✔
283

284
        if (src.find("http://") == 0 || src.find("https://") == 0) {
44✔
285
                event_loop = make_shared<events::EventLoop>();
2✔
286
                http_client =
287
                        make_shared<http::Client>(main_context.GetConfig().GetHttpClientConfig(), *event_loop);
2✔
288
                auto reader = ReaderFromUrl(*event_loop, *http_client, src);
1✔
289
                if (!reader) {
1✔
290
                        return {Result::FailedNothingDone, reader.error()};
×
291
                }
292
                artifact_reader = reader.value();
1✔
293
        } else {
294
                auto stream = io::OpenIfstream(src);
43✔
295
                if (!stream) {
43✔
296
                        return {Result::FailedNothingDone, stream.error()};
×
297
                }
298
                auto file_stream = make_shared<ifstream>(std::move(stream.value()));
43✔
299
                artifact_reader = make_shared<io::StreamReader>(file_stream);
86✔
300
        }
301

302
        artifact::config::ParserConfig config {
44✔
303
                .artifact_scripts_filesystem_path = main_context.GetConfig().paths.GetArtScriptsPath(),
304
                .artifact_scripts_version = 3,
305
                .artifact_verify_keys = main_context.GetConfig().artifact_verify_keys,
44✔
306
                .verify_signature = verify_signature,
307
        };
88✔
308

309
        auto exp_parser = artifact::Parse(*artifact_reader, config);
88✔
310
        if (!exp_parser) {
44✔
311
                return {Result::FailedNothingDone, exp_parser.error()};
×
312
        }
313
        auto &parser = exp_parser.value();
44✔
314

315
        auto exp_header = artifact::View(parser, 0);
44✔
316
        if (!exp_header) {
44✔
317
                return {Result::FailedNothingDone, exp_header.error()};
×
318
        }
319
        auto &header = exp_header.value();
44✔
320

321
        cout << "Installing artifact..." << endl;
44✔
322

323
        if (header.header.payload_type == "") {
44✔
324
                auto data = StateDataFromPayloadHeaderView(header);
4✔
325
                return DoEmptyPayloadArtifact(main_context, data);
2✔
326
        }
327

328
        update_module::UpdateModule update_module(main_context, header.header.payload_type);
84✔
329

330
        auto err =
331
                update_module.CleanAndPrepareFileTree(update_module.GetUpdateModuleWorkDir(), header);
42✔
332
        if (err != error::NoError) {
42✔
333
                err = err.FollowedBy(update_module.Cleanup());
×
334
                return {Result::FailedNothingDone, err};
×
335
        }
336

337
        StateData data = StateDataFromPayloadHeaderView(header);
84✔
338
        err = SaveStateData(main_context.GetMenderStoreDB(), data);
42✔
339
        if (err != error::NoError) {
42✔
340
                err = err.FollowedBy(update_module.Cleanup());
×
341
                return {Result::FailedNothingDone, err};
×
342
        }
343

344
        return DoInstallStates(main_context, data, parser, update_module);
42✔
345
}
346

347
ResultAndError Commit(context::MenderContext &main_context) {
4✔
348
        auto exp_in_progress = LoadStateData(main_context.GetMenderStoreDB());
4✔
349
        if (!exp_in_progress) {
4✔
350
                return {Result::FailedNothingDone, exp_in_progress.error()};
×
351
        }
352
        auto &in_progress = exp_in_progress.value();
4✔
353

354
        if (!in_progress) {
4✔
355
                return {
356
                        Result::NoUpdateInProgress,
357
                        context::MakeError(context::NoUpdateInProgressError, "Cannot commit")};
2✔
358
        }
359
        auto &data = in_progress.value();
3✔
360

361
        update_module::UpdateModule update_module(main_context, data.payload_types[0]);
6✔
362

363
        if (data.payload_types[0] == "rootfs-image") {
3✔
364
                // Special case for rootfs-image upgrades. See comments inside the function.
365
                auto err = update_module.EnsureRootfsImageFileTree(update_module.GetUpdateModuleWorkDir());
3✔
366
                if (err != error::NoError) {
3✔
367
                        return {Result::FailedNothingDone, err};
×
368
                }
369
        }
370

371
        return DoCommit(main_context, data, update_module);
3✔
372
}
373

374
ResultAndError Rollback(context::MenderContext &main_context) {
6✔
375
        auto exp_in_progress = LoadStateData(main_context.GetMenderStoreDB());
6✔
376
        if (!exp_in_progress) {
6✔
377
                return {Result::FailedNothingDone, exp_in_progress.error()};
×
378
        }
379
        auto &in_progress = exp_in_progress.value();
6✔
380

381
        if (!in_progress) {
6✔
382
                return {
383
                        Result::NoUpdateInProgress,
384
                        context::MakeError(context::NoUpdateInProgressError, "Cannot roll back")};
2✔
385
        }
386
        auto &data = in_progress.value();
5✔
387

388
        update_module::UpdateModule update_module(main_context, data.payload_types[0]);
10✔
389

390
        if (data.payload_types[0] == "rootfs-image") {
5✔
391
                // Special case for rootfs-image upgrades. See comments inside the function.
392
                auto err = update_module.EnsureRootfsImageFileTree(update_module.GetUpdateModuleWorkDir());
5✔
393
                if (err != error::NoError) {
5✔
394
                        return {Result::FailedNothingDone, err};
×
395
                }
396
        }
397

398
        auto result = DoRollback(main_context, data, update_module);
5✔
399

400
        if (result.result == Result::NoRollback) {
5✔
401
                // No support for rollback. Return instead of clearing update data. It should be
402
                // cleared by calling commit or restoring the rollback capability.
403
                return result;
1✔
404
        }
405

406
        auto err = update_module.Cleanup();
4✔
407
        if (err != error::NoError) {
4✔
408
                result.result = Result::FailedAndRollbackFailed;
×
409
                result.err = result.err.FollowedBy(err);
×
410
        }
411

412
        if (result.result == Result::RolledBack) {
4✔
413
                err = RemoveStateData(main_context.GetMenderStoreDB());
6✔
414
        } else {
415
                err = CommitBrokenArtifact(main_context, data);
2✔
416
        }
417
        if (err != error::NoError) {
4✔
418
                result.result = Result::RollbackFailed;
×
419
                result.err = result.err.FollowedBy(err);
×
420
        }
421

422
        return result;
4✔
423
}
424

425
ResultAndError DoInstallStates(
42✔
426
        context::MenderContext &main_context,
427
        StateData &data,
428
        artifact::Artifact &artifact,
429
        update_module::UpdateModule &update_module) {
430
        auto payload = artifact.Next();
42✔
431
        if (!payload) {
42✔
432
                return {Result::FailedNothingDone, payload.error()};
×
433
        }
434

435
        const auto &default_paths {main_context.GetConfig().paths};
436
        events::EventLoop loop;
437
        auto script_runner {executor::ScriptRunner(
438
                loop,
439
                chrono::seconds {main_context.GetConfig().state_script_timeout_seconds},
440
                chrono::seconds {main_context.GetConfig().state_script_retry_interval_seconds},
441
                chrono::seconds {main_context.GetConfig().state_script_retry_timeout_seconds},
442
                default_paths.GetArtScriptsPath(),
42✔
443
                default_paths.GetRootfsScriptsPath())};
168✔
444

445
        // ProvidePayloadFileSizes
446
        auto with_sizes = update_module.ProvidePayloadFileSizes();
42✔
447
        if (!with_sizes) {
42✔
448
                log::Error("Could not query for provide file sizes: " + with_sizes.error().String());
×
449
                return InstallationFailureHandler(main_context, data, update_module);
×
450
        }
451

452
        // Download Enter
453
        auto err = script_runner.RunScripts(executor::State::Download, executor::Action::Enter);
42✔
454
        if (err != error::NoError) {
42✔
455
                err = err.FollowedBy(
2✔
456
                        script_runner.RunScripts(executor::State::Download, executor::Action::Error));
2✔
457
                err = err.FollowedBy(update_module.Cleanup());
4✔
458
                err = err.FollowedBy(RemoveStateData(main_context.GetMenderStoreDB()));
4✔
459
                return {Result::FailedNothingDone, err};
2✔
460
        }
461
        if (with_sizes.value()) {
40✔
462
                err = update_module.DownloadWithFileSizes(payload.value());
2✔
463
        } else {
464
                err = update_module.Download(payload.value());
78✔
465
        }
466
        if (err != error::NoError) {
40✔
467
                err = err.FollowedBy(update_module.Cleanup());
2✔
468
                err = err.FollowedBy(RemoveStateData(main_context.GetMenderStoreDB()));
2✔
469
                return {Result::FailedNothingDone, err};
1✔
470
        }
471

472
        // Download Leave
473
        err = script_runner.RunScripts(executor::State::Download, executor::Action::Leave);
39✔
474
        if (err != error::NoError) {
39✔
475
                err = script_runner.RunScripts(executor::State::Download, executor::Action::Error);
1✔
476
                err = err.FollowedBy(update_module.Cleanup());
2✔
477
                err = err.FollowedBy(RemoveStateData(main_context.GetMenderStoreDB()));
2✔
478
                return {Result::FailedNothingDone, err};
1✔
479
        }
480

481

482
        // Install Enter
483
        err = script_runner.RunScripts(executor::State::ArtifactInstall, executor::Action::Enter);
38✔
484
        if (err != error::NoError) {
38✔
485
                auto install_leave_error {
486
                        script_runner.RunScripts(executor::State::ArtifactInstall, executor::Action::Error)};
1✔
487
                log::Error(
1✔
488
                        "Failure during Install Enter script execution: "
489
                        + err.FollowedBy(install_leave_error).String());
2✔
490
                return InstallationFailureHandler(main_context, data, update_module);
1✔
491
        }
492

493
        err = update_module.ArtifactInstall();
37✔
494
        if (err != error::NoError) {
37✔
495
                log::Error("Installation failed: " + err.String());
8✔
496
                return InstallationFailureHandler(main_context, data, update_module);
4✔
497
        }
498

499
        // Install Leave
500
        err = script_runner.RunScripts(executor::State::ArtifactInstall, executor::Action::Leave);
33✔
501
        if (err != error::NoError) {
33✔
502
                auto install_leave_error {
503
                        script_runner.RunScripts(executor::State::ArtifactInstall, executor::Action::Error)};
1✔
504

505
                log::Error(
1✔
506
                        "Failure during Install Leave script execution: "
507
                        + err.FollowedBy(install_leave_error).String());
2✔
508

509
                return InstallationFailureHandler(main_context, data, update_module);
1✔
510
        }
511

512
        auto reboot = update_module.NeedsReboot();
32✔
513
        if (!reboot) {
32✔
514
                log::Error("Could not query for reboot: " + reboot.error().String());
×
515
                return InstallationFailureHandler(main_context, data, update_module);
×
516
        }
517

518
        auto rollback_support = update_module.SupportsRollback();
32✔
519
        if (!rollback_support) {
32✔
520
                log::Error("Could not query for rollback support: " + rollback_support.error().String());
×
521
                return InstallationFailureHandler(main_context, data, update_module);
×
522
        }
523

524
        if (rollback_support.value()) {
32✔
525
                if (reboot.value() != update_module::RebootAction::No) {
9✔
526
                        return {Result::InstalledRebootRequired, error::NoError};
×
527
                } else {
528
                        return {Result::Installed, error::NoError};
9✔
529
                }
530
        }
531

532
        cout << "Update Module doesn't support rollback. Committing immediately." << endl;
23✔
533

534
        auto result = DoCommit(main_context, data, update_module);
23✔
535
        if (result.result == Result::Committed) {
23✔
536
                if (reboot.value() != update_module::RebootAction::No) {
18✔
537
                        result.result = Result::InstalledAndCommittedRebootRequired;
2✔
538
                } else {
539
                        result.result = Result::InstalledAndCommitted;
16✔
540
                }
541
        }
542
        return result;
23✔
543
}
544

545
ResultAndError DoCommit(
26✔
546
        context::MenderContext &main_context,
547
        StateData &data,
548
        update_module::UpdateModule &update_module) {
549
        const auto &default_paths {main_context.GetConfig().paths};
550
        events::EventLoop loop;
551
        auto script_runner {executor::ScriptRunner(
552
                loop,
553
                chrono::seconds {main_context.GetConfig().state_script_timeout_seconds},
554
                chrono::seconds {main_context.GetConfig().state_script_retry_interval_seconds},
555
                chrono::seconds {main_context.GetConfig().state_script_retry_timeout_seconds},
556
                default_paths.GetArtScriptsPath(),
26✔
557
                default_paths.GetRootfsScriptsPath())};
104✔
558
        // Commit Enter
559
        auto err = script_runner.RunScripts(executor::State::ArtifactCommit, executor::Action::Enter);
26✔
560
        if (err != error::NoError) {
26✔
561
                log::Error("Commit Enter State Script error: " + err.String());
6✔
562
                // Commit Error
563
                {
564
                        auto commit_error =
565
                                script_runner.RunScripts(executor::State::ArtifactCommit, executor::Action::Error);
3✔
566
                        if (commit_error != error::NoError) {
3✔
567
                                log::Error("Commit Error State Script error: " + commit_error.String());
×
568
                        }
569
                }
570
                return InstallationFailureHandler(main_context, data, update_module);
3✔
571
        }
572

573
        err = update_module.ArtifactCommit();
23✔
574
        if (err != error::NoError) {
23✔
575
                log::Error("Commit failed: " + err.String());
×
576
                return InstallationFailureHandler(main_context, data, update_module);
×
577
        }
578

579
        auto result = Result::Committed;
580
        error::Error return_err;
23✔
581

582
        // Commit Leave
583
        err = script_runner.RunScripts(executor::State::ArtifactCommit, executor::Action::Leave);
23✔
584
        if (err != error::NoError) {
23✔
585
                auto leave_err =
586
                        script_runner.RunScripts(executor::State::ArtifactCommit, executor::Action::Error);
1✔
587
                log::Error("Error during Commit Leave script: " + err.FollowedBy(leave_err).String());
2✔
588
                result = Result::InstalledButFailedInPostCommit;
589
                return_err = err.FollowedBy(leave_err);
2✔
590
        }
591

592

593
        err = update_module.Cleanup();
23✔
594
        if (err != error::NoError) {
23✔
595
                result = Result::InstalledButFailedInPostCommit;
596
                return_err = return_err.FollowedBy(err);
2✔
597
        }
598

599
        err = main_context.CommitArtifactData(
23✔
600
                data.artifact_name,
23✔
601
                data.artifact_group,
23✔
602
                data.artifact_provides,
23✔
603
                data.artifact_clears_provides,
23✔
604
                [](database::Transaction &txn) {
23✔
605
                        return txn.Remove(context::MenderContext::standalone_state_key);
23✔
606
                });
46✔
607
        if (err != error::NoError) {
23✔
608
                result = Result::InstalledButFailedInPostCommit;
609
                return_err = return_err.FollowedBy(err);
×
610
        }
611

612
        return {result, return_err};
23✔
613
}
614

615
ResultAndError DoRollback(
14✔
616
        context::MenderContext &main_context,
617
        StateData &data,
618
        update_module::UpdateModule &update_module) {
619
        auto exp_rollback_support = update_module.SupportsRollback();
14✔
620
        if (!exp_rollback_support) {
14✔
621
                return {Result::NoRollback, exp_rollback_support.error()};
×
622
        }
623

624
        if (exp_rollback_support.value()) {
14✔
625
                auto default_paths {main_context.GetConfig().paths};
14✔
626
                events::EventLoop loop;
627
                auto script_runner {executor::ScriptRunner(
628
                        loop,
629
                        chrono::seconds {main_context.GetConfig().state_script_timeout_seconds},
630
                        chrono::seconds {main_context.GetConfig().state_script_retry_interval_seconds},
631
                        chrono::seconds {main_context.GetConfig().state_script_retry_timeout_seconds},
632
                        default_paths.GetArtScriptsPath(),
7✔
633
                        default_paths.GetRootfsScriptsPath())};
28✔
634

635
                // Rollback Enter
636
                auto err =
637
                        script_runner.RunScripts(executor::State::ArtifactRollback, executor::Action::Enter);
7✔
638
                if (err != error::NoError) {
7✔
639
                        return {Result::RollbackFailed, err};
×
640
                }
641
                err = update_module.ArtifactRollback();
7✔
642
                if (err != error::NoError) {
7✔
643
                        return {Result::RollbackFailed, err};
2✔
644
                }
645
                // Rollback Leave
646
                err = script_runner.RunScripts(executor::State::ArtifactRollback, executor::Action::Leave);
5✔
647
                if (err != error::NoError) {
5✔
648
                        return {Result::RollbackFailed, err};
×
649
                }
650
                return {Result::RolledBack, error::NoError};
5✔
651
        } else {
652
                return {Result::NoRollback, error::NoError};
7✔
653
        }
654
}
655

656
ResultAndError DoEmptyPayloadArtifact(context::MenderContext &main_context, StateData &data) {
2✔
657
        cout << "Artifact with empty payload. Committing immediately." << endl;
2✔
658

659
        auto err = main_context.CommitArtifactData(
660
                data.artifact_name,
2✔
661
                data.artifact_group,
2✔
662
                data.artifact_provides,
2✔
663
                data.artifact_clears_provides,
2✔
664
                [](database::Transaction &txn) { return error::NoError; });
6✔
665
        if (err != error::NoError) {
2✔
666
                return {Result::InstalledButFailedInPostCommit, err};
×
667
        }
668
        return {Result::InstalledAndCommitted, err};
2✔
669
}
670

671
ResultAndError InstallationFailureHandler(
9✔
672
        context::MenderContext &main_context,
673
        StateData &data,
674
        update_module::UpdateModule &update_module) {
675
        error::Error err;
9✔
676

677
        auto default_paths {main_context.GetConfig().paths};
18✔
678
        events::EventLoop loop;
679
        auto script_runner {executor::ScriptRunner(
680
                loop,
681
                chrono::seconds {main_context.GetConfig().state_script_timeout_seconds},
682
                chrono::seconds {main_context.GetConfig().state_script_retry_interval_seconds},
683
                chrono::seconds {main_context.GetConfig().state_script_retry_timeout_seconds},
684
                default_paths.GetArtScriptsPath(),
9✔
685
                default_paths.GetRootfsScriptsPath())};
36✔
686

687
        auto result = DoRollback(main_context, data, update_module);
9✔
688
        switch (result.result) {
9✔
689
        case Result::RolledBack:
2✔
690
                result.result = Result::FailedAndRolledBack;
2✔
691
                break;
2✔
692
        case Result::NoRollback:
6✔
693
                result.result = Result::FailedAndNoRollback;
6✔
694
                break;
6✔
695
        case Result::RollbackFailed:
1✔
696
                result.result = Result::FailedAndRollbackFailed;
1✔
697
                break;
1✔
698
        default:
×
699
                // Should not happen.
700
                assert(false);
701
                return {
702
                        Result::FailedAndRollbackFailed,
703
                        error::MakeError(
704
                                error::ProgrammingError,
705
                                "Unexpected result in InstallationFailureHandler. This is a bug.")};
×
706
        }
707

708
        // Failure Enter
709
        err = script_runner.RunScripts(
9✔
710
                executor::State::ArtifactFailure, executor::Action::Enter, executor::OnError::Ignore);
9✔
711
        if (err != error::NoError) {
9✔
712
                log::Error("Failure during execution of ArtifactFailure Enter script: " + err.String());
2✔
713
                result.result = Result::FailedAndRollbackFailed;
1✔
714
                result.err = result.err.FollowedBy(err);
2✔
715
        }
716

717
        err = update_module.ArtifactFailure();
9✔
718
        if (err != error::NoError) {
9✔
719
                result.result = Result::FailedAndRollbackFailed;
1✔
720
                result.err = result.err.FollowedBy(err);
2✔
721
        }
722

723
        // Failure Leave
724
        err = script_runner.RunScripts(
9✔
725
                executor::State::ArtifactFailure, executor::Action::Leave, executor::OnError::Ignore);
9✔
726
        if (err != error::NoError) {
9✔
727
                log::Error("Failure during execution of ArtifactFailure Enter script: " + err.String());
4✔
728
                result.result = Result::FailedAndRollbackFailed;
2✔
729
                result.err = result.err.FollowedBy(err);
4✔
730
        }
731

732
        err = update_module.Cleanup();
9✔
733
        if (err != error::NoError) {
9✔
734
                result.result = Result::FailedAndRollbackFailed;
×
735
                result.err = result.err.FollowedBy(err);
×
736
        }
737

738
        if (result.result == Result::FailedAndRolledBack) {
9✔
739
                err = RemoveStateData(main_context.GetMenderStoreDB());
2✔
740
        } else {
741
                err = CommitBrokenArtifact(main_context, data);
16✔
742
        }
743
        if (err != error::NoError) {
9✔
744
                result.result = Result::FailedAndRollbackFailed;
×
745
                result.err = result.err.FollowedBy(err);
×
746
        }
747

748
        return result;
9✔
749
}
750

751
error::Error CommitBrokenArtifact(context::MenderContext &main_context, StateData &data) {
9✔
752
        data.artifact_name += main_context.broken_artifact_name_suffix;
9✔
753
        if (data.artifact_provides) {
9✔
754
                data.artifact_provides.value()["artifact_name"] = data.artifact_name;
18✔
755
        }
756
        return main_context.CommitArtifactData(
757
                data.artifact_name,
758
                data.artifact_group,
9✔
759
                data.artifact_provides,
9✔
760
                data.artifact_clears_provides,
9✔
761
                [](database::Transaction &txn) {
9✔
762
                        return txn.Remove(context::MenderContext::standalone_state_key);
9✔
763
                });
18✔
764
}
765

766
} // namespace standalone
767
} // namespace update
768
} // 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