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
init_tree(config::tree & tree)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
set_defaults(config::tree & tree)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
setup(config::tree & tree,const int syntax_version)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.
config_parser(config::tree & tree_)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*
deep_copy(void) const131 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
push_lua(lutok::state & state) const143 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
set_lua(lutok::state & state,const int value_index)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
set_string(const std::string & raw_value)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
to_string(void) const205 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
default_config(void)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
empty_config(void)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
load_config(const utils::fs::path & file)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