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

mendersoftware / mender / 1012473002

21 Sep 2023 02:17PM UTC coverage: 78.107% (-0.3%) from 78.44%
1012473002

push

gitlab-ci

lluiscampos
chore: log stdout output on errors when parsing UM yes/no commands

Signed-off-by: Lluis Campos <lluis.campos@northern.tech>

2 of 2 new or added lines in 1 file covered. (100.0%)

6468 of 8281 relevant lines covered (78.11%)

11106.28 hits per line

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

83.61
/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);
110✔
48
        if (!exp_bytes) {
55✔
49
                auto &err = exp_bytes.error();
46✔
50
                if (err.code == database::MakeError(database::KeyError, "").code) {
46✔
51
                        return optional::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);
18✔
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);
18✔
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);
9✔
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);
18✔
82
        if (exp_map) {
9✔
83
                dst.artifact_provides = exp_map.value();
8✔
84
        } else {
85
                dst.artifact_provides.reset();
1✔
86
        }
87

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

96
        exp_array = json::Get<vector<string>>(json, keys.payload_types, json::MissingOk::No);
9✔
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;
44✔
132
        dst.artifact_clears_provides = header.header.type_info.clears_artifact_provides;
44✔
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;
42✔
153
        for (auto elem : data.payload_types) {
84✔
154
                if (!first) {
42✔
155
                        ss << ",";
×
156
                }
157
                ss << "\"" << elem << "\"";
42✔
158
                first = false;
42✔
159
        }
160
        ss << "]";
42✔
161

162
        if (data.artifact_provides) {
42✔
163
                ss << ",";
41✔
164
                ss << "\"" << keys.artifact_provides << "\": {";
41✔
165
                bool first = true;
41✔
166
                for (auto elem : data.artifact_provides.value()) {
123✔
167
                        if (!first) {
82✔
168
                                ss << ",";
41✔
169
                        }
170
                        ss << "\"" << elem.first << "\":\"" << elem.second << "\"";
82✔
171
                        first = false;
82✔
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;
41✔
180
                for (auto elem : data.artifact_clears_provides.value()) {
164✔
181
                        if (!first) {
123✔
182
                                ss << ",";
82✔
183
                        }
184
                        ss << "\"" << elem << "\"";
123✔
185
                        first = false;
123✔
186
                }
187
                ss << "]";
41✔
188
        }
189

190
        ss << "}";
42✔
191

192
        string strdata = ss.str();
84✔
193
        vector<uint8_t> bytedata(common::ByteVectorFromString(strdata));
84✔
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>();
2✔
205
        req->SetMethod(http::Method::GET);
1✔
206
        auto err = req->SetAddress(src);
2✔
207
        if (err != error::NoError) {
1✔
208
                return expected::unexpected(err);
×
209
        }
210
        error::Error inner_err;
2✔
211
        io::AsyncReaderPtr reader;
1✔
212
        err = http_client.AsyncCall(
1✔
213
                req,
214
                [&loop, &inner_err, &reader](http::ExpectedIncomingResponsePtr exp_resp) {
2✔
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
                });
2✔
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());
90✔
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),
1✔
276
                                "Update already in progress. Please commit or roll back first")};
3✔
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>();
1✔
286
                http::ClientConfig conf;
1✔
287
                http_client = make_shared<http::Client>(conf, *event_loop);
1✔
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);
43✔
300
        }
301

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

397
        auto result = DoRollback(main_context, data, update_module);
10✔
398

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

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

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

421
        return result;
4✔
422
}
423

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

434
        const auto &default_paths {main_context.GetConfig().paths};
42✔
435
        events::EventLoop loop;
84✔
436
        auto script_runner {executor::ScriptRunner(
437
                loop,
438
                chrono::seconds {main_context.GetConfig().state_script_timeout_seconds},
42✔
439
                default_paths.GetArtScriptsPath(),
84✔
440
                default_paths.GetRootfsScriptsPath())};
