1b0d29bc4SBrooks Davis // Copyright 2010 The Kyua Authors. 2b0d29bc4SBrooks Davis // All rights reserved. 3b0d29bc4SBrooks Davis // 4b0d29bc4SBrooks Davis // Redistribution and use in source and binary forms, with or without 5b0d29bc4SBrooks Davis // modification, are permitted provided that the following conditions are 6b0d29bc4SBrooks Davis // met: 7b0d29bc4SBrooks Davis // 8b0d29bc4SBrooks Davis // * Redistributions of source code must retain the above copyright 9b0d29bc4SBrooks Davis // notice, this list of conditions and the following disclaimer. 10b0d29bc4SBrooks Davis // * Redistributions in binary form must reproduce the above copyright 11b0d29bc4SBrooks Davis // notice, this list of conditions and the following disclaimer in the 12b0d29bc4SBrooks Davis // documentation and/or other materials provided with the distribution. 13b0d29bc4SBrooks Davis // * Neither the name of Google Inc. nor the names of its contributors 14b0d29bc4SBrooks Davis // may be used to endorse or promote products derived from this software 15b0d29bc4SBrooks Davis // without specific prior written permission. 16b0d29bc4SBrooks Davis // 17b0d29bc4SBrooks Davis // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18b0d29bc4SBrooks Davis // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19b0d29bc4SBrooks Davis // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20b0d29bc4SBrooks Davis // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21b0d29bc4SBrooks Davis // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22b0d29bc4SBrooks Davis // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23b0d29bc4SBrooks Davis // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24b0d29bc4SBrooks Davis // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25b0d29bc4SBrooks Davis // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26b0d29bc4SBrooks Davis // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27b0d29bc4SBrooks Davis // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28b0d29bc4SBrooks Davis 29b0d29bc4SBrooks Davis #include "engine/config.hpp" 30b0d29bc4SBrooks Davis 31b0d29bc4SBrooks Davis #if defined(HAVE_CONFIG_H) 32b0d29bc4SBrooks Davis # include "config.h" 33b0d29bc4SBrooks Davis #endif 34b0d29bc4SBrooks Davis 35b0d29bc4SBrooks Davis #include <stdexcept> 36b0d29bc4SBrooks Davis 37b0d29bc4SBrooks Davis #include "engine/exceptions.hpp" 38*257e70f1SIgor Ostapenko #include "engine/execenv/execenv.hpp" 39b0d29bc4SBrooks Davis #include "utils/config/exceptions.hpp" 40b0d29bc4SBrooks Davis #include "utils/config/parser.hpp" 41b0d29bc4SBrooks Davis #include "utils/config/tree.ipp" 42b0d29bc4SBrooks Davis #include "utils/passwd.hpp" 43b0d29bc4SBrooks Davis #include "utils/text/exceptions.hpp" 44b0d29bc4SBrooks Davis #include "utils/text/operations.ipp" 45b0d29bc4SBrooks Davis 46b0d29bc4SBrooks Davis namespace config = utils::config; 47*257e70f1SIgor Ostapenko namespace execenv = engine::execenv; 48b0d29bc4SBrooks Davis namespace fs = utils::fs; 49b0d29bc4SBrooks Davis namespace passwd = utils::passwd; 50b0d29bc4SBrooks Davis namespace text = utils::text; 51b0d29bc4SBrooks Davis 52b0d29bc4SBrooks Davis 53b0d29bc4SBrooks Davis namespace { 54b0d29bc4SBrooks Davis 55b0d29bc4SBrooks Davis 56b0d29bc4SBrooks Davis /// Defines the schema of a configuration tree. 57b0d29bc4SBrooks Davis /// 58b0d29bc4SBrooks Davis /// \param [in,out] tree The tree to populate. The tree should be empty on 59b0d29bc4SBrooks Davis /// entry to prevent collisions with the keys defined in here. 60b0d29bc4SBrooks Davis static void 61b0d29bc4SBrooks Davis init_tree(config::tree& tree) 62b0d29bc4SBrooks Davis { 63b0d29bc4SBrooks Davis tree.define< config::string_node >("architecture"); 64*257e70f1SIgor Ostapenko tree.define< config::strings_set_node >("execenvs"); 65b0d29bc4SBrooks Davis tree.define< config::positive_int_node >("parallelism"); 66b0d29bc4SBrooks Davis tree.define< config::string_node >("platform"); 67b0d29bc4SBrooks Davis tree.define< engine::user_node >("unprivileged_user"); 68b0d29bc4SBrooks Davis tree.define_dynamic("test_suites"); 69b0d29bc4SBrooks Davis } 70b0d29bc4SBrooks Davis 71b0d29bc4SBrooks Davis 72b0d29bc4SBrooks Davis /// Fills in a configuration tree with default values. 73b0d29bc4SBrooks Davis /// 74b0d29bc4SBrooks Davis /// \param [in,out] tree The tree to populate. init_tree() must have been 75b0d29bc4SBrooks Davis /// called on it beforehand. 76b0d29bc4SBrooks Davis static void 77b0d29bc4SBrooks Davis set_defaults(config::tree& tree) 78b0d29bc4SBrooks Davis { 79b0d29bc4SBrooks Davis tree.set< config::string_node >("architecture", KYUA_ARCHITECTURE); 80*257e70f1SIgor Ostapenko 81*257e70f1SIgor Ostapenko std::set< std::string > supported; 82*257e70f1SIgor Ostapenko for (auto em : execenv::execenvs()) 83*257e70f1SIgor Ostapenko if (em->is_supported()) 84*257e70f1SIgor Ostapenko supported.insert(em->name()); 85*257e70f1SIgor Ostapenko supported.insert(execenv::default_execenv_name); 86*257e70f1SIgor Ostapenko tree.set< config::strings_set_node >("execenvs", supported); 87*257e70f1SIgor Ostapenko 88b0d29bc4SBrooks Davis // TODO(jmmv): Automatically derive this from the number of CPUs in the 89b0d29bc4SBrooks Davis // machine and forcibly set to a value greater than 1. Still testing 90b0d29bc4SBrooks Davis // the new parallel implementation as of 2015-02-27 though. 91b0d29bc4SBrooks Davis tree.set< config::positive_int_node >("parallelism", 1); 92b0d29bc4SBrooks Davis tree.set< config::string_node >("platform", KYUA_PLATFORM); 93b0d29bc4SBrooks Davis } 94b0d29bc4SBrooks Davis 95b0d29bc4SBrooks Davis 96b0d29bc4SBrooks Davis /// Configuration parser specialization for Kyua configuration files. 97b0d29bc4SBrooks Davis class config_parser : public config::parser { 98b0d29bc4SBrooks Davis /// Initializes the configuration tree. 99b0d29bc4SBrooks Davis /// 100b0d29bc4SBrooks Davis /// This is a callback executed when the configuration script invokes the 101b0d29bc4SBrooks Davis /// syntax() method. We populate the configuration tree from here with the 102b0d29bc4SBrooks Davis /// schema version requested by the file. 103b0d29bc4SBrooks Davis /// 104b0d29bc4SBrooks Davis /// \param [in,out] tree The tree to populate. 105b0d29bc4SBrooks Davis /// \param syntax_version The version of the file format as specified in the 106b0d29bc4SBrooks Davis /// configuration file. 107b0d29bc4SBrooks Davis /// 108b0d29bc4SBrooks Davis /// \throw config::syntax_error If the syntax_format/syntax_version 109b0d29bc4SBrooks Davis /// combination is not supported. 110b0d29bc4SBrooks Davis void 111b0d29bc4SBrooks Davis setup(config::tree& tree, const int syntax_version) 112b0d29bc4SBrooks Davis { 113b0d29bc4SBrooks Davis if (syntax_version < 1 || syntax_version > 2) 114b0d29bc4SBrooks Davis throw config::syntax_error(F("Unsupported config version %s") % 115b0d29bc4SBrooks Davis syntax_version); 116b0d29bc4SBrooks Davis 117b0d29bc4SBrooks Davis init_tree(tree); 118b0d29bc4SBrooks Davis set_defaults(tree); 119b0d29bc4SBrooks Davis } 120b0d29bc4SBrooks Davis 121b0d29bc4SBrooks Davis public: 122b0d29bc4SBrooks Davis /// Initializes the parser. 123b0d29bc4SBrooks Davis /// 124b0d29bc4SBrooks Davis /// \param [out] tree_ The tree in which the results of the parsing will be 125b0d29bc4SBrooks Davis /// stored when parse() is called. Should be empty on entry. Because 126b0d29bc4SBrooks Davis /// we grab a reference to this object, the tree must remain valid for 127b0d29bc4SBrooks Davis /// the existence of the parser object. 128b0d29bc4SBrooks Davis explicit config_parser(config::tree& tree_) : 129b0d29bc4SBrooks Davis config::parser(tree_) 130b0d29bc4SBrooks Davis { 131b0d29bc4SBrooks Davis } 132b0d29bc4SBrooks Davis }; 133b0d29bc4SBrooks Davis 134b0d29bc4SBrooks Davis 135b0d29bc4SBrooks Davis } // anonymous namespace 136b0d29bc4SBrooks Davis 137b0d29bc4SBrooks Davis 138b0d29bc4SBrooks Davis /// Copies the node. 139b0d29bc4SBrooks Davis /// 140b0d29bc4SBrooks Davis /// \return A dynamically-allocated node. 141b0d29bc4SBrooks Davis config::detail::base_node* 142b0d29bc4SBrooks Davis engine::user_node::deep_copy(void) const 143b0d29bc4SBrooks Davis { 144b0d29bc4SBrooks Davis std::auto_ptr< user_node > new_node(new user_node()); 145b0d29bc4SBrooks Davis new_node->_value = _value; 146b0d29bc4SBrooks Davis return new_node.release(); 147b0d29bc4SBrooks Davis } 148b0d29bc4SBrooks Davis 149b0d29bc4SBrooks Davis 150b0d29bc4SBrooks Davis /// Pushes the node's value onto the Lua stack. 151b0d29bc4SBrooks Davis /// 152b0d29bc4SBrooks Davis /// \param state The Lua state onto which to push the value. 153b0d29bc4SBrooks Davis void 154b0d29bc4SBrooks Davis engine::user_node::push_lua(lutok::state& state) const 155b0d29bc4SBrooks Davis { 156b0d29bc4SBrooks Davis state.push_string(value().name); 157b0d29bc4SBrooks Davis } 158b0d29bc4SBrooks Davis 159b0d29bc4SBrooks Davis 160b0d29bc4SBrooks Davis /// Sets the value of the node from an entry in the Lua stack. 161b0d29bc4SBrooks Davis /// 162b0d29bc4SBrooks Davis /// \param state The Lua state from which to get the value. 163b0d29bc4SBrooks Davis /// \param value_index The stack index in which the value resides. 164b0d29bc4SBrooks Davis /// 165b0d29bc4SBrooks Davis /// \throw value_error If the value in state(value_index) cannot be 166b0d29bc4SBrooks Davis /// processed by this node. 167b0d29bc4SBrooks Davis void 168b0d29bc4SBrooks Davis engine::user_node::set_lua(lutok::state& state, const int value_index) 169b0d29bc4SBrooks Davis { 170b0d29bc4SBrooks Davis if (state.is_number(value_index)) { 171b0d29bc4SBrooks Davis config::typed_leaf_node< passwd::user >::set( 172b0d29bc4SBrooks Davis passwd::find_user_by_uid(state.to_integer(-1))); 173b0d29bc4SBrooks Davis } else if (state.is_string(value_index)) { 174b0d29bc4SBrooks Davis config::typed_leaf_node< passwd::user >::set( 175b0d29bc4SBrooks Davis passwd::find_user_by_name(state.to_string(-1))); 176b0d29bc4SBrooks Davis } else 177b0d29bc4SBrooks Davis throw config::value_error("Invalid user identifier"); 178b0d29bc4SBrooks Davis } 179b0d29bc4SBrooks Davis 180b0d29bc4SBrooks Davis 181b0d29bc4SBrooks Davis /// Sets the value of the node from a raw string representation. 182b0d29bc4SBrooks Davis /// 183b0d29bc4SBrooks Davis /// \param raw_value The value to set the node to. 184b0d29bc4SBrooks Davis /// 185b0d29bc4SBrooks Davis /// \throw value_error If the value is invalid. 186b0d29bc4SBrooks Davis void 187b0d29bc4SBrooks Davis engine::user_node::set_string(const std::string& raw_value) 188b0d29bc4SBrooks Davis { 189b0d29bc4SBrooks Davis try { 190b0d29bc4SBrooks Davis config::typed_leaf_node< passwd::user >::set( 191b0d29bc4SBrooks Davis passwd::find_user_by_name(raw_value)); 192b0d29bc4SBrooks Davis } catch (const std::runtime_error& e) { 193b0d29bc4SBrooks Davis int uid; 194b0d29bc4SBrooks Davis try { 195b0d29bc4SBrooks Davis uid = text::to_type< int >(raw_value); 196b0d29bc4SBrooks Davis } catch (const text::value_error& e2) { 197b0d29bc4SBrooks Davis throw error(F("Cannot find user with name '%s'") % raw_value); 198b0d29bc4SBrooks Davis } 199b0d29bc4SBrooks Davis 200b0d29bc4SBrooks Davis try { 201b0d29bc4SBrooks Davis config::typed_leaf_node< passwd::user >::set( 202b0d29bc4SBrooks Davis passwd::find_user_by_uid(uid)); 203b0d29bc4SBrooks Davis } catch (const std::runtime_error& e2) { 204b0d29bc4SBrooks Davis throw error(F("Cannot find user with UID %s") % uid); 205b0d29bc4SBrooks Davis } 206b0d29bc4SBrooks Davis } 207b0d29bc4SBrooks Davis } 208b0d29bc4SBrooks Davis 209b0d29bc4SBrooks Davis 210b0d29bc4SBrooks Davis /// Converts the contents of the node to a string. 211b0d29bc4SBrooks Davis /// 212b0d29bc4SBrooks Davis /// \pre The node must have a value. 213b0d29bc4SBrooks Davis /// 214b0d29bc4SBrooks Davis /// \return A string representation of the value held by the node. 215b0d29bc4SBrooks Davis std::string 216b0d29bc4SBrooks Davis engine::user_node::to_string(void) const 217b0d29bc4SBrooks Davis { 218b0d29bc4SBrooks Davis return config::typed_leaf_node< passwd::user >::value().name; 219b0d29bc4SBrooks Davis } 220b0d29bc4SBrooks Davis 221b0d29bc4SBrooks Davis 222b0d29bc4SBrooks Davis /// Constructs a config with the built-in settings. 223b0d29bc4SBrooks Davis /// 224b0d29bc4SBrooks Davis /// \return A default test suite configuration. 225b0d29bc4SBrooks Davis config::tree 226b0d29bc4SBrooks Davis engine::default_config(void) 227b0d29bc4SBrooks Davis { 228b0d29bc4SBrooks Davis config::tree tree(false); 229b0d29bc4SBrooks Davis init_tree(tree); 230b0d29bc4SBrooks Davis set_defaults(tree); 231b0d29bc4SBrooks Davis return tree; 232b0d29bc4SBrooks Davis } 233b0d29bc4SBrooks Davis 234b0d29bc4SBrooks Davis 235b0d29bc4SBrooks Davis /// Constructs a config with the built-in settings. 236b0d29bc4SBrooks Davis /// 237b0d29bc4SBrooks Davis /// \return An empty test suite configuration. 238b0d29bc4SBrooks Davis config::tree 239b0d29bc4SBrooks Davis engine::empty_config(void) 240b0d29bc4SBrooks Davis { 241b0d29bc4SBrooks Davis config::tree tree(false); 242b0d29bc4SBrooks Davis init_tree(tree); 243*257e70f1SIgor Ostapenko 244*257e70f1SIgor Ostapenko // Tests of Kyua itself tend to use an empty config, i.e. default 245*257e70f1SIgor Ostapenko // execution environment is used. Let's allow it. 246*257e70f1SIgor Ostapenko std::set< std::string > supported; 247*257e70f1SIgor Ostapenko supported.insert(engine::execenv::default_execenv_name); 248*257e70f1SIgor Ostapenko tree.set< config::strings_set_node >("execenvs", supported); 249*257e70f1SIgor Ostapenko 250b0d29bc4SBrooks Davis return tree; 251b0d29bc4SBrooks Davis } 252b0d29bc4SBrooks Davis 253b0d29bc4SBrooks Davis 254b0d29bc4SBrooks Davis /// Parses a test suite configuration file. 255b0d29bc4SBrooks Davis /// 256b0d29bc4SBrooks Davis /// \param file The file to parse. 257b0d29bc4SBrooks Davis /// 258b0d29bc4SBrooks Davis /// \return High-level representation of the configuration file. 259b0d29bc4SBrooks Davis /// 260b0d29bc4SBrooks Davis /// \throw load_error If there is any problem loading the file. This includes 261b0d29bc4SBrooks Davis /// file access errors and syntax errors. 262b0d29bc4SBrooks Davis config::tree 263b0d29bc4SBrooks Davis engine::load_config(const utils::fs::path& file) 264b0d29bc4SBrooks Davis { 265b0d29bc4SBrooks Davis config::tree tree(false); 266b0d29bc4SBrooks Davis try { 267b0d29bc4SBrooks Davis config_parser(tree).parse(file); 268b0d29bc4SBrooks Davis } catch (const config::error& e) { 269b0d29bc4SBrooks Davis throw load_error(file, e.what()); 270b0d29bc4SBrooks Davis } 271b0d29bc4SBrooks Davis return tree; 272b0d29bc4SBrooks Davis } 273