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

mendersoftware / mender / 1499142629

16 Oct 2024 06:58PM UTC coverage: 76.305% (-0.06%) from 76.361%
1499142629

push

gitlab-ci

lluiscampos
fix: Invalidate cached inventory

Changelog: Invalidate cached inventory data on unauthentication event
to prevent an issue with which the client would not send inventory
data to the server after being unauthorized and authorized again.

Ticket: MEN-7617

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

0 of 4 new or added lines in 1 file covered. (0.0%)

268 existing lines in 7 files now uncovered.

7310 of 9580 relevant lines covered (76.3%)

11291.79 hits per line

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

65.79
/src/mender-update/cli/actions.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/cli/actions.hpp>
16

17
#include <algorithm>
18
#include <iostream>
19
#include <string>
20

21
#include <artifact/config.hpp>
22

23
#include <common/common.hpp>
24
#include <common/error.hpp>
25
#include <common/events.hpp>
26
#include <common/expected.hpp>
27
#include <common/key_value_database.hpp>
28
#include <common/log.hpp>
29
#include <common/path.hpp>
30
#include <common/processes.hpp>
31

32
#include <mender-update/cli/cli.hpp>
33
#include <mender-update/daemon.hpp>
34
#include <mender-update/standalone.hpp>
35

36
#ifdef MENDER_EMBED_MENDER_AUTH
37
#include <mender-auth/cli/actions.hpp>
38
#endif
39

