1// 2// Automated Testing Framework (atf) 3// 4// Copyright (c) 2007, 2008, 2009, 2010 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// TODO: We probably don't want to raise std::runtime_error for the errors 31// detected in this file. 32#include <stdexcept> 33 34#include "atf-c++/config.hpp" 35 36#include "atf-c++/detail/fs.hpp" 37#include "atf-c++/detail/env.hpp" 38#include "atf-c++/detail/sanity.hpp" 39#include "atf-c++/detail/text.hpp" 40 41#include "requirements.hpp" 42#include "user.hpp" 43 44namespace impl = atf::atf_run; 45 46namespace { 47 48static 49bool 50has_program(const atf::fs::path& program) 51{ 52 bool found = false; 53 54 if (program.is_absolute()) { 55 found = atf::fs::is_executable(program); 56 } else { 57 if (program.str().find('/') != std::string::npos) 58 throw std::runtime_error("Relative paths are not allowed " 59 "when searching for a program (" + 60 program.str() + ")"); 61 62 const std::vector< std::string > dirs = atf::text::split( 63 atf::env::get("PATH"), ":"); 64 for (std::vector< std::string >::const_iterator iter = dirs.begin(); 65 !found && iter != dirs.end(); iter++) { 66 const atf::fs::path& p = atf::fs::path(*iter) / program; 67 if (atf::fs::is_executable(p)) 68 found = true; 69 } 70 } 71 72 return found; 73} 74 75static 76std::string 77check_arch(const std::string& arches) 78{ 79 const std::vector< std::string > v = atf::text::split(arches, " "); 80 81 for (std::vector< std::string >::const_iterator iter = v.begin(); 82 iter != v.end(); iter++) { 83 if ((*iter) == atf::config::get("atf_arch")) 84 return ""; 85 } 86 87 if (v.size() == 1) 88 return "Requires the '" + arches + "' architecture"; 89 else 90 return "Requires one of the '" + arches + "' architectures"; 91} 92 93static 94std::string 95check_config(const std::string& variables, const atf::tests::vars_map& config) 96{ 97 const std::vector< std::string > v = atf::text::split(variables, " "); 98 for (std::vector< std::string >::const_iterator iter = v.begin(); 99 iter != v.end(); iter++) { 100 if (config.find((*iter)) == config.end()) 101 return "Required configuration variable '" + (*iter) + "' not " 102 "defined"; 103 } 104 return ""; 105} 106 107static 108std::string 109check_machine(const std::string& machines) 110{ 111 const std::vector< std::string > v = atf::text::split(machines, " "); 112 113 for (std::vector< std::string >::const_iterator iter = v.begin(); 114 iter != v.end(); iter++) { 115 if ((*iter) == atf::config::get("atf_machine")) 116 return ""; 117 } 118 119 if (v.size() == 1) 120 return "Requires the '" + machines + "' machine type"; 121 else 122 return "Requires one of the '" + machines + "' machine types"; 123} 124 125static 126std::string 127check_progs(const std::string& progs) 128{ 129 const std::vector< std::string > v = atf::text::split(progs, " "); 130 for (std::vector< std::string >::const_iterator iter = v.begin(); 131 iter != v.end(); iter++) { 132 if (!has_program(atf::fs::path(*iter))) 133 return "Required program '" + (*iter) + "' not found in the PATH"; 134 } 135 return ""; 136} 137 138static 139std::string 140check_user(const std::string& user, const atf::tests::vars_map& config) 141{ 142 if (user == "root") { 143 if (!impl::is_root()) 144 return "Requires root privileges"; 145 else 146 return ""; 147 } else if (user == "unprivileged") { 148 if (impl::is_root()) { 149 const atf::tests::vars_map::const_iterator iter = config.find( 150 "unprivileged-user"); 151 if (iter == config.end()) 152 return "Requires an unprivileged user and the " 153 "'unprivileged-user' configuration variable is not set"; 154 else { 155 const std::string& unprivileged_user = (*iter).second; 156 try { 157 (void)impl::get_user_ids(unprivileged_user); 158 return ""; 159 } catch (const std::runtime_error& e) { 160 return "Failed to get information for user " + 161 unprivileged_user; 162 } 163 } 164 } else 165 return ""; 166 } else 167 throw std::runtime_error("Invalid value '" + user + "' for property " 168 "require.user"); 169} 170 171} // anonymous namespace 172 173std::string 174impl::check_requirements(const atf::tests::vars_map& metadata, 175 const atf::tests::vars_map& config) 176{ 177 std::string failure_reason = ""; 178 179 for (atf::tests::vars_map::const_iterator iter = metadata.begin(); 180 failure_reason.empty() && iter != metadata.end(); iter++) { 181 const std::string& name = (*iter).first; 182 const std::string& value = (*iter).second; 183 INV(!value.empty()); // Enforced by application/X-atf-tp parser. 184 185 if (name == "require.arch") 186 failure_reason = check_arch(value); 187 else if (name == "require.config") 188 failure_reason = check_config(value, config); 189 else if (name == "require.machine") 190 failure_reason = check_machine(value); 191 else if (name == "require.progs") 192 failure_reason = check_progs(value); 193 else if (name == "require.user") 194 failure_reason = check_user(value, config); 195 else { 196 // Unknown require.* properties are forbidden by the 197 // application/X-atf-tp parser. 198 INV(failure_reason.find("require.") != 0); 199 } 200 } 201 202 return failure_reason; 203} 204 205std::pair< int, int > 206impl::get_required_user(const atf::tests::vars_map& metadata, 207 const atf::tests::vars_map& config) 208{ 209 const atf::tests::vars_map::const_iterator user = metadata.find( 210 "require.user"); 211 if (user == metadata.end()) 212 return std::make_pair(-1, -1); 213 214 if ((*user).second == "unprivileged") { 215 if (impl::is_root()) { 216 const atf::tests::vars_map::const_iterator iter = config.find( 217 "unprivileged-user"); 218 try { 219 return impl::get_user_ids((*iter).second); 220 } catch (const std::exception& e) { 221 UNREACHABLE; // This has been validated by check_user. 222 throw e; 223 } 224 } else { 225 return std::make_pair(-1, -1); 226 } 227 } else 228 return std::make_pair(-1, -1); 229} 230