diff --git a/contrib/kyua/os/freebsd/main.cpp b/contrib/kyua/cli/cmd_prepare.hpp copy from contrib/kyua/os/freebsd/main.cpp copy to contrib/kyua/cli/cmd_prepare.hpp --- a/contrib/kyua/os/freebsd/main.cpp +++ b/contrib/kyua/cli/cmd_prepare.hpp @@ -26,40 +26,28 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#include "os/freebsd/main.hpp" +/// \file cli/cmd_prepare.hpp +/// Provides the cmd_prepare class. -#include "engine/execenv/execenv.hpp" -#include "os/freebsd/execenv_jail_manager.hpp" +#if !defined(CLI_CMD_PREPARE_HPP) +#define CLI_CMD_PREPARE_HPP -#include "engine/requirements.hpp" -#include "os/freebsd/reqs_checker_kmods.hpp" +#include "cli/common.hpp" -namespace execenv = engine::execenv; +namespace cli { -/// FreeBSD related features initialization. -/// -/// \param argc The number of arguments passed on the command line. -/// \param argv NULL-terminated array containing the command line arguments. -/// -/// \return 0 on success, some other integer on error. -/// -/// \throw std::exception This throws any uncaught exception. Such exceptions -/// are bugs, but we let them propagate so that the runtime will abort and -/// dump core. -int -freebsd::main(const int, const char* const* const) + +/// Implementation of the "prepare" subcommand. +class cmd_prepare : public cli_command { - execenv::register_execenv( - std::shared_ptr< execenv::manager >(new freebsd::execenv_jail_manager()) - ); +public: + cmd_prepare(void); + + int run(utils::cmdline::ui*, const utils::cmdline::parsed_cmdline&, + const utils::config::tree&); +}; + -#ifdef __FreeBSD__ - engine::register_reqs_checker( - std::shared_ptr< engine::reqs_checker >( - new freebsd::reqs_checker_kmods() - ) - ); -#endif +} // namespace cli - return 0; -} +#endif // !defined(CLI_CMD_PREPARE_HPP) diff --git a/contrib/kyua/os/freebsd/main.cpp b/contrib/kyua/cli/cmd_prepare.cpp copy from contrib/kyua/os/freebsd/main.cpp copy to contrib/kyua/cli/cmd_prepare.cpp --- a/contrib/kyua/os/freebsd/main.cpp +++ b/contrib/kyua/cli/cmd_prepare.cpp @@ -26,40 +26,51 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#include "os/freebsd/main.hpp" +#include "cli/cmd_prepare.hpp" -#include "engine/execenv/execenv.hpp" -#include "os/freebsd/execenv_jail_manager.hpp" +#include "cli/common.ipp" +#include "engine/prepare/prepare.hpp" +#include "utils/cmdline/options.hpp" -#include "engine/requirements.hpp" -#include "os/freebsd/reqs_checker_kmods.hpp" +namespace cmdline = utils::cmdline; +namespace config = utils::config; +namespace prepare = engine::prepare; -namespace execenv = engine::execenv; +using cli::cmd_prepare; -/// FreeBSD related features initialization. -/// -/// \param argc The number of arguments passed on the command line. -/// \param argv NULL-terminated array containing the command line arguments. + +/// Default constructor for cmd_prepare. +cmd_prepare::cmd_prepare(void) : cli_command( + "prepare", "[handler-name ...]", 0, -1, + "Prepares environment and declared requirements before testing") +{ + add_option(kyuafile_option); + add_option(build_root_option); + add_option(cmdline::bool_option('n', "dry-run", "Do not alter the system")); +} + + +/// Entry point for the "prepare" subcommand. /// -/// \return 0 on success, some other integer on error. +/// \param ui Object to interact with the I/O of the program. +/// \param cmdline Representation of the command line to the subcommand. +/// \param user_config The runtime configuration of the program. /// -/// \throw std::exception This throws any uncaught exception. Such exceptions -/// are bugs, but we let them propagate so that the runtime will abort and -/// dump core. +/// \return 0 if successful, 1 otherwise. int -freebsd::main(const int, const char* const* const) +cmd_prepare::run(cmdline::ui* ui, const cmdline::parsed_cmdline& cmdline, + const config::tree& user_config) { - execenv::register_execenv( - std::shared_ptr< execenv::manager >(new freebsd::execenv_jail_manager()) - ); - -#ifdef __FreeBSD__ - engine::register_reqs_checker( - std::shared_ptr< engine::reqs_checker >( - new freebsd::reqs_checker_kmods() - ) - ); -#endif + // List available preparation handlers + if (cmdline.arguments().empty()) { + for (auto& h : prepare::handlers()) { + ui->out(h->name(), false); + ui->out("\t\t\t", false); + ui->out(h->description()); + } + return EXIT_SUCCESS; + } - return 0; + // Or run the given ones + return prepare::run(cmdline.arguments(), ui, cmdline, user_config); } diff --git a/contrib/kyua/cli/main.cpp b/contrib/kyua/cli/main.cpp --- a/contrib/kyua/cli/main.cpp +++ b/contrib/kyua/cli/main.cpp @@ -49,6 +49,7 @@ #include "cli/cmd_debug.hpp" #include "cli/cmd_help.hpp" #include "cli/cmd_list.hpp" +#include "cli/cmd_prepare.hpp" #include "cli/cmd_report.hpp" #include "cli/cmd_report_html.hpp" #include "cli/cmd_report_junit.hpp" @@ -191,6 +192,7 @@ commands.insert(new cli::cmd_debug(), "Workspace"); commands.insert(new cli::cmd_list(), "Workspace"); + commands.insert(new cli::cmd_prepare(), "Workspace"); commands.insert(new cli::cmd_test(), "Workspace"); commands.insert(new cli::cmd_report(), "Reporting"); diff --git a/contrib/kyua/doc/kyuafile.5.in b/contrib/kyua/doc/kyuafile.5.in --- a/contrib/kyua/doc/kyuafile.5.in +++ b/contrib/kyua/doc/kyuafile.5.in @@ -300,6 +300,7 @@ .Pp ATF: .Va require.kmods +(appends to the list declared at the Kyuafile level instead of overwriting it) .It Va required_memory Amount of physical memory that the test needs to run successfully. .Pp diff --git a/contrib/kyua/engine/prepare/prepare.hpp b/contrib/kyua/engine/prepare/prepare.hpp new file mode 100644 --- /dev/null +++ b/contrib/kyua/engine/prepare/prepare.hpp @@ -0,0 +1,107 @@ +// Copyright 2024 The Kyua Authors. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of Google Inc. nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +/// \file engine/prepare/prepare.hpp +/// Requirement preparation subsystem interface. + +#if !defined(ENGINE_PREPARE_HPP) +#define ENGINE_PREPARE_HPP + +#include +#include + +#include "utils/cmdline/parser.ipp" +#include "utils/cmdline/ui.hpp" +#include "utils/config/tree.ipp" + +namespace cmdline = utils::cmdline; +namespace config = utils::config; + + +namespace engine { +namespace prepare { + + +/// Abstract interface of a requirement preparation handler. +class handler { +public: + /// Constructor. + handler() {} + + /// Destructor. + virtual ~handler() {} + + /// Returns name of the handler. + virtual const std::string& name() const = 0; + + /// Returns short description of the handler. + virtual const std::string& description() const = 0; + + /// Runs the requirement preparation handler. + /// + /// \param ui Object to interact with the I/O of the program. + /// \param cmdline Representation of the command line to the subcommand. + /// \param user_config The runtime configuration of the program. + /// + /// \return 0 to indicate success. + virtual int exec(cmdline::ui* ui, + const cmdline::parsed_cmdline& cmdline, + const config::tree& user_config) const = 0; +}; + + +/// Registers a requirement preparation handler. +/// +/// \param handler A requirement preparation handler. +void register_handler(const std::shared_ptr< handler > handler); + + +/// Returns the list of registered requirement preparation handlers. +/// +/// \return A vector of pointers to requirement preparation handlers. +const std::vector< std::shared_ptr< handler > > handlers(); + + +/// Run named handlers. +/// +/// \param handler_names Names of the handlers to run. +/// \param ui Object to interact with the I/O of the program. +/// \param cmdline Representation of the command line to the subcommand. +/// \param user_config The runtime configuration of the program. +/// +/// \return 0 to indicate success. +int run(const std::vector< std::string >& handler_names, + cmdline::ui* ui, + const cmdline::parsed_cmdline& cmdline, + const config::tree& user_config); + + +} // namespace prepare +} // namespace engine + +#endif // !defined(ENGINE_PREPARE_HPP) diff --git a/contrib/kyua/os/freebsd/main.cpp b/contrib/kyua/engine/prepare/prepare.cpp copy from contrib/kyua/os/freebsd/main.cpp copy to contrib/kyua/engine/prepare/prepare.cpp --- a/contrib/kyua/os/freebsd/main.cpp +++ b/contrib/kyua/engine/prepare/prepare.cpp @@ -26,40 +26,58 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#include "os/freebsd/main.hpp" +#include "engine/prepare/prepare.hpp" -#include "engine/execenv/execenv.hpp" -#include "os/freebsd/execenv_jail_manager.hpp" +#include "engine/prepare/prepare_all.hpp" -#include "engine/requirements.hpp" -#include "os/freebsd/reqs_checker_kmods.hpp" +namespace prepare = engine::prepare; -namespace execenv = engine::execenv; -/// FreeBSD related features initialization. +/// List of registered requirement preparation handlers. /// -/// \param argc The number of arguments passed on the command line. -/// \param argv NULL-terminated array containing the command line arguments. -/// -/// \return 0 on success, some other integer on error. -/// -/// \throw std::exception This throws any uncaught exception. Such exceptions -/// are bugs, but we let them propagate so that the runtime will abort and -/// dump core. +/// Use register_handler() to add an entry to this global list. +static std::vector< std::shared_ptr< prepare::handler > > _handlers = { + std::shared_ptr< prepare::handler >(new prepare::prepare_all()) +}; + + +void +prepare::register_handler(const std::shared_ptr< handler > handler) +{ + _handlers.push_back(handler); +} + + +const std::vector< std::shared_ptr< prepare::handler > > +prepare::handlers() +{ + return _handlers; +} + + int -freebsd::main(const int, const char* const* const) +prepare::run(const std::vector< std::string >& handler_names, + cmdline::ui* ui, + const cmdline::parsed_cmdline& cmdline, + const config::tree& user_config) { - execenv::register_execenv( - std::shared_ptr< execenv::manager >(new freebsd::execenv_jail_manager()) - ); + for (auto& hname : handler_names) { + std::shared_ptr< prepare::handler > handler = nullptr; + for (auto& h : prepare::handlers()) + if (h->name() == hname) { + handler = h; + break; + } + + if (handler == nullptr) { + ui->out(F("Unknown requirement preparation handler: %s") % hname); + return EXIT_FAILURE; + } -#ifdef __FreeBSD__ - engine::register_reqs_checker( - std::shared_ptr< engine::reqs_checker >( - new freebsd::reqs_checker_kmods() - ) - ); -#endif + if (handler->exec(ui, cmdline, user_config) != EXIT_SUCCESS) + // suppress the actual code -- main limits possible exit codes + return EXIT_FAILURE; + } - return 0; + return EXIT_SUCCESS; } diff --git a/contrib/kyua/os/freebsd/main.cpp b/contrib/kyua/engine/prepare/prepare_all.hpp copy from contrib/kyua/os/freebsd/main.cpp copy to contrib/kyua/engine/prepare/prepare_all.hpp --- a/contrib/kyua/os/freebsd/main.cpp +++ b/contrib/kyua/engine/prepare/prepare_all.hpp @@ -26,40 +26,28 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#include "os/freebsd/main.hpp" +/// \file engine/prepare/prepare_all.hpp +/// The requirement preparation handler which runs all registered handlers. -#include "engine/execenv/execenv.hpp" -#include "os/freebsd/execenv_jail_manager.hpp" +#if !defined(ENGINE_PREPARE_ALL_HPP) +#define ENGINE_PREPARE_ALL_HPP -#include "engine/requirements.hpp" -#include "os/freebsd/reqs_checker_kmods.hpp" +#include "engine/prepare/prepare.hpp" -namespace execenv = engine::execenv; +namespace engine { +namespace prepare { -/// FreeBSD related features initialization. -/// -/// \param argc The number of arguments passed on the command line. -/// \param argv NULL-terminated array containing the command line arguments. -/// -/// \return 0 on success, some other integer on error. -/// -/// \throw std::exception This throws any uncaught exception. Such exceptions -/// are bugs, but we let them propagate so that the runtime will abort and -/// dump core. -int -freebsd::main(const int, const char* const* const) -{ - execenv::register_execenv( - std::shared_ptr< execenv::manager >(new freebsd::execenv_jail_manager()) - ); -#ifdef __FreeBSD__ - engine::register_reqs_checker( - std::shared_ptr< engine::reqs_checker >( - new freebsd::reqs_checker_kmods() - ) - ); -#endif +class prepare_all : public prepare::handler { +public: + const std::string& name() const; + const std::string& description() const; + int exec(cmdline::ui*, const cmdline::parsed_cmdline&, + const config::tree&) const; +}; - return 0; -} + +} // namespace prepare +} // namespace engine + +#endif // !defined(ENGINE_PREPARE_ALL_HPP) diff --git a/contrib/kyua/os/freebsd/main.cpp b/contrib/kyua/engine/prepare/prepare_all.cpp copy from contrib/kyua/os/freebsd/main.cpp copy to contrib/kyua/engine/prepare/prepare_all.cpp --- a/contrib/kyua/os/freebsd/main.cpp +++ b/contrib/kyua/engine/prepare/prepare_all.cpp @@ -26,40 +26,41 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#include "os/freebsd/main.hpp" +#include "engine/prepare/prepare_all.hpp" -#include "engine/execenv/execenv.hpp" -#include "os/freebsd/execenv_jail_manager.hpp" +static const std::string _name = "all"; +static const std::string _description = "Run all preparations"; -#include "engine/requirements.hpp" -#include "os/freebsd/reqs_checker_kmods.hpp" +namespace prepare = engine::prepare; + + +const std::string& +prepare::prepare_all::name() const +{ + return _name; +} + + +const std::string& +prepare::prepare_all::description() const +{ + return _description; +} -namespace execenv = engine::execenv; -/// FreeBSD related features initialization. -/// -/// \param argc The number of arguments passed on the command line. -/// \param argv NULL-terminated array containing the command line arguments. -/// -/// \return 0 on success, some other integer on error. -/// -/// \throw std::exception This throws any uncaught exception. Such exceptions -/// are bugs, but we let them propagate so that the runtime will abort and -/// dump core. int -freebsd::main(const int, const char* const* const) +prepare::prepare_all::exec(cmdline::ui* ui, + const cmdline::parsed_cmdline& cmdline, + const config::tree& user_config) const { - execenv::register_execenv( - std::shared_ptr< execenv::manager >(new freebsd::execenv_jail_manager()) - ); + for (auto& handler : prepare::handlers()) { + if (handler->name() == this->name()) + continue; -#ifdef __FreeBSD__ - engine::register_reqs_checker( - std::shared_ptr< engine::reqs_checker >( - new freebsd::reqs_checker_kmods() - ) - ); -#endif + int error = handler->exec(ui, cmdline, user_config); + if (error != EXIT_SUCCESS) + return error; + } - return 0; + return EXIT_SUCCESS; } diff --git a/contrib/kyua/model/metadata.hpp b/contrib/kyua/model/metadata.hpp --- a/contrib/kyua/model/metadata.hpp +++ b/contrib/kyua/model/metadata.hpp @@ -75,8 +75,8 @@ const strings_set& required_configs(void) const; const utils::units::bytes& required_disk_space(void) const; const paths_set& required_files(void) const; - const utils::units::bytes& required_memory(void) const; const strings_set& required_kmods(void) const; + const utils::units::bytes& required_memory(void) const; const paths_set& required_programs(void) const; const std::string& required_user(void) const; const utils::datetime::delta& timeout(void) const; @@ -121,8 +121,8 @@ metadata_builder& set_required_configs(const strings_set&); metadata_builder& set_required_disk_space(const utils::units::bytes&); metadata_builder& set_required_files(const paths_set&); - metadata_builder& set_required_memory(const utils::units::bytes&); metadata_builder& set_required_kmods(const strings_set&); + metadata_builder& set_required_memory(const utils::units::bytes&); metadata_builder& set_required_programs(const paths_set&); metadata_builder& set_required_user(const std::string&); metadata_builder& set_string(const std::string&, const std::string&); diff --git a/contrib/kyua/model/metadata.cpp b/contrib/kyua/model/metadata.cpp --- a/contrib/kyua/model/metadata.cpp +++ b/contrib/kyua/model/metadata.cpp @@ -255,8 +255,8 @@ tree.define< config::strings_set_node >("required_configs"); tree.define< bytes_node >("required_disk_space"); tree.define< paths_set_node >("required_files"); - tree.define< bytes_node >("required_memory"); tree.define< config::strings_set_node >("required_kmods"); + tree.define< bytes_node >("required_memory"); tree.define< paths_set_node >("required_programs"); tree.define< user_node >("required_user"); tree.define< delta_node >("timeout"); @@ -282,8 +282,8 @@ model::strings_set()); tree.set< bytes_node >("required_disk_space", units::bytes(0)); tree.set< paths_set_node >("required_files", model::paths_set()); - tree.set< bytes_node >("required_memory", units::bytes(0)); tree.set< config::strings_set_node >("required_kmods", model::strings_set()); + tree.set< bytes_node >("required_memory", units::bytes(0)); tree.set< paths_set_node >("required_programs", model::paths_set()); tree.set< user_node >("required_user", ""); // TODO(jmmv): We shouldn't be setting a default timeout like this. See @@ -585,20 +585,6 @@ } -/// Returns the amount of memory required by the test. -/// -/// \return Number of bytes, or 0 if this does not apply. -const units::bytes& -model::metadata::required_memory(void) const -{ - if (_pimpl->props.is_set("required_memory")) { - return _pimpl->props.lookup< bytes_node >("required_memory"); - } else { - return get_defaults().lookup< bytes_node >("required_memory"); - } -} - - /// Returns the list of kernel modules needed by the test. /// /// \return Set of kernel module names. @@ -615,6 +601,20 @@ } +/// Returns the amount of memory required by the test. +/// +/// \return Number of bytes, or 0 if this does not apply. +const units::bytes& +model::metadata::required_memory(void) const +{ + if (_pimpl->props.is_set("required_memory")) { + return _pimpl->props.lookup< bytes_node >("required_memory"); + } else { + return get_defaults().lookup< bytes_node >("required_memory"); + } +} + + /// Returns the list of programs needed by the test. /// /// \return Set of paths. @@ -1059,6 +1059,21 @@ } +/// Sets the list of required kernel modules. +/// +/// \param vars Set of kernel module names. +/// +/// \return A reference to this builder. +/// +/// \throw model::error If the value is invalid. +model::metadata_builder& +model::metadata_builder::set_required_kmods(const model::strings_set& kmods) +{ + set< config::strings_set_node >(_pimpl->props, "required_kmods", kmods); + return *this; +} + + /// Sets the amount of memory required by the test. /// /// \param bytes Number of bytes. diff --git a/contrib/kyua/os/freebsd/main.cpp b/contrib/kyua/os/freebsd/main.cpp --- a/contrib/kyua/os/freebsd/main.cpp +++ b/contrib/kyua/os/freebsd/main.cpp @@ -34,7 +34,12 @@ #include "engine/requirements.hpp" #include "os/freebsd/reqs_checker_kmods.hpp" +#include "engine/prepare/prepare.hpp" +#include "os/freebsd/prepare_kmods.hpp" + namespace execenv = engine::execenv; +namespace prepare = engine::prepare; + /// FreeBSD related features initialization. /// @@ -59,6 +64,9 @@ new freebsd::reqs_checker_kmods() ) ); + + prepare::register_handler( + std::shared_ptr< prepare::handler >(new freebsd::prepare_kmods())); #endif return 0; diff --git a/contrib/kyua/os/freebsd/main.cpp b/contrib/kyua/os/freebsd/prepare_kmods.hpp copy from contrib/kyua/os/freebsd/main.cpp copy to contrib/kyua/os/freebsd/prepare_kmods.hpp --- a/contrib/kyua/os/freebsd/main.cpp +++ b/contrib/kyua/os/freebsd/prepare_kmods.hpp @@ -26,40 +26,30 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#include "os/freebsd/main.hpp" +/// \file os/freebsd/prepare_kmods.hpp +/// The preparation handler which loads FreeBSD modules declared via +/// "required_kmods" metadata. -#include "engine/execenv/execenv.hpp" -#include "os/freebsd/execenv_jail_manager.hpp" +#if !defined(FREEBSD_PREPARE_KMODS_HPP) +#define FREEBSD_PREPARE_KMODS_HPP -#include "engine/requirements.hpp" -#include "os/freebsd/reqs_checker_kmods.hpp" +#include "engine/prepare/prepare.hpp" -namespace execenv = engine::execenv; +namespace prepare = engine::prepare; -/// FreeBSD related features initialization. -/// -/// \param argc The number of arguments passed on the command line. -/// \param argv NULL-terminated array containing the command line arguments. -/// -/// \return 0 on success, some other integer on error. -/// -/// \throw std::exception This throws any uncaught exception. Such exceptions -/// are bugs, but we let them propagate so that the runtime will abort and -/// dump core. -int -freebsd::main(const int, const char* const* const) -{ - execenv::register_execenv( - std::shared_ptr< execenv::manager >(new freebsd::execenv_jail_manager()) - ); -#ifdef __FreeBSD__ - engine::register_reqs_checker( - std::shared_ptr< engine::reqs_checker >( - new freebsd::reqs_checker_kmods() - ) - ); -#endif +namespace freebsd { - return 0; -} + +class prepare_kmods : public prepare::handler { +public: + const std::string& name() const; + const std::string& description() const; + int exec(cmdline::ui*, const cmdline::parsed_cmdline&, + const config::tree&) const; +}; + + +} // namespace freebsd + +#endif // !defined(FREEBSD_PREPARE_KMODS_HPP) diff --git a/contrib/kyua/os/freebsd/prepare_kmods.cpp b/contrib/kyua/os/freebsd/prepare_kmods.cpp new file mode 100644 --- /dev/null +++ b/contrib/kyua/os/freebsd/prepare_kmods.cpp @@ -0,0 +1,140 @@ +// Copyright 2024 The Kyua Authors. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of Google Inc. nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "os/freebsd/prepare_kmods.hpp" + +#include "cli/common.hpp" +#include "drivers/list_tests.hpp" +#include "engine/filters.hpp" +#include "model/metadata.hpp" +#include "model/test_case.hpp" +#include "model/test_program.hpp" + +// FreeBSD kldload syscall +extern "C" { +#include +#include +} + + +namespace { + + +static const std::string _name = "kmods"; +static const std::string _description = "FreeBSD: load modules specified " + "in the required_kmods metadata"; + + +/// Hooks for list_tests to examine test cases as they come. +class list_hooks : public drivers::list_tests::base_hooks { + /// Collected names of required kernel modules. + std::set< std::string >& _modules; + +public: + /// Initializes the hooks. + /// + /// \param modules_ The set of modules to fill. + list_hooks(std::set< std::string >& modules_) : + _modules(modules_) + { + } + + /// Examine a test case as soon as it is found. + /// + /// \param test_program The test program containing the test case. + /// \param test_case_name The name of the located test case. + void + got_test_case(const model::test_program& test_program, + const std::string& test_case_name) + { + auto test_case = test_program.find(test_case_name); + auto kmods = test_case.get_metadata().required_kmods(); + _modules.insert(kmods.begin(), kmods.end()); + } +}; + + +} // anonymous namespace + + +namespace freebsd { + + +const std::string& +prepare_kmods::name() const +{ + return _name; +} + + +const std::string& +prepare_kmods::description() const +{ + return _description; +} + + +int +prepare_kmods::exec(cmdline::ui* ui, const cmdline::parsed_cmdline& cmdline, + const config::tree& user_config) const +{ + // Collect required modules + std::set< std::string > modules; + list_hooks hooks(modules); + const std::set< engine::test_filter > nofilters; + const drivers::list_tests::result result = drivers::list_tests::drive( + cli::kyuafile_path(cmdline), cli::build_root_path(cmdline), + nofilters, user_config, hooks); + + // Nothing to do + if (modules.empty()) + return EXIT_SUCCESS; + + // Announce the work + ui->out("kldload", false); + for (auto& m : modules) + ui->out(F(" %s") % m, false); + ui->out(""); + + if (cmdline.has_option("dry-run")) + return EXIT_SUCCESS; + + // Load the modules + for (auto& m : modules) { + if (::kldload(m.c_str()) != -1 || errno == EEXIST) + continue; + ui->err(F("kldload %s: ") % m, false); + ui->err(strerror(errno)); + return errno; + } + + return EXIT_SUCCESS; +} + + +} // namespace freebsd diff --git a/usr.bin/kyua/Makefile b/usr.bin/kyua/Makefile --- a/usr.bin/kyua/Makefile +++ b/usr.bin/kyua/Makefile @@ -126,11 +126,14 @@ engine/tap_parser.cpp \ engine/scheduler.cpp \ engine/execenv/execenv.cpp \ - engine/execenv/execenv_host.cpp + engine/execenv/execenv_host.cpp \ + engine/prepare/prepare.cpp \ + engine/prepare/prepare_all.cpp SRCS+= os/freebsd/execenv_jail_manager.cpp \ os/freebsd/main.cpp \ - os/freebsd/reqs_checker_kmods.cpp + os/freebsd/reqs_checker_kmods.cpp \ + os/freebsd/prepare_kmods.cpp SRCS+= store/dbtypes.cpp \ store/exceptions.cpp \ @@ -155,6 +158,7 @@ cli/cmd_debug.cpp \ cli/cmd_help.cpp \ cli/cmd_list.cpp \ + cli/cmd_prepare.cpp \ cli/cmd_report.cpp \ cli/cmd_report_html.cpp \ cli/cmd_report_junit.cpp \