40
namespace mender {
41
namespace update {
42
namespace cli {
43

44
namespace processes = mender::common::processes;
45
namespace conf = mender::client_shared::conf;
46
namespace daemon = mender::update::daemon;
47
namespace database = mender::common::key_value_database;
48
namespace error = mender::common::error;
49
namespace events = mender::common::events;
50
namespace expected = mender::common::expected;
51
namespace http = mender::common::http;
52
namespace kv_db = mender::common::key_value_database;
53
namespace log = mender::common::log;
54
namespace path = mender::common::path;
55
namespace standalone = mender::update::standalone;
56

57
static error::Error DoMaybeInstallBootstrapArtifact(context::MenderContext &main_context) {
107✔
58
        const string bootstrap_artifact_path {
59
                main_context.GetConfig().paths.GetBootstrapArtifactFile()};
60
        // Check if the DB is populated - then install conditionally
61
        auto &db = main_context.GetMenderStoreDB();
107✔
62
        auto exp_key = db.Read(main_context.artifact_name_key);
107✔
63
        if (exp_key) {
107✔
64
                // Key exists. Do nothing
65
                return error::NoError;
58✔
66
        }
67
        error::Error err = exp_key.error();
49✔
68
        if (err.code != kv_db::MakeError(kv_db::KeyError, "Key Not found").code) {
49✔
69
                return err;
×
70
        }
71

72
        // Key does not exist, install the bootstrap artifact if it exists
73
        if (!path::FileExists(bootstrap_artifact_path)) {
49✔
74
                log::Debug("No Bootstrap Artifact found at: " + bootstrap_artifact_path);
46✔
75
                error::Error err =
76
                        db.Write(main_context.artifact_name_key, common::ByteVectorFromString("unknown"));
46✔
77
                if (err != error::NoError) {
46✔
78
                        return err;
×
79
                }
80
                return error::NoError;
46✔
81
        }
82
        log::Info("Installing the bootstrap Artifact");
6✔
83
        events::EventLoop loop;
84
        standalone::Context ctx {main_context, loop};
6✔
85
        auto result = standalone::Install(
86
                ctx,
87
                bootstrap_artifact_path,
88
                artifact::config::Signature::Skip,
89
                standalone::InstallOptions::NoStdout);
3✔
90

91
        if (result.err != error::NoError) {
3✔
92
                error::Error err =
93
                        db.Write(main_context.artifact_name_key, common::ByteVectorFromString("unknown"));
×
94
                return result.err.FollowedBy(err).WithContext("Failed to install the bootstrap Artifact");
×
95
        }
96
        return error::NoError;
3✔
97
}
98

99
error::Error MaybeInstallBootstrapArtifact(context::MenderContext &main_context) {
107✔
100
        const string bootstrap_artifact_path {
101
                main_context.GetConfig().paths.GetBootstrapArtifactFile()};
102
        error::Error err = DoMaybeInstallBootstrapArtifact(main_context);
107✔
103

104
        // Unconditionally delete the bootstrap Artifact
105
        if (path::FileExists(bootstrap_artifact_path)) {
107✔
106
                error::Error delete_err = path::FileDelete(bootstrap_artifact_path);
3✔
107
                if (delete_err != error::NoError) {
3✔
108
                        return err.FollowedBy(
109
                                delete_err.WithContext("Failed to delete the bootstrap Artifact"));
×
110
                }
111
        }
112
        return err;
107✔
113
}
114

115
error::Error ShowArtifactAction::Execute(context::MenderContext &main_context) {
2✔
116
        error::Error err = MaybeInstallBootstrapArtifact(main_context);
2✔
117
        if (err != error::NoError) {
2✔
118
                return err;
×
119
        }
120

121
        auto exp_provides = main_context.LoadProvides();
2✔
122
        if (!exp_provides) {
2✔
123
                return exp_provides.error();
×
124
        }
125

126
        auto &provides = exp_provides.value();
2✔
127
        if (provides.count("artifact_name") == 0 || provides["artifact_name"] == "") {
8✔
128
                cout << "unknown" << endl;
×
129
        } else {
130
                cout << provides["artifact_name"] << endl;
6✔
131
        }
132
        return error::NoError;
2✔
133
}
134

135
error::Error ShowProvidesAction::Execute(context::MenderContext &main_context) {
40✔
136
        error::Error err = MaybeInstallBootstrapArtifact(main_context);
40✔
137
        if (err != error::NoError) {
40✔
138
                return err;
×
139
        }
140

141
        auto exp_provides = main_context.LoadProvides();
40✔
142
        if (!exp_provides) {
40✔
143
                return exp_provides.error();
×
144
        }
145

146
        auto &provides = exp_provides.value();
40✔
147
        for (const auto &elem : provides) {
137✔
148
                cout << elem.first << "=" << elem.second << endl;
97✔
149
        }
150

151
        return error::NoError;
40✔
152
}
153

154
static error::Error ResultHandler(standalone::ResultAndError result) {
87✔
155
        using Result = standalone::Result;
156

157
        if (result.err != error::NoError) {
87✔
158
                log::Error(result.err.String());
48✔
159
        } else if (ResultContains(result.result, Result::Failed)) {
63✔
160
                // All error states, make sure they have an error.
161
                result.err = error::MakeError(error::ExitWithFailureError, "");
4✔
162
        }
163

164
        using r = Result;
165
        auto contains = [&result](r val) { return ResultContains(result.result, val); };
438✔
166
        auto none_of = [&result](r val) { return ResultNoneOf(result.result, val); };
167
        auto add = [](string &str, const string &content) {
5✔
168
                if (str.size() > 0) {
5✔
169
                        str += " ";
×
170
                }
171
                str += content;
5✔
172
        };
5✔
173

174
        string operation_done;
175
        string operation_failure;
176

177
        // For failure case, include which attempted operation failed.
178
        if (contains(r::DownloadFailed)) {
87✔
179
                operation_failure = "Streaming failed.";
6✔
180
        } else if (contains(r::InstallFailed)) {
81✔
181
                operation_failure = "Installation failed.";
8✔
182
        } else if (contains(r::CommitFailed)) {
73✔
183
                operation_failure = "Committing failed.";
6✔
184
        }
185

186
        // For done case, include which operation succeded.
187
        if (contains(r::Committed) and none_of(r::Installed)) {
87✔
188
                operation_done = "Committed.";
6✔
189
        } else if (contains(r::Installed) and none_of(r::Committed)) {
81✔
190
                operation_done =
191
                        "Installed, but not committed.\n"
192
                        "Use 'commit' to update, or 'rollback' to roll back the update.";
17✔
193
        } else if (contains(r::Downloaded) and none_of(r::Installed)) {
64✔
194
                operation_done = "Streamed to storage, but not installed/enabled.";
9✔
195
        } else if (contains(r::Installed | r::Committed)) {
55✔
196
                operation_done = "Installed and committed.";
29✔
197
        } else if (contains(r::NoUpdateInProgress)) {
26✔
198
                operation_done = "No update in progress.";
2✔
199
        } else if (contains(r::Cleaned) and none_of(~r::Cleaned)) {
24✔
200
                // Only include this message if it was the only thing done.
201
                operation_done = "Cleaned up.";
2✔
202
        }
203

204
        // Pick which one of the done/failure cases to use. If the failure happened after the
205
        // commit, we pick the done case, since the operation was still completed.
206
        string &operation = (contains(r::Failed) and none_of(r::FailedInPostCommit | r::CleanupFailed))
24✔
207
                                                        ? operation_failure
208
                                                        : operation_done;
87✔
209

210
        string prefix;
211
        string additional;
212

213
        if (contains(r::AutoCommitWanted) and contains(r::Installed)
33✔
214
                and (contains(r::Committed) or contains(r::Failed))) {
120✔
215
                prefix = "Update Module doesn't support rollback. Committing immediately.";
32✔
216
        }
217

218
        if (contains(r::RollbackFailed)) {
87✔
219
                additional =
220
                        "Rollback failed. "
221
                        "System may be in an inconsistent state.";
7✔
222
        } else if (contains(r::NoRollback)) {
80✔
223
                additional =
224
                        "Update Module does not support rollback. "
225
                        "System may be in an inconsistent state.";
5✔
226
        } else if (contains(r::NoRollbackNecessary)) {
75✔
227
                additional = "System not modified.";
7✔
228
        } else if (contains(r::RolledBack)) {
68✔
229
                additional = "Rolled back.";
10✔
230
        }
231

232
        if (contains(r::FailedInPostCommit)) {
87✔
233
                add(additional, "One or more post-commit steps failed.");
4✔
234
        }
235
        if (contains(r::CleanupFailed)) {
87✔
236
                add(additional, "Cleanup failed.");
2✔
237
        }
238

239
        if (contains(r::RebootRequired)) {
87✔
240
                add(additional, "At least one payload requested a reboot of the device it updated.");
4✔
241
                if (result.err == error::NoError) {
2✔
242
                        result.err = context::MakeError(context::RebootRequiredError, "Reboot required");
4✔
243
                }
244
        }
245

246
        if (prefix.size() > 0) {
87✔
247
                cout << prefix << endl;
32✔
248
        }
249
        if (operation.size() > 0) {
87✔
250
                cout << operation << endl;
70✔
251
        }
252
        if (additional.size() > 0) {
87✔
253
                cout << additional << endl;
34✔
254
        }
255

256
        return result.err;
174✔
257
}
258

259
error::Error InstallAction::Execute(context::MenderContext &main_context) {
60✔
260
        error::Error err = MaybeInstallBootstrapArtifact(main_context);
60✔
261
        if (err != error::NoError) {
60✔
262
                return err;
263
        }
264
        events::EventLoop loop;
265
        standalone::Context ctx {main_context, loop};
120✔
266
        ctx.stop_before = std::move(stop_before_);
60✔
267
        auto result = standalone::Install(ctx, src_);
60✔
268
        err = ResultHandler(result);
120✔
269
        if (!reboot_exit_code_
60✔
270
                && err.code == context::MakeError(context::RebootRequiredError, "").code) {
236✔
271
                // If reboot exit code isn't requested, then this type of error should be treated as
272
                // plain success.
273
                err = error::NoError;
1✔
274
        }
275
        return err;
276
}
277

278
error::Error ResumeAction::Execute(context::MenderContext &main_context) {
13✔
279
        events::EventLoop loop;
280
        standalone::Context ctx {main_context, loop};
26✔
281
        ctx.stop_before = std::move(stop_before_);
13✔
282

283
        auto result = standalone::Resume(ctx);
13✔
284
        auto err = ResultHandler(result);
13✔
285

286
        if (!reboot_exit_code_
13✔
287
                && err.code == context::MakeError(context::RebootRequiredError, "").code) {
52✔
288
                // If reboot exit code isn't requested, then this type of error should be treated as
289
                // plain success.
UNCOV
290
                err = error::NoError;
×
291
        }
292
        return err;
13✔
293
}
294

295
error::Error CommitAction::Execute(context::MenderContext &main_context) {
6✔
296
        events::EventLoop loop;
297
        standalone::Context ctx {main_context, loop};
12✔
298
        ctx.stop_before = std::move(stop_before_);
6✔
299
        auto result = standalone::Commit(ctx);
6✔
300
        return ResultHandler(result);
18✔
301
}
302

303
error::Error RollbackAction::Execute(context::MenderContext &main_context) {
8✔
304
        events::EventLoop loop;
305
        standalone::Context ctx {main_context, loop};
16✔
306
        ctx.stop_before = std::move(stop_before_);
8✔
307
        auto result = standalone::Rollback(ctx);
8✔
308
        return ResultHandler(result);
24✔
309
}
310

UNCOV
311
error::Error DaemonAction::Execute(context::MenderContext &main_context) {
×
312
        events::EventLoop event_loop;
313
        daemon::Context ctx(main_context, event_loop);
×
314

315
#if not defined(MENDER_USE_DBUS) and defined(MENDER_EMBED_MENDER_AUTH)
316
        // Passphrase is not currently supported when launching from mender-update cli.
317
        auto key_store = mender::auth::cli::KeystoreFromConfig(ctx.mender_context.GetConfig(), "");
318
        ctx.authenticator.SetCryptoArgs(
319
                {key_store->KeyName(), key_store->PassPhrase(), key_store->SSLEngine()});
320
#endif
321

322
        daemon::StateMachine state_machine(ctx, event_loop);
×
UNCOV
323
        state_machine.LoadStateFromDb();
×
324
        error::Error err = MaybeInstallBootstrapArtifact(main_context);
×
UNCOV
325
        if (err != error::NoError) {
×
UNCOV
326
                return err;
×
327
        }
328

329
        event_loop.Post([]() {
×
330
                log::Info("The update client daemon is now ready to handle incoming deployments");
×
331
        });
×
332

UNCOV
333
        return state_machine.Run();
×
334
}
335

336
static expected::ExpectedString GetPID() {
×
UNCOV
337
        processes::Process proc({"systemctl", "show", "--property=MainPID", "mender-updated"});
×
338
        auto exp_line_data = proc.GenerateLineData();
×
339
        if (!exp_line_data) {
×
340
                return expected::unexpected(
×
341
                        exp_line_data.error().WithContext("Failed to get the MainPID from systemctl"));
×
342
        }
343
        if (exp_line_data.value().size() < 1) {
×
UNCOV
344
                return expected::unexpected(error::Error(
×
345
                        make_error_condition(errc::message_size), "No output received from systemctl"));
×
346
        }
347
        const string prefix {"MainPID="};
×
UNCOV
348
        const string line = exp_line_data.value().at(0);
×
349
        auto split_index = line.find(prefix);
×
350
        if (split_index == string::npos) {
×
351
                return expected::unexpected(error::Error(
×
352
                        make_error_condition(errc::no_message), "No output received from systemctl"));
×
353
        }
UNCOV
354
        if (split_index != 0) {
×
355
                return expected::unexpected(error::Error(
×
UNCOV
356
                        make_error_condition(errc::bad_message), "Unexpected output from systemctl"));
×
357
        }
358
        const string PID = line.substr(split_index + prefix.size(), line.size());
×
359
        if (PID == "" or PID == "0") {
×
360
                return expected::unexpected(error::Error(
×
361
                        make_error_condition(errc::no_message),
×
362
                        "No PID found for mender-updated. The service is not running"));
×
363
        }
364
        return PID;
×
365
}
366

UNCOV
367
static error::Error SendSignal(const string &signal, const string &pid) {
×
UNCOV
368
        const vector<string> command {"kill", "-" + signal, pid};
×
369
        const string command_string = common::JoinStrings(command, " ");
×
370
        processes::Process proc(command);
×
371
        auto err = proc.Start();
×
372
        if (err != error::NoError) {
×
UNCOV
373
                return err.WithContext("Command '" + command_string + "'");
×
374
        }
375
        return proc.Wait().WithContext("Command '" + command_string + "'");
×
376
}
377

378
error::Error SendInventoryAction::Execute(context::MenderContext &main_context) {
×
379
        auto pid = GetPID();
×
380
        if (!pid) {
×
381
                return pid.error().WithContext("Failed to force an inventory update");
×
382
        }
383

UNCOV
384
        return SendSignal("SIGUSR2", pid.value()).WithContext("Failed to force an inventory update");
×
385
}
386

UNCOV
387
error::Error CheckUpdateAction::Execute(context::MenderContext &main_context) {
×
UNCOV
388
        auto pid = GetPID();
×
UNCOV
389
        if (!pid) {
×
UNCOV
390
                return pid.error().WithContext("Failed to force an update check");
×
391
        }
UNCOV
392
        return SendSignal("SIGUSR1", pid.value()).WithContext("Failed to force an update check");
×
393
}
394

395
} // namespace cli
396
} // namespace update
397
} // 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