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

mendersoftware / mender / 1054626264

30 Oct 2023 10:27AM UTC coverage: 80.137% (-0.06%) from 80.194%
1054626264

push

gitlab-ci

kacf
chore: Add many missing error checks and exception harnesses.

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

100 of 100 new or added lines in 7 files covered. (100.0%)

6887 of 8594 relevant lines covered (80.14%)

9361.04 hits per line

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

67.09
/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
namespace processes = mender::common::processes;
37

38

39
UpdateModule::StateRunner::StateRunner(
841✔
40
        events::EventLoop &loop,
41
        State state,
42
        const string &module_path,
43
        const string &module_work_path) :
841✔
44
        loop(loop),
45
        module_work_path(module_work_path),
46
        proc({module_path, StateToString(state), module_work_path}) {
3,364✔
47
        proc.SetWorkDir(module_work_path);
48
}
841✔
49

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

54
        string state_string = StateToString(state);
841✔
55
        error_code ec;
841✔
56
        if (!fs::is_directory(module_work_path, ec) && ec) {
841✔
57
                if (state == State::Cleanup) {
1✔
58
                        loop.Post([this, state]() { ProcessFinishedHandler(state, error::NoError); });
×
59
                        return error::NoError;
×
60
                } else if (ec) {
1✔
61
                        return error::Error(
62
                                ec.default_error_condition(),
2✔
63
                                state_string + ": Error while checking file tree: " + module_work_path);
2✔
64
                } else {
65
                        return error::Error(
66
                                make_error_condition(errc::no_such_file_or_directory),
×
67
                                state_string + ": File tree does not exist: " + module_work_path);
×
68
                }
69
        }
70

71
        processes::OutputHandler stderr_handler {"Update Module output (stderr): "};
840✔
72

73
        error::Error processStart;
840✔
74
        if (procOut) {
840✔
75
                // Provide string to put content in.
76
                output.emplace(string());
300✔
77
                processStart = proc.Start(
300✔
78
                        [this](const char *data, size_t size) {
199✔
79
                                // At the moment, no state that queries output accepts more than one line,
80
                                // so reject multiple lines here. This would have been rejected anyway due
81
                                // to matching, but by doing it here, we also prevent using excessive memory
82
                                // if the process dumps a large log on us.
83
                                if (!first_line_captured) {
200✔
84
                                        auto lines = mender::common::SplitString(string(data, size), "\n");
597✔
85
                                        if (lines.size() >= 1) {
199✔
86
                                                *output = lines[0];
87
                                                first_line_captured = true;
199✔
88
                                        }
89
                                        if (lines.size() > 2 || (lines.size() == 2 && lines[1] != "")) {
199✔
90
                                                too_many_lines = true;
2✔
91
                                        }
92
                                } else {
93
                                        too_many_lines = true;
1✔
94
                                }
95
                        },
200✔
96
                        stderr_handler);
600✔
97
        } else {
98
                processStart = proc.Start(
540✔
99
                        processes::OutputHandler {"Update Module output (stdout): "}, stderr_handler);
1,080✔
100
        }
101
        if (processStart != error::NoError) {
840✔
102
                return GetProcessError(processStart).WithContext(state_string);
×
103
        }
104

105
        error::Error err;
840✔
106
        err = proc.AsyncWait(
840✔
107
                loop,
108
                [this, state, handler](error::Error process_err) {
5,038✔
109
                        if (process_err.code == make_error_condition(errc::timed_out)) {
840✔
110
                                proc.EnsureTerminated();
2✔
111
                        }
112

113
                        auto err = process_err.WithContext(StateToString(state));
840✔
114
                        ProcessFinishedHandler(state, err);
1,680✔
115
                },
840✔
116
                timeout_seconds);
1,680✔
117

118
        return err;
840✔
119
}
120

121
void UpdateModule::StateRunner::ProcessFinishedHandler(State state, error::Error err) {
840✔
122
        if (state == State::Cleanup) {
840✔
123
                std::error_code ec;
122✔
124
                // False is returned if the directory doesn't exist, and `ec` is only set to an
125
                // error if it's not this type of error, which is what we want.
126
                if (!fs::remove_all(module_work_path, ec) && ec) {
122✔
127
                        err = err.FollowedBy(error::Error(
×
128
                                ec.default_error_condition(),
×
129
                                StateToString(state) + ": Error removing directory: " + module_work_path));
×
130
                }
131
        }
132

133
        if (err == error::NoError && too_many_lines) {
840✔
134
                err = error::Error(
3✔
135
                        make_error_condition(errc::protocol_error),
6✔
136
                        "Too many lines when querying " + StateToString(state));
9✔
137
        }
138

139
        if (err != error::NoError) {
840✔
140
                handler(expected::unexpected(err));
126✔
141
        } else {
142
                handler(output);
1,554✔
143
        }
144
}
840✔
145

146
error::Error UpdateModule::AsyncSystemReboot(
×
147
        events::EventLoop &event_loop, StateFinishedHandler handler) {
148
        system_reboot_.reset(new SystemRebootRunner {vector<string> {"reboot"}, event_loop});
×
149

150
        log::Info("Calling `reboot` command and waiting for system to restart.");
×
151
        system_reboot_->proc.Start();
×
152

153
        auto err = system_reboot_->proc.AsyncWait(event_loop, [](error::Error err) {
×
154
                // Even if it returns, give the reboot ten minutes to kill us. `handler` will only
155
                // be called from the timeout handler.
156
                if (err != error::NoError) {
×
157
                        log::Warning("`reboot` command returned error: " + err.String());
×
158
                }
159
        });
×
160
        if (err != error::NoError) {
×
161
                return err.WithContext("Unable to call system reboot command");
×
162
        }
163

164
        system_reboot_->timeout.AsyncWait(chrono::minutes(10), [handler](error::Error err) {
×
165
                if (err != error::NoError) {
×
166
                        handler(err.WithContext("UpdateModule::AsyncSystemReboot"));
×
167
                }
168

169
                handler(error::Error(
×
170
                        make_error_condition(errc::timed_out),
×
171
                        "`reboot` command did not kill us; rebooting failed"));
×
172
        });
×
173

174
        return error::NoError;
×
175
}
176

177
} // namespace v3
178
} // namespace update_module
179
} // namespace update
180
} // 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