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 <cassert> 36 #include <cerrno> 37 #include <cstdlib> 38 #include <cstring> 39 #include <stdexcept> 40 41 #include "config.hpp" 42 #include "env.hpp" 43 #include "fs.hpp" 44 #include "requirements.hpp" 45 #include "text.hpp" 46 #include "user.hpp" 47 48 namespace impl = tools; 49 50 namespace { 51 52 typedef std::map< std::string, std::string > vars_map; 53 54 static 55 bool 56 has_program(const tools::fs::path& program) 57 { 58 bool found = false; 59 60 if (program.is_absolute()) { 61 found = tools::fs::is_executable(program); 62 } else { 63 if (program.str().find('/') != std::string::npos) 64 throw std::runtime_error("Relative paths are not allowed " 65 "when searching for a program (" + 66 program.str() + ")"); 67 68 const std::vector< std::string > dirs = tools::text::split( 69 tools::env::get("PATH"), ":"); 70 for (std::vector< std::string >::const_iterator iter = dirs.begin(); 71 !found && iter != dirs.end(); iter++) { 72 const tools::fs::path& p = tools::fs::path(*iter) / program; 73 if (tools::fs::is_executable(p)) 74 found = true; 75 } 76 } 77 78 return found; 79 } 80 81 static 82 std::string 83 check_arch(const std::string& arches) 84 { 85 const std::vector< std::string > v = tools::text::split(arches, " "); 86 87 for (std::vector< std::string >::const_iterator iter = v.begin(); 88 iter != v.end(); iter++) { 89 if ((*iter) == tools::config::get("atf_arch")) 90 return ""; 91 } 92 93 if (v.size() == 1) 94 return "Requires the '" + arches + "' architecture"; 95 else 96 return "Requires one of the '" + arches + "' architectures"; 97 } 98 99 static 100 std::string 101 check_config(const std::string& variables, const vars_map& config) 102 { 103 const std::vector< std::string > v = tools::text::split(variables, " "); 104 for (std::vector< std::string >::const_iterator iter = v.begin(); 105 iter != v.end(); iter++) { 106 if (config.find((*iter)) == config.end()) 107 return "Required configuration variable '" + (*iter) + "' not " 108 "defined"; 109 } 110 return ""; 111 } 112 113 static 114 std::string 115 check_files(const std::string& progs) 116 { 117 const std::vector< std::string > v = tools::text::split(progs, " "); 118 for (std::vector< std::string >::const_iterator iter = v.begin(); 119 iter != v.end(); iter++) { 120 const tools::fs::path file(*iter); 121 if (!file.is_absolute()) 122 throw std::runtime_error("Relative paths are not allowed when " 123 "checking for a required file (" + file.str() + ")"); 124 if (!tools::fs::exists(file)) 125 return "Required file '" + file.str() + "' not found"; 126 } 127 return ""; 128 } 129 130 static 131 std::string 132 check_machine(const std::string& machines) 133 { 134 const std::vector< std::string > v = tools::text::split(machines, " "); 135 136 for (std::vector< std::string >::const_iterator iter = v.begin(); 137 iter != v.end(); iter++) { 138 if ((*iter) == tools::config::get("atf_machine")) 139 return ""; 140 } 141 142 if (v.size() == 1) 143 return "Requires the '" + machines + "' machine type"; 144 else 145 return "Requires one of the '" + machines + "' machine types"; 146 } 147 148 static 149 std::string 150 check_memory(const std::string& raw_memory) 151 { 152 const int64_t needed = tools::text::to_bytes(raw_memory); 153 154 int64_t available; 155 std::size_t available_length = sizeof(available); 156 if (::sysctlbyname("hw.usermem64", &available, &available_length, 157 NULL, 0) == -1) { 158 const char* e = std::strerror(errno); 159 return "Failed to get sysctl(hw.usermem64) value: " + std::string(e); 160 } 161 162 if (available < needed) { 163 return "Not enough memory; needed " + tools::text::to_string(needed) + 164 ", available " + tools::text::to_string(available); 165 } else 166 return ""; 167 } 168 169 static 170 std::string 171 check_progs(const std::string& progs) 172 { 173 const std::vector< std::string > v = tools::text::split(progs, " "); 174 for (std::vector< std::string >::const_iterator iter = v.begin(); 175 iter != v.end(); iter++) { 176 if (!has_program(tools::fs::path(*iter))) 177 return "Required program '" + (*iter) + "' not found in the PATH"; 178 } 179 return ""; 180 } 181 182 static 183 std::string 184 check_user(const std::string& user, const vars_map& config) 185 { 186 if (user == "root") { 187 if (!tools::user::is_root()) 188 return "Requires root privileges"; 189 else 190 return ""; 191 } else if (user == "unprivileged") { 192 if (tools::user::is_root()) { 193 const vars_map::const_iterator iter = config.find( 194 "unprivileged-user"); 195 if (iter == config.end()) 196 return "Requires an unprivileged user and the " 197 "'unprivileged-user' configuration variable is not set"; 198 else { 199 const std::string& unprivileged_user = (*iter).second; 200 try { 201 (void)tools::user::get_user_ids(unprivileged_user); 202 return ""; 203 } catch (const std::runtime_error& e) { 204 return "Failed to get information for user " + 205 unprivileged_user; 206 } 207 } 208 } else 209 return ""; 210 } else 211 throw std::runtime_error("Invalid value '" + user + "' for property " 212 "require.user"); 213 } 214 215 } // anonymous namespace 216 217 std::string 218 impl::check_requirements(const vars_map& metadata, 219 const vars_map& config) 220 { 221 std::string failure_reason = ""; 222 223 for (vars_map::const_iterator iter = metadata.begin(); 224 failure_reason.empty() && iter != metadata.end(); iter++) { 225 const std::string& name = (*iter).first; 226 const std::string& value = (*iter).second; 227 assert(!value.empty()); // Enforced by application/X-atf-tp parser. 228 229 if (name == "require.arch") 230 failure_reason = check_arch(value); 231 else if (name == "require.config") 232 failure_reason = check_config(value, config); 233 else if (name == "require.files") 234 failure_reason = check_files(value); 235 else if (name == "require.machine") 236 failure_reason = check_machine(value); 237 else if (name == "require.memory") 238 failure_reason = check_memory(value); 239 else if (name == "require.progs") 240 failure_reason = check_progs(value); 241 else if (name == "require.user") 242 failure_reason = check_user(value, config); 243 else { 244 // Unknown require.* properties are forbidden by the 245 // application/X-atf-tp parser. 246 assert(failure_reason.find("require.") != 0); 247 } 248 } 249 250 return failure_reason; 251 } 252 253 std::pair< int, int > 254 impl::get_required_user(const vars_map& metadata, 255 const vars_map& config) 256 { 257 const vars_map::const_iterator user = metadata.find( 258 "require.user"); 259 if (user == metadata.end()) 260 return std::make_pair(-1, -1); 261 262 if ((*user).second == "unprivileged") { 263 if (tools::user::is_root()) { 264 const vars_map::const_iterator iter = config.find( 265 "unprivileged-user"); 266 try { 267 return tools::user::get_user_ids((*iter).second); 268 } catch (const std::exception& e) { 269 std::abort(); // This has been validated by check_user. 270 } 271 } else { 272 return std::make_pair(-1, -1); 273 } 274 } else 275 return std::make_pair(-1, -1); 276 } 277