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