xref: /freebsd/contrib/kyua/engine/config.cpp (revision b392a90ba4e5ea07d8a88a834fd102191d1967bf)
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"
38257e70f1SIgor 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;
47257e70f1SIgor 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
init_tree(config::tree & tree)61b0d29bc4SBrooks Davis init_tree(config::tree& tree)
62b0d29bc4SBrooks Davis {
63b0d29bc4SBrooks Davis     tree.define< config::string_node >("architecture");
64257e70f1SIgor 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
set_defaults(config::tree & tree)77b0d29bc4SBrooks Davis set_defaults(config::tree& tree)
78b0d29bc4SBrooks Davis {
79b0d29bc4SBrooks Davis     tree.set< config::string_node >("architecture", KYUA_ARCHITECTURE);
80257e70f1SIgor Ostapenko 
81257e70f1SIgor Ostapenko     std::set< std::string > supported;
82257e70f1SIgor Ostapenko     for (auto em : execenv::execenvs())
83257e70f1SIgor Ostapenko         if (em->is_supported())
84257e70f1SIgor Ostapenko             supported.insert(em->name());
85257e70f1SIgor Ostapenko     supported.insert(execenv::default_execenv_name);
86257e70f1SIgor Ostapenko     tree.set< config::strings_set_node >("execenvs", supported);
87257e70f1SIgor 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
setup(config::tree & tree,const int syntax_version)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.
config_parser(config::tree & tree_)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*
deep_copy(void) const142b0d29bc4SBrooks Davis engine::user_node::deep_copy(void) const
143b0d29bc4SBrooks Davis {
144*b392a90bSJohn Baldwin     std::unique_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
push_lua(lutok::state & state) const154b0d29bc4SBrooks 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
set_lua(lutok::state & state,const int value_index)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
set_string(const std::string & raw_value)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
to_string(void) const216b0d29bc4SBrooks 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
default_config(void)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
empty_config(void)239b0d29bc4SBrooks Davis engine::empty_config(void)
240b0d29bc4SBrooks Davis {
241b0d29bc4SBrooks Davis     config::tree tree(false);
242b0d29bc4SBrooks Davis     init_tree(tree);
243257e70f1SIgor Ostapenko 
244257e70f1SIgor Ostapenko     // Tests of Kyua itself tend to use an empty config, i.e. default
245257e70f1SIgor Ostapenko     // execution environment is used. Let's allow it.
246257e70f1SIgor Ostapenko     std::set< std::string > supported;
247257e70f1SIgor Ostapenko     supported.insert(engine::execenv::default_execenv_name);
248257e70f1SIgor Ostapenko     tree.set< config::strings_set_node >("execenvs", supported);
249257e70f1SIgor 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
load_config(const utils::fs::path & file)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