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 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 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 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 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 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 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 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 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 190 check_memory_unknown(const int64_t needed ATF_DEFS_ATTRIBUTE_UNUSED) 191 { 192 return ""; 193 } 194 #endif 195 196 static 197 std::string 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 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 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 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 > 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