210✔
441

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

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

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

478

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

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

496
        // Install Leave
497
        err = script_runner.RunScripts(executor::State::ArtifactInstall, executor::Action::Leave);
33✔
498
        if (err != error::NoError) {
33✔
499
                auto install_leave_error {
500
                        script_runner.RunScripts(executor::State::ArtifactInstall, executor::Action::Error)};
2✔
501

502
                log::Error(
1✔
503
                        "Failure during Install Leave script execution: "
504
                        + err.FollowedBy(install_leave_error).String());
2✔
505

506
                return InstallationFailureHandler(main_context, data, update_module);
1✔
507
        }
508

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

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

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

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

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

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

568
        err = update_module.ArtifactCommit();
23✔
569
        if (err != error::NoError) {
23✔
570
                log::Error("Commit failed: " + err.String());
×
571
                return InstallationFailureHandler(main_context, data, update_module);
×
572
        }
573

574
        auto result = Result::Committed;
23✔
575
        error::Error return_err;
46✔
576

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

587

588
        err = update_module.Cleanup();
23✔
589
        if (err != error::NoError) {
23✔
590
                result = Result::InstalledButFailedInPostCommit;
1✔
591
                return_err = return_err.FollowedBy(err);
1✔
592
        }
593

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

607
        return {result, return_err};
23✔
608
}
609

610
ResultAndError DoRollback(
14✔
611
        context::MenderContext &main_context,
612
        StateData &data,
613
        update_module::UpdateModule &update_module) {
614
        auto exp_rollback_support = update_module.SupportsRollback();
28✔
615
        if (!exp_rollback_support) {
14✔
616
                return {Result::NoRollback, exp_rollback_support.error()};
×
617
        }
618

619
        if (exp_rollback_support.value()) {
14✔
620
                auto default_paths {main_context.GetConfig().paths};
14✔
621
                events::EventLoop loop;
14✔
622
                auto script_runner {executor::ScriptRunner(
623
                        loop,
624
                        chrono::seconds {main_context.GetConfig().state_script_timeout_seconds},
7✔
625
                        default_paths.GetArtScriptsPath(),
14✔
626
                        default_paths.GetRootfsScriptsPath())};
35✔
627

628
                // Rollback Enter
629
                auto err =
630
                        script_runner.RunScripts(executor::State::ArtifactRollback, executor::Action::Enter);
14✔
631
                if (err != error::NoError) {
7✔
632
                        return {Result::RollbackFailed, err};
×
633
                }
634
                err = update_module.ArtifactRollback();
7✔
635
                if (err != error::NoError) {
7✔
636
                        return {Result::RollbackFailed, err};
2✔
637
                }
638
                // Rollback Leave
639
                err = script_runner.RunScripts(executor::State::ArtifactRollback, executor::Action::Leave);
5✔
640
                if (err != error::NoError) {
5✔
641
                        return {Result::RollbackFailed, err};
×
642
                }
643
                return {Result::RolledBack, error::NoError};
5✔
644
        } else {
645
                return {Result::NoRollback, error::NoError};
7✔
646
        }
647
}
648

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

652
        auto err = main_context.CommitArtifactData(
653
                data.artifact_name,
2✔
654
                data.artifact_group,
2✔
655
                data.artifact_provides,
2✔
656
                data.artifact_clears_provides,
2✔
657
                [](database::Transaction &txn) { return error::NoError; });
8✔
658
        if (err != error::NoError) {
2✔
659
                return {Result::InstalledButFailedInPostCommit, err};
×
660
        }
661
        return {Result::InstalledAndCommitted, err};
2✔
662
}
663

