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

mendersoftware / mender / 947394036

pending completion
947394036

push

gitlab-ci

kacf
chore: Treat events with no state transitions as fatal.

This was discussed with the team members. Since an unhandled event is
almost guaranteed to hang the state machine, then it's better to
terminate and let systemd try to restart us, in the hopes that
recovery will still work.

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

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

4268 of 5997 relevant lines covered (71.17%)

148.52 hits per line

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

88.73
/mender-update/update_module/v3/platform/c++17/update_module_call.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/update_module/v3/update_module.hpp>
16

17
#include <iostream>
18
#include <sstream>
19

20
#include <filesystem>
21

22
#include <common/common.hpp>
23
#include <common/events.hpp>
24
#include <common/log.hpp>
25
#include <common/processes.hpp>
26

27
namespace mender {
28
namespace update {
29
namespace update_module {
30
namespace v3 {
31

32
namespace error = mender::common::error;
33
namespace events = mender::common::events;
34
namespace log = mender::common::log;
35
namespace fs = std::filesystem;
36

37
UpdateModule::StateRunner::StateRunner(
162✔
38
        events::EventLoop &loop,
39
        State state,
40
        const string &module_path,
41
        const string &module_work_path) :
162✔
42
        loop(loop),
43
        module_work_path(module_work_path),
44
        proc({module_path, StateToString(state), module_work_path}),
45
        timeout(loop) {
648✔
46
        proc.SetWorkDir(module_work_path);
162✔
47
}
162✔
48

49
error::Error UpdateModule::StateRunner::AsyncCallState(
162✔
50
        State state, bool procOut, chrono::seconds timeout_seconds, HandlerFunction handler) {
51
        this->handler = handler;
162✔
52

53
        string state_string = StateToString(state);
324✔
54
        if (!fs::is_directory(module_work_path)) {
162✔
55
                if (state == State::Cleanup) {
1✔
56
                        loop.Post([this, state]() { ProcessFinishedHandler(state, error::NoError); });
×
57
                        return error::NoError;
×
58
                } else {
59
                        return error::Error(
60
                                make_error_condition(errc::no_such_file_or_directory),
1✔
61
                                state_string + ": File tree does not exist: " + module_work_path);
3✔
62
                }
63
        }
64

65
        class OutputHandler {
66
        public:
67
                void operator()(const char *data, size_t size) {
9✔
68
                        if (size == 0) {
9✔
69
                                return;
×
70
                        }
71
                        // Get rid of exactly one trailing newline, if there is one. This is because
72
                        // we unconditionally print one at the end of every log line. If the string
73
                        // does not contain a trailing newline, add a "{...}" instead, since we
74
                        // cannot avoid breaking the line apart then.
75
                        string content(data, size);
18✔
76
                        if (content.back() == '\n') {
9✔
77
                                content.pop_back();
9✔
78
                        } else {
79
                                content.append("{...}");
×
80
                        }
81
                        auto lines = mender::common::SplitString(content, "\n");
27✔
82
                        for (auto line : lines) {
18✔
83
                                log::Info(prefix + line);
9✔
84
                        }
85
                }
86
                string prefix;
87
        } stderr_handler {"Update Module output (stderr): "};
322✔
88

89
        error::Error processStart;
322✔
90
        if (procOut) {
161✔
91
                // Provide string to put content in.
92
                output.emplace(string());
65✔
93
                processStart = proc.Start(
65✔
94
                        [this](const char *data, size_t size) {
81✔
95
                                // At the moment, no state that queries output accepts more than one line,
96
                                // so reject multiple lines here. This would have been rejected anyway due
97
                                // to matching, but by doing it here, we also prevent using excessive memory
98
                                // if the process dumps a large log on us.
99
                                if (!first_line_captured) {
27✔
100
                                        auto lines = mender::common::SplitString(string(data, size), "\n");
104✔
101
                                        if (lines.size() >= 1) {
26✔
102
                                                *output = lines[0];
26✔
103
                                                first_line_captured = true;
26✔
104
                                        }
105
                                        if (lines.size() > 2 || (lines.size() == 2 && lines[1] != "")) {
26✔
106
                                                too_many_lines = true;
1✔
107
                                        }
108
                                } else {
109
                                        too_many_lines = true;
1✔
110
                                }
111
                        },
27✔
112
                        stderr_handler);
130✔
113
        } else {
114
                processStart =
115
                        proc.Start(OutputHandler {"Update Module output (stdout): "}, stderr_handler);
96✔
116
        }
117
        if (processStart != error::NoError) {
161✔
118
                return GetProcessError(processStart).WithContext(state_string);
×
119
        }
120

121
        error::Error err;
322✔
122
        err = proc.AsyncWait(loop, [this, state, handler](error::Error process_err) {
161✔
123
                timeout.Cancel();
160✔
124
                auto err = process_err.WithContext(StateToString(state));
160✔
125
                ProcessFinishedHandler(state, err);
160✔
126
        });
482✔
127

128
        timeout.AsyncWait(timeout_seconds, [this, state, handler](error::Error inner_err) {
161✔
129
                proc.EnsureTerminated();
1✔
130
                auto err = error::Error(
131
                        make_error_condition(errc::timed_out),
2✔
132
                        StateToString(state) + ": Timed out while waiting for Update Module to complete");
3✔
133
                ProcessFinishedHandler(state, err);
1✔
134
        });
323✔
135

136
        return err;
161✔
137
}
138

139
void UpdateModule::StateRunner::ProcessFinishedHandler(State state, error::Error err) {
161✔
140
        if (state == State::Cleanup) {
161✔
141
                std::error_code ec;
28✔
142
                // False is returned if the directory doesn't exist, and `ec` is only set to an
143
                // error if it's not this type of error, which is what we want.
144
                if (!fs::remove_all(module_work_path, ec) && ec) {
28✔
145
                        err = err.FollowedBy(error::Error(
×
146
                                ec.default_error_condition(),
×
147
                                StateToString(state) + ": Error removing directory: " + module_work_path));
×
148
                }
149
        }
150

151
        if (err == error::NoError && too_many_lines) {
161✔
152
                err = error::Error(
2✔
153
                        make_error_condition(errc::protocol_error),
2✔
154
                        "Too many lines when querying " + StateToString(state));
6✔
155
        }
156

157
        if (err != error::NoError) {
161✔
158
                handler(expected::unexpected(err));
13✔
159
        } else {
160
                handler(output);
148✔
161
        }
162
}
161✔
163

164
} // namespace v3
165
} // namespace update_module
166
} // namespace update
167
} // 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