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 "utils/config/exceptions.hpp" 39 #include "utils/config/parser.hpp" 40 #include "utils/config/tree.ipp" 41 #include "utils/passwd.hpp" 42 #include "utils/text/exceptions.hpp" 43 #include "utils/text/operations.ipp" 44 45 namespace config = utils::config; 46 namespace fs = utils::fs; 47 namespace passwd = utils::passwd; 48 namespace text = utils::text; 49 50 51 namespace { 52 53 54 /// Defines the schema of a configuration tree. 55 /// 56 /// \param [in,out] tree The tree to populate. The tree should be empty on 57 /// entry to prevent collisions with the keys defined in here. 58 static void 59 init_tree(config::tree& tree) 60 { 61 tree.define< config::string_node >("architecture"); 62 tree.define< config::positive_int_node >("parallelism"); 63 tree.define< config::string_node >("platform"); 64 tree.define< engine::user_node >("unprivileged_user"); 65 tree.define_dynamic("test_suites"); 66 } 67 68 69 /// Fills in a configuration tree with default values. 70 /// 71 /// \param [in,out] tree The tree to populate. init_tree() must have been 72 /// called on it beforehand. 73 static void 74 set_defaults(config::tree& tree) 75 { 76 tree.set< config::string_node >("architecture", KYUA_ARCHITECTURE); 77 // TODO(jmmv): Automatically derive this from the number of CPUs in the 78 // machine and forcibly set to a value greater than 1. Still testing 79 // the new parallel implementation as of 2015-02-27 though. 80 tree.set< config::positive_int_node >("parallelism", 1); 81 tree.set< config::string_node >("platform", KYUA_PLATFORM); 82 } 83 84 85 /// Configuration parser specialization for Kyua configuration files. 86 class config_parser : public config::parser { 87 /// Initializes the configuration tree. 88 /// 89 /// This is a callback executed when the configuration script invokes the 90 /// syntax() method. We populate the configuration tree from here with the 91 /// schema version requested by the file. 92 /// 93 /// \param [in,out] tree The tree to populate. 94 /// \param syntax_version The version of the file format as specified in the 95 /// configuration file. 96 /// 97 /// \throw config::syntax_error If the syntax_format/syntax_version 98 /// combination is not supported. 99 void 100 setup(config::tree& tree, const int syntax_version) 101 { 102 if (syntax_version < 1 || syntax_version > 2) 103 throw config::syntax_error(F("Unsupported config version %s") % 104 syntax_version); 105 106 init_tree(tree); 107 set_defaults(tree); 108 } 109 110 public: 111 /// Initializes the parser. 112 /// 113 /// \param [out] tree_ The tree in which the results of the parsing will be 114 /// stored when parse() is called. Should be empty on entry. Because 115 /// we grab a reference to this object, the tree must remain valid for 116 /// the existence of the parser object. 117 explicit config_parser(config::tree& tree_) : 118 config::parser(tree_) 119 { 120 } 121 }; 122 123 124 } // anonymous namespace 125 126 127 /// Copies the node. 128 /// 129 /// \return A dynamically-allocated node. 130 config::detail::base_node* 131 engine::user_node::deep_copy(void) const 132 { 133 std::auto_ptr< user_node > new_node(new user_node()); 134 new_node->_value = _value; 135 return new_node.release(); 136 } 137 138 139 /// Pushes the node's value onto the Lua stack. 140 /// 141 /// \param state The Lua state onto which to push the value. 142 void 143 engine::user_node::push_lua(lutok::state& state) const 144 { 145 state.push_string(value().name); 146 } 147 148 149 /// Sets the value of the node from an entry in the Lua stack. 150 /// 151 /// \param state The Lua state from which to get the value. 152 /// \param value_index The stack index in which the value resides. 153 /// 154 /// \throw value_error If the value in state(value_index) cannot be 155 /// processed by this node. 156 void 157 engine::user_node::set_lua(lutok::state& state, const int value_index) 158 { 159 if (state.is_number(value_index)) { 160 config::typed_leaf_node< passwd::user >::set( 161 passwd::find_user_by_uid(state.to_integer(-1))); 162 } else if (state.is_string(value_index)) { 163 config::typed_leaf_node< passwd::user >::set( 164 passwd::find_user_by_name(state.to_string(-1))); 165 } else 166 throw config::value_error("Invalid user identifier"); 167 } 168 169 170 /// Sets the value of the node from a raw string representation. 171 /// 172 /// \param raw_value The value to set the node to. 173 /// 174 /// \throw value_error If the value is invalid. 175 void 176 engine::user_node::set_string(const std::string& raw_value) 177 { 178 try { 179 config::typed_leaf_node< passwd::user >::set( 180 passwd::find_user_by_name(raw_value)); 181 } catch (const std::runtime_error& e) { 182 int uid; 183 try { 184 uid = text::to_type< int >(raw_value); 185 } catch (const text::value_error& e2) { 186 throw error(F("Cannot find user with name '%s'") % raw_value); 187 } 188 189 try { 190 config::typed_leaf_node< passwd::user >::set( 191 passwd::find_user_by_uid(uid)); 192 } catch (const std::runtime_error& e2) { 193 throw error(F("Cannot find user with UID %s") % uid); 194 } 195 } 196 } 197 198 199 /// Converts the contents of the node to a string. 200 /// 201 /// \pre The node must have a value. 202 /// 203 /// \return A string representation of the value held by the node. 204 std::string 205 engine::user_node::to_string(void) const 206 { 207 return config::typed_leaf_node< passwd::user >::value().name; 208 } 209 210 211 /// Constructs a config with the built-in settings. 212 /// 213 /// \return A default test suite configuration. 214 config::tree 215 engine::default_config(void) 216 { 217 config::tree tree(false); 218 init_tree(tree); 219 set_defaults(tree); 220 return tree; 221 } 222 223 224 /// Constructs a config with the built-in settings. 225 /// 226 /// \return An empty test suite configuration. 227 config::tree 228 engine::empty_config(void) 229 { 230 config::tree tree(false); 231 init_tree(tree); 232 return tree; 233 } 234 235 236 /// Parses a test suite configuration file. 237 /// 238 /// \param file The file to parse. 239 /// 240 /// \return High-level representation of the configuration file. 241 /// 242 /// \throw load_error If there is any problem loading the file. This includes 243 /// file access errors and syntax errors. 244 config::tree 245 engine::load_config(const utils::fs::path& file) 246 { 247 config::tree tree(false); 248 try { 249 config_parser(tree).parse(file); 250 } catch (const config::error& e) { 251 throw load_error(file, e.what()); 252 } 253 return tree; 254 } 255