1*b0d29bc4SBrooks Davis // Copyright 2012 The Kyua Authors. 2*b0d29bc4SBrooks Davis // All rights reserved. 3*b0d29bc4SBrooks Davis // 4*b0d29bc4SBrooks Davis // Redistribution and use in source and binary forms, with or without 5*b0d29bc4SBrooks Davis // modification, are permitted provided that the following conditions are 6*b0d29bc4SBrooks Davis // met: 7*b0d29bc4SBrooks Davis // 8*b0d29bc4SBrooks Davis // * Redistributions of source code must retain the above copyright 9*b0d29bc4SBrooks Davis // notice, this list of conditions and the following disclaimer. 10*b0d29bc4SBrooks Davis // * Redistributions in binary form must reproduce the above copyright 11*b0d29bc4SBrooks Davis // notice, this list of conditions and the following disclaimer in the 12*b0d29bc4SBrooks Davis // documentation and/or other materials provided with the distribution. 13*b0d29bc4SBrooks Davis // * Neither the name of Google Inc. nor the names of its contributors 14*b0d29bc4SBrooks Davis // may be used to endorse or promote products derived from this software 15*b0d29bc4SBrooks Davis // without specific prior written permission. 16*b0d29bc4SBrooks Davis // 17*b0d29bc4SBrooks Davis // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18*b0d29bc4SBrooks Davis // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19*b0d29bc4SBrooks Davis // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20*b0d29bc4SBrooks Davis // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21*b0d29bc4SBrooks Davis // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22*b0d29bc4SBrooks Davis // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23*b0d29bc4SBrooks Davis // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24*b0d29bc4SBrooks Davis // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25*b0d29bc4SBrooks Davis // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26*b0d29bc4SBrooks Davis // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27*b0d29bc4SBrooks Davis // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28*b0d29bc4SBrooks Davis 29*b0d29bc4SBrooks Davis #include "engine/requirements.hpp" 30*b0d29bc4SBrooks Davis 31*b0d29bc4SBrooks Davis #include "model/metadata.hpp" 32*b0d29bc4SBrooks Davis #include "model/types.hpp" 33*b0d29bc4SBrooks Davis #include "utils/config/nodes.ipp" 34*b0d29bc4SBrooks Davis #include "utils/config/tree.ipp" 35*b0d29bc4SBrooks Davis #include "utils/format/macros.hpp" 36*b0d29bc4SBrooks Davis #include "utils/fs/operations.hpp" 37*b0d29bc4SBrooks Davis #include "utils/fs/path.hpp" 38*b0d29bc4SBrooks Davis #include "utils/memory.hpp" 39*b0d29bc4SBrooks Davis #include "utils/passwd.hpp" 40*b0d29bc4SBrooks Davis #include "utils/sanity.hpp" 41*b0d29bc4SBrooks Davis #include "utils/units.hpp" 42*b0d29bc4SBrooks Davis 43*b0d29bc4SBrooks Davis namespace config = utils::config; 44*b0d29bc4SBrooks Davis namespace fs = utils::fs; 45*b0d29bc4SBrooks Davis namespace passwd = utils::passwd; 46*b0d29bc4SBrooks Davis namespace units = utils::units; 47*b0d29bc4SBrooks Davis 48*b0d29bc4SBrooks Davis 49*b0d29bc4SBrooks Davis namespace { 50*b0d29bc4SBrooks Davis 51*b0d29bc4SBrooks Davis 52*b0d29bc4SBrooks Davis /// Checks if all required configuration variables are present. 53*b0d29bc4SBrooks Davis /// 54*b0d29bc4SBrooks Davis /// \param required_configs Set of required variable names. 55*b0d29bc4SBrooks Davis /// \param user_config Runtime user configuration. 56*b0d29bc4SBrooks Davis /// \param test_suite_name Name of the test suite the test belongs to. 57*b0d29bc4SBrooks Davis /// 58*b0d29bc4SBrooks Davis /// \return Empty if all variables are present or an error message otherwise. 59*b0d29bc4SBrooks Davis static std::string 60*b0d29bc4SBrooks Davis check_required_configs(const model::strings_set& required_configs, 61*b0d29bc4SBrooks Davis const config::tree& user_config, 62*b0d29bc4SBrooks Davis const std::string& test_suite_name) 63*b0d29bc4SBrooks Davis { 64*b0d29bc4SBrooks Davis for (model::strings_set::const_iterator iter = required_configs.begin(); 65*b0d29bc4SBrooks Davis iter != required_configs.end(); iter++) { 66*b0d29bc4SBrooks Davis std::string property; 67*b0d29bc4SBrooks Davis // TODO(jmmv): All this rewrite logic belongs in the ATF interface. 68*b0d29bc4SBrooks Davis if ((*iter) == "unprivileged-user" || (*iter) == "unprivileged_user") 69*b0d29bc4SBrooks Davis property = "unprivileged_user"; 70*b0d29bc4SBrooks Davis else 71*b0d29bc4SBrooks Davis property = F("test_suites.%s.%s") % test_suite_name % (*iter); 72*b0d29bc4SBrooks Davis 73*b0d29bc4SBrooks Davis if (!user_config.is_set(property)) 74*b0d29bc4SBrooks Davis return F("Required configuration property '%s' not defined") % 75*b0d29bc4SBrooks Davis (*iter); 76*b0d29bc4SBrooks Davis } 77*b0d29bc4SBrooks Davis return ""; 78*b0d29bc4SBrooks Davis } 79*b0d29bc4SBrooks Davis 80*b0d29bc4SBrooks Davis 81*b0d29bc4SBrooks Davis /// Checks if the allowed architectures match the current architecture. 82*b0d29bc4SBrooks Davis /// 83*b0d29bc4SBrooks Davis /// \param allowed_architectures Set of allowed architectures. 84*b0d29bc4SBrooks Davis /// \param user_config Runtime user configuration. 85*b0d29bc4SBrooks Davis /// 86*b0d29bc4SBrooks Davis /// \return Empty if the current architecture is in the list or an error 87*b0d29bc4SBrooks Davis /// message otherwise. 88*b0d29bc4SBrooks Davis static std::string 89*b0d29bc4SBrooks Davis check_allowed_architectures(const model::strings_set& allowed_architectures, 90*b0d29bc4SBrooks Davis const config::tree& user_config) 91*b0d29bc4SBrooks Davis { 92*b0d29bc4SBrooks Davis if (!allowed_architectures.empty()) { 93*b0d29bc4SBrooks Davis const std::string architecture = 94*b0d29bc4SBrooks Davis user_config.lookup< config::string_node >("architecture"); 95*b0d29bc4SBrooks Davis if (allowed_architectures.find(architecture) == 96*b0d29bc4SBrooks Davis allowed_architectures.end()) 97*b0d29bc4SBrooks Davis return F("Current architecture '%s' not supported") % architecture; 98*b0d29bc4SBrooks Davis } 99*b0d29bc4SBrooks Davis return ""; 100*b0d29bc4SBrooks Davis } 101*b0d29bc4SBrooks Davis 102*b0d29bc4SBrooks Davis 103*b0d29bc4SBrooks Davis /// Checks if the allowed platforms match the current architecture. 104*b0d29bc4SBrooks Davis /// 105*b0d29bc4SBrooks Davis /// \param allowed_platforms Set of allowed platforms. 106*b0d29bc4SBrooks Davis /// \param user_config Runtime user configuration. 107*b0d29bc4SBrooks Davis /// 108*b0d29bc4SBrooks Davis /// \return Empty if the current platform is in the list or an error message 109*b0d29bc4SBrooks Davis /// otherwise. 110*b0d29bc4SBrooks Davis static std::string 111*b0d29bc4SBrooks Davis check_allowed_platforms(const model::strings_set& allowed_platforms, 112*b0d29bc4SBrooks Davis const config::tree& user_config) 113*b0d29bc4SBrooks Davis { 114*b0d29bc4SBrooks Davis if (!allowed_platforms.empty()) { 115*b0d29bc4SBrooks Davis const std::string platform = 116*b0d29bc4SBrooks Davis user_config.lookup< config::string_node >("platform"); 117*b0d29bc4SBrooks Davis if (allowed_platforms.find(platform) == allowed_platforms.end()) 118*b0d29bc4SBrooks Davis return F("Current platform '%s' not supported") % platform; 119*b0d29bc4SBrooks Davis } 120*b0d29bc4SBrooks Davis return ""; 121*b0d29bc4SBrooks Davis } 122*b0d29bc4SBrooks Davis 123*b0d29bc4SBrooks Davis 124*b0d29bc4SBrooks Davis /// Checks if the current user matches the required user. 125*b0d29bc4SBrooks Davis /// 126*b0d29bc4SBrooks Davis /// \param required_user Name of the required user category. 127*b0d29bc4SBrooks Davis /// \param user_config Runtime user configuration. 128*b0d29bc4SBrooks Davis /// 129*b0d29bc4SBrooks Davis /// \return Empty if the current user fits the required user characteristics or 130*b0d29bc4SBrooks Davis /// an error message otherwise. 131*b0d29bc4SBrooks Davis static std::string 132*b0d29bc4SBrooks Davis check_required_user(const std::string& required_user, 133*b0d29bc4SBrooks Davis const config::tree& user_config) 134*b0d29bc4SBrooks Davis { 135*b0d29bc4SBrooks Davis if (!required_user.empty()) { 136*b0d29bc4SBrooks Davis const passwd::user user = passwd::current_user(); 137*b0d29bc4SBrooks Davis if (required_user == "root") { 138*b0d29bc4SBrooks Davis if (!user.is_root()) 139*b0d29bc4SBrooks Davis return "Requires root privileges"; 140*b0d29bc4SBrooks Davis } else if (required_user == "unprivileged") { 141*b0d29bc4SBrooks Davis if (user.is_root()) 142*b0d29bc4SBrooks Davis if (!user_config.is_set("unprivileged_user")) 143*b0d29bc4SBrooks Davis return "Requires an unprivileged user but the " 144*b0d29bc4SBrooks Davis "unprivileged-user configuration variable is not " 145*b0d29bc4SBrooks Davis "defined"; 146*b0d29bc4SBrooks Davis } else 147*b0d29bc4SBrooks Davis UNREACHABLE_MSG("Value of require.user not properly validated"); 148*b0d29bc4SBrooks Davis } 149*b0d29bc4SBrooks Davis return ""; 150*b0d29bc4SBrooks Davis } 151*b0d29bc4SBrooks Davis 152*b0d29bc4SBrooks Davis 153*b0d29bc4SBrooks Davis /// Checks if all required files exist. 154*b0d29bc4SBrooks Davis /// 155*b0d29bc4SBrooks Davis /// \param required_files Set of paths. 156*b0d29bc4SBrooks Davis /// 157*b0d29bc4SBrooks Davis /// \return Empty if the required files all exist or an error message otherwise. 158*b0d29bc4SBrooks Davis static std::string 159*b0d29bc4SBrooks Davis check_required_files(const model::paths_set& required_files) 160*b0d29bc4SBrooks Davis { 161*b0d29bc4SBrooks Davis for (model::paths_set::const_iterator iter = required_files.begin(); 162*b0d29bc4SBrooks Davis iter != required_files.end(); iter++) { 163*b0d29bc4SBrooks Davis INV((*iter).is_absolute()); 164*b0d29bc4SBrooks Davis if (!fs::exists(*iter)) 165*b0d29bc4SBrooks Davis return F("Required file '%s' not found") % *iter; 166*b0d29bc4SBrooks Davis } 167*b0d29bc4SBrooks Davis return ""; 168*b0d29bc4SBrooks Davis } 169*b0d29bc4SBrooks Davis 170*b0d29bc4SBrooks Davis 171*b0d29bc4SBrooks Davis /// Checks if all required programs exist. 172*b0d29bc4SBrooks Davis /// 173*b0d29bc4SBrooks Davis /// \param required_programs Set of paths. 174*b0d29bc4SBrooks Davis /// 175*b0d29bc4SBrooks Davis /// \return Empty if the required programs all exist or an error message 176*b0d29bc4SBrooks Davis /// otherwise. 177*b0d29bc4SBrooks Davis static std::string 178*b0d29bc4SBrooks Davis check_required_programs(const model::paths_set& required_programs) 179*b0d29bc4SBrooks Davis { 180*b0d29bc4SBrooks Davis for (model::paths_set::const_iterator iter = required_programs.begin(); 181*b0d29bc4SBrooks Davis iter != required_programs.end(); iter++) { 182*b0d29bc4SBrooks Davis if ((*iter).is_absolute()) { 183*b0d29bc4SBrooks Davis if (!fs::exists(*iter)) 184*b0d29bc4SBrooks Davis return F("Required program '%s' not found") % *iter; 185*b0d29bc4SBrooks Davis } else { 186*b0d29bc4SBrooks Davis if (!fs::find_in_path((*iter).c_str())) 187*b0d29bc4SBrooks Davis return F("Required program '%s' not found in PATH") % *iter; 188*b0d29bc4SBrooks Davis } 189*b0d29bc4SBrooks Davis } 190*b0d29bc4SBrooks Davis return ""; 191*b0d29bc4SBrooks Davis } 192*b0d29bc4SBrooks Davis 193*b0d29bc4SBrooks Davis 194*b0d29bc4SBrooks Davis /// Checks if the current system has the specified amount of memory. 195*b0d29bc4SBrooks Davis /// 196*b0d29bc4SBrooks Davis /// \param required_memory Amount of required physical memory, or zero if not 197*b0d29bc4SBrooks Davis /// applicable. 198*b0d29bc4SBrooks Davis /// 199*b0d29bc4SBrooks Davis /// \return Empty if the current system has the required amount of memory or an 200*b0d29bc4SBrooks Davis /// error message otherwise. 201*b0d29bc4SBrooks Davis static std::string 202*b0d29bc4SBrooks Davis check_required_memory(const units::bytes& required_memory) 203*b0d29bc4SBrooks Davis { 204*b0d29bc4SBrooks Davis if (required_memory > 0) { 205*b0d29bc4SBrooks Davis const units::bytes physical_memory = utils::physical_memory(); 206*b0d29bc4SBrooks Davis if (physical_memory > 0 && physical_memory < required_memory) 207*b0d29bc4SBrooks Davis return F("Requires %s bytes of physical memory but only %s " 208*b0d29bc4SBrooks Davis "available") % 209*b0d29bc4SBrooks Davis required_memory.format() % physical_memory.format(); 210*b0d29bc4SBrooks Davis } 211*b0d29bc4SBrooks Davis return ""; 212*b0d29bc4SBrooks Davis } 213*b0d29bc4SBrooks Davis 214*b0d29bc4SBrooks Davis 215*b0d29bc4SBrooks Davis /// Checks if the work directory's file system has enough free disk space. 216*b0d29bc4SBrooks Davis /// 217*b0d29bc4SBrooks Davis /// \param required_disk_space Amount of required free disk space, or zero if 218*b0d29bc4SBrooks Davis /// not applicable. 219*b0d29bc4SBrooks Davis /// \param work_directory Path to where the test case will be run. 220*b0d29bc4SBrooks Davis /// 221*b0d29bc4SBrooks Davis /// \return Empty if the file system where the work directory is hosted has 222*b0d29bc4SBrooks Davis /// enough free disk space or an error message otherwise. 223*b0d29bc4SBrooks Davis static std::string 224*b0d29bc4SBrooks Davis check_required_disk_space(const units::bytes& required_disk_space, 225*b0d29bc4SBrooks Davis const fs::path& work_directory) 226*b0d29bc4SBrooks Davis { 227*b0d29bc4SBrooks Davis if (required_disk_space > 0) { 228*b0d29bc4SBrooks Davis const units::bytes free_disk_space = fs::free_disk_space( 229*b0d29bc4SBrooks Davis work_directory); 230*b0d29bc4SBrooks Davis if (free_disk_space < required_disk_space) 231*b0d29bc4SBrooks Davis return F("Requires %s bytes of free disk space but only %s " 232*b0d29bc4SBrooks Davis "available") % 233*b0d29bc4SBrooks Davis required_disk_space.format() % free_disk_space.format(); 234*b0d29bc4SBrooks Davis } 235*b0d29bc4SBrooks Davis return ""; 236*b0d29bc4SBrooks Davis } 237*b0d29bc4SBrooks Davis 238*b0d29bc4SBrooks Davis 239*b0d29bc4SBrooks Davis } // anonymous namespace 240*b0d29bc4SBrooks Davis 241*b0d29bc4SBrooks Davis 242*b0d29bc4SBrooks Davis /// Checks if all the requirements specified by the test case are met. 243*b0d29bc4SBrooks Davis /// 244*b0d29bc4SBrooks Davis /// \param md The test metadata. 245*b0d29bc4SBrooks Davis /// \param cfg The engine configuration. 246*b0d29bc4SBrooks Davis /// \param test_suite Name of the test suite the test belongs to. 247*b0d29bc4SBrooks Davis /// \param work_directory Path to where the test case will be run. 248*b0d29bc4SBrooks Davis /// 249*b0d29bc4SBrooks Davis /// \return A string describing the reason for skipping the test, or empty if 250*b0d29bc4SBrooks Davis /// the test should be executed. 251*b0d29bc4SBrooks Davis std::string 252*b0d29bc4SBrooks Davis engine::check_reqs(const model::metadata& md, const config::tree& cfg, 253*b0d29bc4SBrooks Davis const std::string& test_suite, 254*b0d29bc4SBrooks Davis const fs::path& work_directory) 255*b0d29bc4SBrooks Davis { 256*b0d29bc4SBrooks Davis std::string reason; 257*b0d29bc4SBrooks Davis 258*b0d29bc4SBrooks Davis reason = check_required_configs(md.required_configs(), cfg, test_suite); 259*b0d29bc4SBrooks Davis if (!reason.empty()) 260*b0d29bc4SBrooks Davis return reason; 261*b0d29bc4SBrooks Davis 262*b0d29bc4SBrooks Davis reason = check_allowed_architectures(md.allowed_architectures(), cfg); 263*b0d29bc4SBrooks Davis if (!reason.empty()) 264*b0d29bc4SBrooks Davis return reason; 265*b0d29bc4SBrooks Davis 266*b0d29bc4SBrooks Davis reason = check_allowed_platforms(md.allowed_platforms(), cfg); 267*b0d29bc4SBrooks Davis if (!reason.empty()) 268*b0d29bc4SBrooks Davis return reason; 269*b0d29bc4SBrooks Davis 270*b0d29bc4SBrooks Davis reason = check_required_user(md.required_user(), cfg); 271*b0d29bc4SBrooks Davis if (!reason.empty()) 272*b0d29bc4SBrooks Davis return reason; 273*b0d29bc4SBrooks Davis 274*b0d29bc4SBrooks Davis reason = check_required_files(md.required_files()); 275*b0d29bc4SBrooks Davis if (!reason.empty()) 276*b0d29bc4SBrooks Davis return reason; 277*b0d29bc4SBrooks Davis 278*b0d29bc4SBrooks Davis reason = check_required_programs(md.required_programs()); 279*b0d29bc4SBrooks Davis if (!reason.empty()) 280*b0d29bc4SBrooks Davis return reason; 281*b0d29bc4SBrooks Davis 282*b0d29bc4SBrooks Davis reason = check_required_memory(md.required_memory()); 283*b0d29bc4SBrooks Davis if (!reason.empty()) 284*b0d29bc4SBrooks Davis return reason; 285*b0d29bc4SBrooks Davis 286*b0d29bc4SBrooks Davis reason = check_required_disk_space(md.required_disk_space(), 287*b0d29bc4SBrooks Davis work_directory); 288*b0d29bc4SBrooks Davis if (!reason.empty()) 289*b0d29bc4SBrooks Davis return reason; 290*b0d29bc4SBrooks Davis 291*b0d29bc4SBrooks Davis INV(reason.empty()); 292*b0d29bc4SBrooks Davis return reason; 293*b0d29bc4SBrooks Davis } 294