1 //
2 // Automated Testing Framework (atf)
3 //
4 // Copyright (c) 2007 The NetBSD Foundation, Inc.
5 // All rights reserved.
6 //
7 // Redistribution and use in source and binary forms, with or without
8 // modification, are permitted provided that the following conditions
9 // are met:
10 // 1. Redistributions of source code must retain the above copyright
11 // notice, this list of conditions and the following disclaimer.
12 // 2. Redistributions in binary form must reproduce the above copyright
13 // notice, this list of conditions and the following disclaimer in the
14 // documentation and/or other materials provided with the distribution.
15 //
16 // THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
17 // CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
18 // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19 // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 // IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
21 // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
23 // GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25 // IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
26 // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
27 // IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 //
29
30 extern "C" {
31 #include <sys/param.h>
32 #include <sys/sysctl.h>
33 }
34
35 #include <cerrno>
36 #include <cstring>
37 #include <stdexcept>
38
39 extern "C" {
40 #include "atf-c/defs.h"
41 }
42
43 #include "atf-c++/config.hpp"
44
45 #include "atf-c++/detail/fs.hpp"
46 #include "atf-c++/detail/env.hpp"
47 #include "atf-c++/detail/sanity.hpp"
48 #include "atf-c++/detail/text.hpp"
49
50 #include "requirements.hpp"
51 #include "user.hpp"
52
53 namespace impl = atf::atf_run;
54
55 namespace {
56
57 static
58 bool
has_program(const atf::fs::path & program)59 has_program(const atf::fs::path& program)
60 {
61 bool found = false;
62
63 if (program.is_absolute()) {
64 found = atf::fs::is_executable(program);
65 } else {
66 if (program.str().find('/') != std::string::npos)
67 throw std::runtime_error("Relative paths are not allowed "
68 "when searching for a program (" +
69 program.str() + ")");
70
71 const std::vector< std::string > dirs = atf::text::split(
72 atf::env::get("PATH"), ":");
73 for (std::vector< std::string >::const_iterator iter = dirs.begin();
74 !found && iter != dirs.end(); iter++) {
75 const atf::fs::path& p = atf::fs::path(*iter) / program;
76 if (atf::fs::is_executable(p))
77 found = true;
78 }
79 }
80
81 return found;
82 }
83
84 static
85 std::string
check_arch(const std::string & arches)86 check_arch(const std::string& arches)
87 {
88 const std::vector< std::string > v = atf::text::split(arches, " ");
89
90 for (std::vector< std::string >::const_iterator iter = v.begin();
91 iter != v.end(); iter++) {
92 if ((*iter) == atf::config::get("atf_arch"))
93 return "";
94 }
95
96 if (v.size() == 1)
97 return "Requires the '" + arches + "' architecture";
98 else
99 return "Requires one of the '" + arches + "' architectures";
100 }
101
102 static
103 std::string
check_config(const std::string & variables,const atf::tests::vars_map & config)104 check_config(const std::string& variables, const atf::tests::vars_map& config)
105 {
106 const std::vector< std::string > v = atf::text::split(variables, " ");
107 for (std::vector< std::string >::const_iterator iter = v.begin();
108 iter != v.end(); iter++) {
109 if (config.find((*iter)) == config.end())
110 return "Required configuration variable '" + (*iter) + "' not "
111 "defined";
112 }
113 return "";
114 }
115
116 static
117 std::string
check_files(const std::string & progs)118 check_files(const std::string& progs)
119 {
120 const std::vector< std::string > v = atf::text::split(progs, " ");
121 for (std::vector< std::string >::const_iterator iter = v.begin();
122 iter != v.end(); iter++) {
123 const atf::fs::path file(*iter);
124 if (!file.is_absolute())
125 throw std::runtime_error("Relative paths are not allowed when "
126 "checking for a required file (" + file.str() + ")");
127 if (!atf::fs::exists(file))
128 return "Required file '" + file.str() + "' not found";
129 }
130 return "";
131 }
132
133 static
134 std::string
check_machine(const std::string & machines)135 check_machine(const std::string& machines)
136 {
137 const std::vector< std::string > v = atf::text::split(machines, " ");
138
139 for (std::vector< std::string >::const_iterator iter = v.begin();
140 iter != v.end(); iter++) {
141 if ((*iter) == atf::config::get("atf_machine"))
142 return "";
143 }
144
145 if (v.size() == 1)
146 return "Requires the '" + machines + "' machine type";
147 else
148 return "Requires one of the '" + machines + "' machine types";
149 }
150
151 #if defined(__APPLE__) || defined(__NetBSD__)
152 static
153 std::string
check_memory_sysctl(const int64_t needed,const char * sysctl_variable)154 check_memory_sysctl(const int64_t needed, const char* sysctl_variable)
155 {
156 int64_t available;
157 std::size_t available_length = sizeof(available);
158 if (::sysctlbyname(sysctl_variable, &available, &available_length,
159 NULL, 0) == -1) {
160 const char* e = std::strerror(errno);
161 return "Failed to get sysctl(hw.usermem64) value: " + std::string(e);
162 }
163
164 if (available < needed) {
165 return "Not enough memory; needed " + atf::text::to_string(needed) +
166 ", available " + atf::text::to_string(available);
167 } else
168 return "";
169 }
170 # if defined(__APPLE__)
171 static
172 std::string
check_memory_darwin(const int64_t needed)173 check_memory_darwin(const int64_t needed)
174 {
175 return check_memory_sysctl(needed, "hw.usermem");
176 }
177 # elif defined(__NetBSD__)
178 static
179 std::string
check_memory_netbsd(const int64_t needed)180 check_memory_netbsd(const int64_t needed)
181 {
182 return check_memory_sysctl(needed, "hw.usermem64");
183 }
184 # else
185 # error "Conditional error"
186 # endif
187 #else
188 static
189 std::string
check_memory_unknown(const int64_t needed ATF_DEFS_ATTRIBUTE_UNUSED)190 check_memory_unknown(const int64_t needed ATF_DEFS_ATTRIBUTE_UNUSED)
191 {
192 return "";
193 }
194 #endif
195
196 static
197 std::string
check_memory(const std::string & raw_memory)198 check_memory(const std::string& raw_memory)
199 {
200 const int64_t needed = atf::text::to_bytes(raw_memory);
201
202 #if defined(__APPLE__)
203 return check_memory_darwin(needed);
204 #elif defined(__NetBSD__)
205 return check_memory_netbsd(needed);
206 #else
207 return check_memory_unknown(needed);
208 #endif
209 }
210
211 static
212 std::string
check_progs(const std::string & progs)213 check_progs(const std::string& progs)
214 {
215 const std::vector< std::string > v = atf::text::split(progs, " ");
216 for (std::vector< std::string >::const_iterator iter = v.begin();
217 iter != v.end(); iter++) {
218 if (!has_program(atf::fs::path(*iter)))
219 return "Required program '" + (*iter) + "' not found in the PATH";
220 }
221 return "";
222 }
223
224 static
225 std::string
check_user(const std::string & user,const atf::tests::vars_map & config)226 check_user(const std::string& user, const atf::tests::vars_map& config)
227 {
228 if (user == "root") {
229 if (!impl::is_root())
230 return "Requires root privileges";
231 else
232 return "";
233 } else if (user == "unprivileged") {
234 if (impl::is_root()) {
235 const atf::tests::vars_map::const_iterator iter = config.find(
236 "unprivileged-user");
237 if (iter == config.end())
238 return "Requires an unprivileged user and the "
239 "'unprivileged-user' configuration variable is not set";
240 else {
241 const std::string& unprivileged_user = (*iter).second;
242 try {
243 (void)impl::get_user_ids(unprivileged_user);
244 return "";
245 } catch (const std::runtime_error& e) {
246 return "Failed to get information for user " +
247 unprivileged_user;
248 }
249 }
250 } else
251 return "";
252 } else
253 throw std::runtime_error("Invalid value '" + user + "' for property "
254 "require.user");
255 }
256
257 } // anonymous namespace
258
259 std::string
check_requirements(const atf::tests::vars_map & metadata,const atf::tests::vars_map & config)260 impl::check_requirements(const atf::tests::vars_map& metadata,
261 const atf::tests::vars_map& config)
262 {
263 std::string failure_reason = "";
264
265 for (atf::tests::vars_map::const_iterator iter = metadata.begin();
266 failure_reason.empty() && iter != metadata.end(); iter++) {
267 const std::string& name = (*iter).first;
268 const std::string& value = (*iter).second;
269 INV(!value.empty()); // Enforced by application/X-atf-tp parser.
270
271 if (name == "require.arch")
272 failure_reason = check_arch(value);
273 else if (name == "require.config")
274 failure_reason = check_config(value, config);
275 else if (name == "require.files")
276 failure_reason = check_files(value);
277 else if (name == "require.machine")
278 failure_reason = check_machine(value);
279 else if (name == "require.memory")
280 failure_reason = check_memory(value);
281 else if (name == "require.progs")
282 failure_reason = check_progs(value);
283 else if (name == "require.user")
284 failure_reason = check_user(value, config);
285 else {
286 // Unknown require.* properties are forbidden by the
287 // application/X-atf-tp parser.
288 INV(failure_reason.find("require.") != 0);
289 }
290 }
291
292 return failure_reason;
293 }
294
295 std::pair< int, int >
get_required_user(const atf::tests::vars_map & metadata,const atf::tests::vars_map & config)296 impl::get_required_user(const atf::tests::vars_map& metadata,
297 const atf::tests::vars_map& config)
298 {
299 const atf::tests::vars_map::const_iterator user = metadata.find(
300 "require.user");
301 if (user == metadata.end())
302 return std::make_pair(-1, -1);
303
304 if ((*user).second == "unprivileged") {
305 if (impl::is_root()) {
306 const atf::tests::vars_map::const_iterator iter = config.find(
307 "unprivileged-user");
308 try {
309 return impl::get_user_ids((*iter).second);
310 } catch (const std::exception& e) {
311 UNREACHABLE; // This has been validated by check_user.
312 throw e;
313 }
314 } else {
315 return std::make_pair(-1, -1);
316 }
317 } else
318 return std::make_pair(-1, -1);
319 }
320