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

mendersoftware / mender / 950534094

pending completion
950534094

push

gitlab-ci

kacf
chore: Add `StartsWith` and `EndsWith` generic utility functions.

Also use the latter in `states.cpp`.

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

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

4931 of 6276 relevant lines covered (78.57%)

196.18 hits per line

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

88.89
/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(
381✔
38
        events::EventLoop &loop,
39
        State state,
40
        const string &module_path,
41
        const string &module_work_path) :
381✔
42
        loop(loop),
43
        module_work_path(module_work_path),
44
        proc({module_path, StateToString(state), module_work_path}),
45
        timeout(loop) {
1,524✔
46
        proc.SetWorkDir(module_work_path);
381✔
47
}
381✔
48

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

53
        string state_string = StateToString(state);
762✔
54
        if (!fs::is_directory(module_work_path)) {
381✔
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): "};
760✔
88

89
        error::Error processStart;
760✔
90
        if (procOut) {
380✔
91
                // Provide string to put content in.
92
                output.emplace(string());
114✔
93
                processStart = proc.Start(
114✔
94
                        [this](const char *data, size_t size) {
228✔
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) {
76✔
100
                                        auto lines = mender::common::SplitString(string(data, size), "\n");
300✔
101
                                        if (lines.size() >= 1) {
75✔
102
                                                *output = lines[0];
75✔
103
                                                first_line_captured = true;
75✔
104
                                        }
105
                                        if (lines.size() > 2 || (lines.size() == 2 && lines[1] != "")) {
75✔
106
                                                too_many_lines = true;
1✔
107
                                        }
108
                                } else {
109
                                        too_many_lines = true;
1✔
110
                                }
111
                        },
76✔
112
                        stderr_handler);
228✔
113
        } else {
114
                processStart =
115
                        proc.Start(OutputHandler {"Update Module output (stdout): "}, stderr_handler);
266✔
116
        }
117
        if (processStart != error::NoError) {
380✔
118
                return GetProcessError(processStart).WithContext(state_string);
×
119
        }
120

121
        error::Error err;
760✔
122
        err = proc.AsyncWait(loop, [this, state, handler](error::Error process_err) {
380✔
123
                // Cancel the timer, so we don't get two handlers called.
124
                timeout.Cancel();
378✔
125
                auto err = process_err.WithContext(StateToString(state));
378✔
126
                ProcessFinishedHandler(state, err);
378✔
127
        });
1,138✔
128

129
        timeout.AsyncWait(timeout_seconds, [this, state, handler](error::Error inner_err) {
380✔
130
                // Cancel the AsyncWait, so we don't get two handlers called.
131
                proc.Cancel();
2✔
132
                proc.EnsureTerminated();
2✔
133
                auto err = error::Error(
134
                        make_error_condition(errc::timed_out),
4✔
135
                        StateToString(state) + ": Timed out while waiting for Update Module to complete");
6✔
136
                ProcessFinishedHandler(state, err);
2✔
137
        });
762✔
138

139
        return err;
380✔
140
}
141

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

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

160
        if (err != error::NoError) {
380✔
161
                handler(expected::unexpected(err));
42✔
162
        } else {
163
                handler(output);
338✔
164
        }
165
}
380✔
166

167
} // namespace v3
168
} // namespace update_module
169
} // namespace update
170
} // 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