664
ResultAndError InstallationFailureHandler(
9✔
665
        context::MenderContext &main_context,
666
        StateData &data,
667
        update_module::UpdateModule &update_module) {
668
        error::Error err;
18✔
669

670
        auto default_paths {main_context.GetConfig().paths};
18✔
671
        events::EventLoop loop;
18✔
672
        auto script_runner {executor::ScriptRunner(
673
                loop,
674
                chrono::seconds {main_context.GetConfig().state_script_timeout_seconds},
9✔
675
                default_paths.GetArtScriptsPath(),
18✔
676
                default_paths.GetRootfsScriptsPath())};
45✔
677

678
        auto result = DoRollback(main_context, data, update_module);
18✔
679
        switch (result.result) {
9✔
680
        case Result::RolledBack:
2✔
681
                result.result = Result::FailedAndRolledBack;
2✔
682
                break;
2✔
683
        case Result::NoRollback:
6✔
684
                result.result = Result::FailedAndNoRollback;
6✔
685
                break;
6✔
686
        case Result::RollbackFailed:
1✔
687
                result.result = Result::FailedAndRollbackFailed;
1✔
688
                break;
1✔
689
        default:
×
690
                // Should not happen.
691
                assert(false);
×
692
                return {
693
                        Result::FailedAndRollbackFailed,
694
                        error::MakeError(
695
                                error::ProgrammingError,
696
                                "Unexpected result in InstallationFailureHandler. This is a bug.")};
697
        }
698

699
        // Failure Enter
700
        err = script_runner.RunScripts(
9✔
701
                executor::State::ArtifactFailure, executor::Action::Enter, executor::RunError::Ignore);
9✔
702
        if (err != error::NoError) {
9✔
703
                log::Error("Failure during execution of ArtifactFailure Enter script: " + err.String());
1✔
704
                result.result = Result::FailedAndRollbackFailed;
1✔
705
                result.err = result.err.FollowedBy(err);
1✔
706
        }
707

708
        err = update_module.ArtifactFailure();
9✔
709
        if (err != error::NoError) {
9✔
710
                result.result = Result::FailedAndRollbackFailed;
1✔
711
                result.err = result.err.FollowedBy(err);
1✔
712
        }
713

714
        // Failure Leave
715
        err = script_runner.RunScripts(
9✔
716
                executor::State::ArtifactFailure, executor::Action::Leave, executor::RunError::Ignore);
9✔
717
        if (err != error::NoError) {
9✔
718
                log::Error("Failure during execution of ArtifactFailure Enter script: " + err.String());
2✔
719
                result.result = Result::FailedAndRollbackFailed;
2✔
720
                result.err = result.err.FollowedBy(err);
2✔
721
        }
722

723
        err = update_module.Cleanup();
9✔
724
        if (err != error::NoError) {
9✔
725
                result.result = Result::FailedAndRollbackFailed;
×
726
                result.err = result.err.FollowedBy(err);
×
727
        }
728

729
        if (result.result == Result::FailedAndRolledBack) {
9✔
730
                err = RemoveStateData(main_context.GetMenderStoreDB());
1✔
731
        } else {
732
                err = CommitBrokenArtifact(main_context, data);
8✔
733
        }
734
        if (err != error::NoError) {
9✔
735
                result.result = Result::FailedAndRollbackFailed;
×
736
                result.err = result.err.FollowedBy(err);
×
737
        }
738

739
        return result;
9✔
740
}
741

742
error::Error CommitBrokenArtifact(context::MenderContext &main_context, StateData &data) {
9✔
743
        data.artifact_name += main_context.broken_artifact_name_suffix;
9✔
744
        if (data.artifact_provides) {
9✔
745
                data.artifact_provides.value()["artifact_name"] = data.artifact_name;
9✔
746
        }
747
        return main_context.CommitArtifactData(
748
                data.artifact_name,
9✔
749
                data.artifact_group,
9✔
750
                data.artifact_provides,
9✔
751
                data.artifact_clears_provides,
9✔
752
                [](database::Transaction &txn) {
9✔
753
                        return txn.Remove(context::MenderContext::standalone_state_key);
9✔
754
                });
9✔
755
}
756

757
} // namespace standalone
758
} // namespace update
759
} // 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