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