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