mirror of
https://github.com/freebsd/freebsd-src
synced 2024-09-30 21:54:50 +00:00
kyua: Add FreeBSD Jail execution environment support
A new Kyua concept is added -- "execution environment". A test can be configured to be run within a specific environment. The test case lifecycle is extended respectively: - execenv init (creates a jail or does nothing for default execenv="host") - test exec - cleanup exec (optional) - execenv cleanup (removes a jail or does nothing for default execenv="host") The following new functionality is provided, from bottom to top: 1 ATF based tests - The new "execenv" metadata property can be set to explicitly ask for an execution environment: "host" or "jail". If it's not defined, as all existing tests do, then it implicitly means "host". - The new "execenv.jail.params" metadata property can be optionally defined to ask Kyua to use specific jail(8) parameters during creation of a temporary jail. An example is "vnet allow.raw_sockets". Kyua implicitly adds "children.max" to "execenv_jail_params" parameters with the maximum possible value. A test case can override it. 2 Kyuafile - The same new metadata properties can be defined on Kyuafile level: "execenv" and "execenv_jail_params". - Note that historically ATF uses dotted style of metadata naming, while Kyua uses underscore style. Hence "execenv.jail.params" vs. "execenv_jail_params". 3 kyua.conf, kyua CLI - The new "execenvs" engine configuration variable can be set to a list of execution environments to run only tests designed for. Tests of not listed environments are skipped. - By default, this variable lists all execution environments supported by a Kyua binary, e.g. execenvs="host jail". - This variable can be changed via "kyua.conf" or via kyua CLI's "-v" parameter. For example, "kyua -v execenvs=host test" will run only host-based tests and skip jail-based ones. - Current value of this variable can be examined with "kyua config". [markj] This feature has not landed upstream yet. See the discussion in https://github.com/freebsd/kyua/pull/224 . Having the ability to automatically jail tests allows many network tests to run in parallel, giving a drastic speedup. So, let's import the feature and start using it in main. Signed-off-by: Igor Ostapenko <pm@igoro.pro> Reviewed by: markj, kp Tested by: markj, kp MFC after: 3 months Differential Revision: https://reviews.freebsd.org/D45865
This commit is contained in:
parent
75e1fea68a
commit
257e70f1d5
|
@ -10,3 +10,4 @@
|
|||
|
||||
* The FreeBSD Foundation
|
||||
* Google Inc.
|
||||
* Igor Ostapenko <pm@igoro.pro>
|
||||
|
|
|
@ -61,6 +61,7 @@ fake_config(void)
|
|||
{
|
||||
config::tree user_config = engine::default_config();
|
||||
user_config.set_string("architecture", "the-architecture");
|
||||
user_config.set_string("execenvs", "the-env");
|
||||
user_config.set_string("parallelism", "128");
|
||||
user_config.set_string("platform", "the-platform");
|
||||
//user_config.set_string("unprivileged_user", "");
|
||||
|
@ -83,12 +84,13 @@ ATF_TEST_CASE_BODY(all)
|
|||
cmdline::ui_mock ui;
|
||||
ATF_REQUIRE_EQ(EXIT_SUCCESS, cmd.main(&ui, args, fake_config()));
|
||||
|
||||
ATF_REQUIRE_EQ(5, ui.out_log().size());
|
||||
ATF_REQUIRE_EQ(6, ui.out_log().size());
|
||||
ATF_REQUIRE_EQ("architecture = the-architecture", ui.out_log()[0]);
|
||||
ATF_REQUIRE_EQ("parallelism = 128", ui.out_log()[1]);
|
||||
ATF_REQUIRE_EQ("platform = the-platform", ui.out_log()[2]);
|
||||
ATF_REQUIRE_EQ("test_suites.foo.bar = first", ui.out_log()[3]);
|
||||
ATF_REQUIRE_EQ("test_suites.foo.baz = second", ui.out_log()[4]);
|
||||
ATF_REQUIRE_EQ("execenvs = the-env", ui.out_log()[1]);
|
||||
ATF_REQUIRE_EQ("parallelism = 128", ui.out_log()[2]);
|
||||
ATF_REQUIRE_EQ("platform = the-platform", ui.out_log()[3]);
|
||||
ATF_REQUIRE_EQ("test_suites.foo.bar = first", ui.out_log()[4]);
|
||||
ATF_REQUIRE_EQ("test_suites.foo.baz = second", ui.out_log()[5]);
|
||||
ATF_REQUIRE(ui.err_log().empty());
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
.\" Copyright 2012 The Kyua Authors.
|
||||
.\" Copyright 2012-2024 The Kyua Authors.
|
||||
.\" All rights reserved.
|
||||
.\"
|
||||
.\" Redistribution and use in source and binary forms, with or without
|
||||
|
@ -25,7 +25,7 @@
|
|||
.\" 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.
|
||||
.Dd February 20, 2015
|
||||
.Dd March 22, 2024
|
||||
.Dt KYUA.CONF 5
|
||||
.Os
|
||||
.Sh NAME
|
||||
|
@ -36,6 +36,7 @@
|
|||
.Pp
|
||||
Variables:
|
||||
.Va architecture ,
|
||||
.Va execenvs ,
|
||||
.Va platform ,
|
||||
.Va test_suites ,
|
||||
.Va unprivileged_user .
|
||||
|
@ -72,6 +73,14 @@ The following variables are internally recognized by
|
|||
.Bl -tag -width XX -offset indent
|
||||
.It Va architecture
|
||||
Name of the system architecture (aka processor type).
|
||||
.It Va execenvs
|
||||
Whitespace-separated list of execution environment names.
|
||||
.Pp
|
||||
Only tests which require one of the given execution environments will be run.
|
||||
.Pp
|
||||
See
|
||||
.Xr kyuafile 5
|
||||
for the list of possible execution environments.
|
||||
.It Va parallelism
|
||||
Maximum number of test cases to execute concurrently.
|
||||
.It Va platform
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
.\" Copyright 2012 The Kyua Authors.
|
||||
.\" Copyright 2012-2024 The Kyua Authors.
|
||||
.\" All rights reserved.
|
||||
.\"
|
||||
.\" Redistribution and use in source and binary forms, with or without
|
||||
|
@ -25,7 +25,7 @@
|
|||
.\" 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.
|
||||
.Dd July 3, 2015
|
||||
.Dd March 23, 2024
|
||||
.Dt KYUAFILE 5
|
||||
.Os
|
||||
.Sh NAME
|
||||
|
@ -173,6 +173,75 @@ Refer to the
|
|||
section below for clarification.
|
||||
.It Va description
|
||||
Textual description of the test.
|
||||
.It Va execenv
|
||||
The name of the execution environment to be used for running the test.
|
||||
If empty or not defined, the
|
||||
.Sq host
|
||||
execution environment is meant.
|
||||
The possible values are:
|
||||
.Bl -tag -width xUnnnnnnn
|
||||
.It host
|
||||
The default environment which runs the test as a usual child process.
|
||||
.It jail
|
||||
The
|
||||
.Fx
|
||||
.Xr jail 8
|
||||
environment.
|
||||
It creates a temporary jail to run the test and its optional cleanup logic
|
||||
within.
|
||||
.Pp
|
||||
This feature requires
|
||||
.Xr kyua 1
|
||||
to be running with superuser privileges.
|
||||
.Pp
|
||||
The difference between
|
||||
.Va security.jail.children.max
|
||||
and
|
||||
.Va security.jail.children.cur
|
||||
sysctl of the jail
|
||||
.Xr kyua 1
|
||||
is running within must have a value high enough for the jail based tests
|
||||
planned to be run.
|
||||
For instance, the value 1 should be enough for a sequential run of simple
|
||||
tests.
|
||||
Otherwise, such aspects as parallel test execution and sub-jails spawned
|
||||
by specific test cases should be considered.
|
||||
.Pp
|
||||
The formula of a temporary jail name is
|
||||
.Sq kyua
|
||||
+
|
||||
.Va test program path
|
||||
+
|
||||
.Sq _
|
||||
+
|
||||
.Va test case name .
|
||||
All non-alphanumeric characters are replaced with
|
||||
.Sq _ .
|
||||
.Sq kyua_usr_tests_sys_netpfil_pf_pass_block_v4
|
||||
is an example for /usr/tests/sys/netpfil/pf/pass_block:v4 test case.
|
||||
.El
|
||||
.It Va execenv_jail_params
|
||||
Additional test-specific whitespace-separated parameters of
|
||||
.Fx
|
||||
.Xr jail 8
|
||||
to create a temporary jail within which the test is run.
|
||||
It makes sense only if execenv is set to
|
||||
.Sq jail .
|
||||
.sp
|
||||
.Xr kyua 1
|
||||
implicitly passes
|
||||
.Sq children.max
|
||||
parameter to
|
||||
.Xr jail 8
|
||||
for a temporary jail with the maximum possible value according to
|
||||
the jail
|
||||
.Xr kyua 1
|
||||
itself is running within.
|
||||
It allows tests to easily spawn their own sub-jails without additional
|
||||
configuration.
|
||||
It can be overridden via
|
||||
.Va execenv_jail_params
|
||||
if needed.
|
||||
.It Va is_exclusive
|
||||
If true, indicates that this test program cannot be executed along any other
|
||||
programs at the same time.
|
||||
|
@ -360,6 +429,36 @@ test_suite('FreeBSD')
|
|||
plain_test_program{name='the_test',
|
||||
['custom.FreeBSD-Bug-Id']='category/12345'}
|
||||
.Ed
|
||||
.Ss FreeBSD jail execution environment
|
||||
The following example configures the test to be run within a temporary jail
|
||||
with
|
||||
.Xr vnet 9
|
||||
support and the permission to create raw sockets:
|
||||
.Bd -literal -offset indent
|
||||
syntax(2)
|
||||
|
||||
test_suite('FreeBSD')
|
||||
|
||||
atf_test_program{name='network_test',
|
||||
execenv='jail',
|
||||
execenv_jail_params='vnet allow.raw_sockets',
|
||||
required_user='root'}
|
||||
.Ed
|
||||
.Pp
|
||||
A test case itself may have no requirements in superuser privileges,
|
||||
but required_user='root' metadata property reminds that the jail execution
|
||||
environment requires
|
||||
.Xr kyua 1
|
||||
being running with root privileges, and the test is skipped otherwise with
|
||||
the respective message. The combination of
|
||||
.Va execenv
|
||||
set to
|
||||
.Sq jail
|
||||
and
|
||||
.Va required_user
|
||||
set to
|
||||
.Sq unprivileged
|
||||
does not work respectively.
|
||||
.Ss Connecting disjoint test suites
|
||||
Now suppose you had various test suites on your file system and you would
|
||||
like to connect them together so that they could be executed and treated as
|
||||
|
|
|
@ -63,6 +63,8 @@ static const char* const default_metadata =
|
|||
"allowed_architectures is empty\n"
|
||||
"allowed_platforms is empty\n"
|
||||
"description is empty\n"
|
||||
"execenv is empty\n"
|
||||
"execenv_jail_params is empty\n"
|
||||
"has_cleanup = false\n"
|
||||
"is_exclusive = false\n"
|
||||
"required_configs is empty\n"
|
||||
|
@ -80,6 +82,8 @@ static const char* const overriden_metadata =
|
|||
"allowed_architectures is empty\n"
|
||||
"allowed_platforms is empty\n"
|
||||
"description = Textual description\n"
|
||||
"execenv is empty\n"
|
||||
"execenv_jail_params is empty\n"
|
||||
"has_cleanup = false\n"
|
||||
"is_exclusive = false\n"
|
||||
"required_configs is empty\n"
|
||||
|
@ -199,6 +203,8 @@ ATF_TEST_CASE_BODY(junit_metadata__overrides)
|
|||
.add_allowed_architecture("arch1")
|
||||
.add_allowed_platform("platform1")
|
||||
.set_description("This is a test")
|
||||
.set_execenv("jail")
|
||||
.set_execenv_jail_params("vnet")
|
||||
.set_has_cleanup(true)
|
||||
.set_is_exclusive(true)
|
||||
.add_required_config("config1")
|
||||
|
@ -215,6 +221,8 @@ ATF_TEST_CASE_BODY(junit_metadata__overrides)
|
|||
+ "allowed_architectures = arch1\n"
|
||||
+ "allowed_platforms = platform1\n"
|
||||
+ "description = This is a test\n"
|
||||
+ "execenv = jail\n"
|
||||
+ "execenv_jail_params = vnet\n"
|
||||
+ "has_cleanup = true\n"
|
||||
+ "is_exclusive = true\n"
|
||||
+ "required_configs = config1\n"
|
||||
|
|
|
@ -39,6 +39,7 @@ extern "C" {
|
|||
#include "engine/atf_list.hpp"
|
||||
#include "engine/atf_result.hpp"
|
||||
#include "engine/exceptions.hpp"
|
||||
#include "engine/execenv/execenv.hpp"
|
||||
#include "model/test_case.hpp"
|
||||
#include "model/test_program.hpp"
|
||||
#include "model/test_result.hpp"
|
||||
|
@ -54,6 +55,7 @@ extern "C" {
|
|||
#include "utils/stream.hpp"
|
||||
|
||||
namespace config = utils::config;
|
||||
namespace execenv = engine::execenv;
|
||||
namespace fs = utils::fs;
|
||||
namespace process = utils::process;
|
||||
|
||||
|
@ -190,7 +192,10 @@ engine::atf_interface::exec_test(const model::test_program& test_program,
|
|||
|
||||
args.push_back(F("-r%s") % (control_directory / result_name));
|
||||
args.push_back(test_case_name);
|
||||
process::exec(test_program.absolute_path(), args);
|
||||
|
||||
auto e = execenv::get(test_program, test_case_name);
|
||||
e->init();
|
||||
e->exec(args);
|
||||
}
|
||||
|
||||
|
||||
|
@ -219,7 +224,9 @@ engine::atf_interface::exec_cleanup(
|
|||
}
|
||||
|
||||
args.push_back(F("%s:cleanup") % test_case_name);
|
||||
process::exec(test_program.absolute_path(), args);
|
||||
|
||||
auto e = execenv::get(test_program, test_case_name);
|
||||
e->exec(args);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -121,6 +121,10 @@ engine::parse_atf_metadata(const model::properties_map& props)
|
|||
mdbuilder.set_string("has_cleanup", value);
|
||||
} else if (name == "require.arch") {
|
||||
mdbuilder.set_string("allowed_architectures", value);
|
||||
} else if (name == "execenv") {
|
||||
mdbuilder.set_string("execenv", value);
|
||||
} else if (name == "execenv.jail.params") {
|
||||
mdbuilder.set_string("execenv_jail_params", value);
|
||||
} else if (name == "require.config") {
|
||||
mdbuilder.set_string("required_configs", value);
|
||||
} else if (name == "require.files") {
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include <stdexcept>
|
||||
|
||||
#include "engine/exceptions.hpp"
|
||||
#include "engine/execenv/execenv.hpp"
|
||||
#include "utils/config/exceptions.hpp"
|
||||
#include "utils/config/parser.hpp"
|
||||
#include "utils/config/tree.ipp"
|
||||
|
@ -43,6 +44,7 @@
|
|||
#include "utils/text/operations.ipp"
|
||||
|
||||
namespace config = utils::config;
|
||||
namespace execenv = engine::execenv;
|
||||
namespace fs = utils::fs;
|
||||
namespace passwd = utils::passwd;
|
||||
namespace text = utils::text;
|
||||
|
@ -59,6 +61,7 @@ static void
|
|||
init_tree(config::tree& tree)
|
||||
{
|
||||
tree.define< config::string_node >("architecture");
|
||||
tree.define< config::strings_set_node >("execenvs");
|
||||
tree.define< config::positive_int_node >("parallelism");
|
||||
tree.define< config::string_node >("platform");
|
||||
tree.define< engine::user_node >("unprivileged_user");
|
||||
|
@ -74,6 +77,14 @@ static void
|
|||
set_defaults(config::tree& tree)
|
||||
{
|
||||
tree.set< config::string_node >("architecture", KYUA_ARCHITECTURE);
|
||||
|
||||
std::set< std::string > supported;
|
||||
for (auto em : execenv::execenvs())
|
||||
if (em->is_supported())
|
||||
supported.insert(em->name());
|
||||
supported.insert(execenv::default_execenv_name);
|
||||
tree.set< config::strings_set_node >("execenvs", supported);
|
||||
|
||||
// TODO(jmmv): Automatically derive this from the number of CPUs in the
|
||||
// machine and forcibly set to a value greater than 1. Still testing
|
||||
// the new parallel implementation as of 2015-02-27 though.
|
||||
|
@ -229,6 +240,13 @@ engine::empty_config(void)
|
|||
{
|
||||
config::tree tree(false);
|
||||
init_tree(tree);
|
||||
|
||||
// Tests of Kyua itself tend to use an empty config, i.e. default
|
||||
// execution environment is used. Let's allow it.
|
||||
std::set< std::string > supported;
|
||||
supported.insert(engine::execenv::default_execenv_name);
|
||||
tree.set< config::strings_set_node >("execenvs", supported);
|
||||
|
||||
return tree;
|
||||
}
|
||||
|
||||
|
|
74
contrib/kyua/engine/execenv/execenv.cpp
Normal file
74
contrib/kyua/engine/execenv/execenv.cpp
Normal file
|
@ -0,0 +1,74 @@
|
|||
// Copyright 2023 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 "engine/execenv/execenv.hpp"
|
||||
|
||||
#include "engine/execenv/execenv_host.hpp"
|
||||
|
||||
namespace execenv = engine::execenv;
|
||||
|
||||
using utils::none;
|
||||
|
||||
|
||||
const char* execenv::default_execenv_name = "host";
|
||||
|
||||
|
||||
/// List of registered execution environments, except default host one.
|
||||
///
|
||||
/// Use register_execenv() to add an entry to this global list.
|
||||
static std::vector< std::shared_ptr< execenv::manager > >
|
||||
execenv_managers;
|
||||
|
||||
|
||||
void
|
||||
execenv::register_execenv(const std::shared_ptr< execenv::manager > manager)
|
||||
{
|
||||
execenv_managers.push_back(manager);
|
||||
}
|
||||
|
||||
|
||||
const std::vector< std::shared_ptr< execenv::manager> >
|
||||
execenv::execenvs()
|
||||
{
|
||||
return execenv_managers;
|
||||
}
|
||||
|
||||
|
||||
std::unique_ptr< execenv::interface >
|
||||
execenv::get(const model::test_program& test_program,
|
||||
const std::string& test_case_name)
|
||||
{
|
||||
for (auto m : execenv_managers) {
|
||||
auto e = m->probe(test_program, test_case_name);
|
||||
if (e != nullptr)
|
||||
return e;
|
||||
}
|
||||
|
||||
return std::unique_ptr< execenv::interface >(
|
||||
new execenv::execenv_host(test_program, test_case_name));
|
||||
}
|
149
contrib/kyua/engine/execenv/execenv.hpp
Normal file
149
contrib/kyua/engine/execenv/execenv.hpp
Normal file
|
@ -0,0 +1,149 @@
|
|||
// Copyright 2023 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/execenv/execenv.hpp
|
||||
/// Execution environment subsystem interface.
|
||||
|
||||
#if !defined(ENGINE_EXECENV_EXECENV_HPP)
|
||||
#define ENGINE_EXECENV_EXECENV_HPP
|
||||
|
||||
#include "model/test_program.hpp"
|
||||
#include "utils/optional.ipp"
|
||||
#include "utils/process/operations_fwd.hpp"
|
||||
|
||||
using utils::process::args_vector;
|
||||
using utils::optional;
|
||||
|
||||
namespace engine {
|
||||
namespace execenv {
|
||||
|
||||
|
||||
extern const char* default_execenv_name;
|
||||
|
||||
|
||||
/// Abstract interface of an execution environment.
|
||||
class interface {
|
||||
protected:
|
||||
const model::test_program& _test_program;
|
||||
const std::string& _test_case_name;
|
||||
|
||||
public:
|
||||
/// Constructor.
|
||||
///
|
||||
/// \param program The test program.
|
||||
/// \param test_case_name Name of the test case.
|
||||
interface(const model::test_program& test_program,
|
||||
const std::string& test_case_name) :
|
||||
_test_program(test_program),
|
||||
_test_case_name(test_case_name)
|
||||
{}
|
||||
|
||||
/// Destructor.
|
||||
virtual ~interface() {}
|
||||
|
||||
/// Initializes execution environment.
|
||||
///
|
||||
/// It's expected to be called inside a fork which runs
|
||||
/// scheduler::interface::exec_test(), so we can fail a test fast if its
|
||||
/// execution environment setup fails, and test execution could use the
|
||||
/// configured proc environment, if expected.
|
||||
virtual void init() const = 0;
|
||||
|
||||
/// Cleanups or removes execution environment.
|
||||
///
|
||||
/// It's expected to be called inside a fork for execenv cleanup.
|
||||
virtual void cleanup() const = 0;
|
||||
|
||||
/// Executes a test within the execution environment.
|
||||
///
|
||||
/// It's expected to be called inside a fork which runs
|
||||
/// scheduler::interface::exec_test() or exec_cleanup().
|
||||
///
|
||||
/// \param args The arguments to pass to the binary.
|
||||
virtual void exec(const args_vector& args) const UTILS_NORETURN = 0;
|
||||
};
|
||||
|
||||
|
||||
/// Abstract interface of an execution environment manager.
|
||||
class manager {
|
||||
public:
|
||||
/// Destructor.
|
||||
virtual ~manager() {}
|
||||
|
||||
/// Returns name of an execution environment.
|
||||
virtual const std::string& name() const = 0;
|
||||
|
||||
/// Returns whether this execution environment is actually supported.
|
||||
///
|
||||
/// It can be compile time and/or runtime check.
|
||||
virtual bool is_supported() const = 0;
|
||||
|
||||
/// Returns execution environment for a test.
|
||||
///
|
||||
/// It checks if the given test is designed for this execution environment.
|
||||
///
|
||||
/// \param program The test program.
|
||||
/// \param test_case_name Name of the test case.
|
||||
///
|
||||
/// \return An execenv object if the test conforms, or none.
|
||||
virtual std::unique_ptr< interface > probe(
|
||||
const model::test_program& test_program,
|
||||
const std::string& test_case_name) const = 0;
|
||||
|
||||
// TODO: execenv related extra metadata could be provided by a manager
|
||||
// not to know how exactly and where it should be added to the kyua
|
||||
};
|
||||
|
||||
|
||||
/// Registers an execution environment.
|
||||
///
|
||||
/// \param manager Execution environment manager.
|
||||
void register_execenv(const std::shared_ptr< manager > manager);
|
||||
|
||||
|
||||
/// Returns list of registered execenv managers, except default host one.
|
||||
///
|
||||
/// \return A vector of pointers to execenv managers.
|
||||
const std::vector< std::shared_ptr< manager> > execenvs();
|
||||
|
||||
|
||||
/// Returns execution environment for a test case.
|
||||
///
|
||||
/// \param program The test program.
|
||||
/// \param test_case_name Name of the test case.
|
||||
///
|
||||
/// \return An execution environment of a test.
|
||||
std::unique_ptr< execenv::interface > get(
|
||||
const model::test_program& test_program,
|
||||
const std::string& test_case_name);
|
||||
|
||||
|
||||
} // namespace execenv
|
||||
} // namespace engine
|
||||
|
||||
#endif // !defined(ENGINE_EXECENV_EXECENV_HPP)
|
52
contrib/kyua/engine/execenv/execenv_host.cpp
Normal file
52
contrib/kyua/engine/execenv/execenv_host.cpp
Normal file
|
@ -0,0 +1,52 @@
|
|||
// 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 "engine/execenv/execenv_host.hpp"
|
||||
|
||||
#include "utils/fs/path.hpp"
|
||||
#include "utils/process/operations.hpp"
|
||||
|
||||
void
|
||||
execenv::execenv_host::init() const
|
||||
{
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
execenv::execenv_host::cleanup() const
|
||||
{
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
execenv::execenv_host::exec(const args_vector& args) const
|
||||
{
|
||||
utils::process::exec(_test_program.absolute_path(), args);
|
||||
}
|
63
contrib/kyua/engine/execenv/execenv_host.hpp
Normal file
63
contrib/kyua/engine/execenv/execenv_host.hpp
Normal file
|
@ -0,0 +1,63 @@
|
|||
// 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/execenv/execenv_host.hpp
|
||||
/// Default execution environment.
|
||||
|
||||
#if !defined(ENGINE_EXECENV_EXECENV_HOST_HPP)
|
||||
#define ENGINE_EXECENV_EXECENV_HOST_HPP
|
||||
|
||||
#include "engine/execenv/execenv.hpp"
|
||||
|
||||
#include "utils/process/operations_fwd.hpp"
|
||||
|
||||
namespace execenv = engine::execenv;
|
||||
|
||||
using utils::process::args_vector;
|
||||
|
||||
namespace engine {
|
||||
namespace execenv {
|
||||
|
||||
|
||||
class execenv_host : public execenv::interface {
|
||||
public:
|
||||
execenv_host(const model::test_program& test_program,
|
||||
const std::string& test_case_name) :
|
||||
execenv::interface(test_program, test_case_name)
|
||||
{}
|
||||
|
||||
void init() const;
|
||||
void cleanup() const;
|
||||
void exec(const args_vector& args) const UTILS_NORETURN;
|
||||
};
|
||||
|
||||
|
||||
} // namespace execenv
|
||||
} // namespace engine
|
||||
|
||||
#endif // !defined(ENGINE_EXECENV_EXECENV_HOST_HPP)
|
|
@ -34,6 +34,7 @@ extern "C" {
|
|||
|
||||
#include <cstdlib>
|
||||
|
||||
#include "engine/execenv/execenv.hpp"
|
||||
#include "model/test_case.hpp"
|
||||
#include "model/test_program.hpp"
|
||||
#include "model/test_result.hpp"
|
||||
|
@ -47,6 +48,7 @@ extern "C" {
|
|||
#include "utils/sanity.hpp"
|
||||
|
||||
namespace config = utils::config;
|
||||
namespace execenv = engine::execenv;
|
||||
namespace fs = utils::fs;
|
||||
namespace process = utils::process;
|
||||
|
||||
|
@ -104,7 +106,10 @@ engine::plain_interface::exec_test(
|
|||
}
|
||||
|
||||
process::args_vector args;
|
||||
process::exec(test_program.absolute_path(), args);
|
||||
|
||||
auto e = execenv::get(test_program, test_case_name);
|
||||
e->init();
|
||||
e->exec(args);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
|
||||
#include "engine/requirements.hpp"
|
||||
|
||||
#include "engine/execenv/execenv.hpp"
|
||||
#include "model/metadata.hpp"
|
||||
#include "model/types.hpp"
|
||||
#include "utils/config/nodes.ipp"
|
||||
|
@ -100,6 +101,34 @@ check_allowed_architectures(const model::strings_set& allowed_architectures,
|
|||
}
|
||||
|
||||
|
||||
/// Checks if test's execenv matches the user configuration.
|
||||
///
|
||||
/// \param execenv Execution environment name a test is designed for.
|
||||
/// \param user_config Runtime user configuration.
|
||||
///
|
||||
/// \return Empty if the execenv is in the list or an error message otherwise.
|
||||
static std::string
|
||||
check_execenv(const std::string& execenv, const config::tree& user_config)
|
||||
{
|
||||
std::string name = execenv;
|
||||
if (name.empty())
|
||||
name = engine::execenv::default_execenv_name; // if test claims nothing
|
||||
|
||||
std::set< std::string > execenvs;
|
||||
try {
|
||||
execenvs = user_config.lookup< config::strings_set_node >("execenvs");
|
||||
} catch (const config::unknown_key_error&) {
|
||||
// okay, user config does not define it, empty set then
|
||||
}
|
||||
|
||||
if (execenvs.find(name) == execenvs.end())
|
||||
return F("'%s' execenv is not supported or not allowed by "
|
||||
"the runtime user configuration") % name;
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
/// Checks if the allowed platforms match the current architecture.
|
||||
///
|
||||
/// \param allowed_platforms Set of allowed platforms.
|
||||
|
@ -263,6 +292,10 @@ engine::check_reqs(const model::metadata& md, const config::tree& cfg,
|
|||
if (!reason.empty())
|
||||
return reason;
|
||||
|
||||
reason = check_execenv(md.execenv(), cfg);
|
||||
if (!reason.empty())
|
||||
return reason;
|
||||
|
||||
reason = check_allowed_platforms(md.allowed_platforms(), cfg);
|
||||
if (!reason.empty())
|
||||
return reason;
|
||||
|
|
|
@ -40,6 +40,7 @@ extern "C" {
|
|||
|
||||
#include "engine/config.hpp"
|
||||
#include "engine/exceptions.hpp"
|
||||
#include "engine/execenv/execenv.hpp"
|
||||
#include "engine/requirements.hpp"
|
||||
#include "model/context.hpp"
|
||||
#include "model/metadata.hpp"
|
||||
|
@ -68,6 +69,7 @@ extern "C" {
|
|||
|
||||
namespace config = utils::config;
|
||||
namespace datetime = utils::datetime;
|
||||
namespace execenv = engine::execenv;
|
||||
namespace executor = utils::process::executor;
|
||||
namespace fs = utils::fs;
|
||||
namespace logging = utils::logging;
|
||||
|
@ -87,6 +89,10 @@ using utils::optional;
|
|||
datetime::delta scheduler::cleanup_timeout(60, 0);
|
||||
|
||||
|
||||
/// Timeout for the test case execenv cleanup operation.
|
||||
datetime::delta scheduler::execenv_cleanup_timeout(60, 0);
|
||||
|
||||
|
||||
/// Timeout for the test case listing operation.
|
||||
///
|
||||
/// TODO(jmmv): This is here only for testing purposes. Maybe we should expose
|
||||
|
@ -206,6 +212,18 @@ struct test_exec_data : public exec_data {
|
|||
/// denote that no further attempts shall be made at cleaning this up.
|
||||
bool needs_cleanup;
|
||||
|
||||
/// Whether this test case still needs to have its execenv cleanup executed.
|
||||
///
|
||||
/// This is set externally when the cleanup routine is actually invoked to
|
||||
/// denote that no further attempts shall be made at cleaning this up.
|
||||
bool needs_execenv_cleanup;
|
||||
|
||||
/// Original PID of the test case subprocess.
|
||||
///
|
||||
/// This is used for the cleanup upon termination by a signal, to reap the
|
||||
/// leftovers and form missing exit_handle.
|
||||
pid_t pid;
|
||||
|
||||
/// The exit_handle for this test once it has completed.
|
||||
///
|
||||
/// This is set externally when the test case has finished, as we need this
|
||||
|
@ -222,12 +240,14 @@ struct test_exec_data : public exec_data {
|
|||
test_exec_data(const model::test_program_ptr test_program_,
|
||||
const std::string& test_case_name_,
|
||||
const std::shared_ptr< scheduler::interface > interface_,
|
||||
const config::tree& user_config_) :
|
||||
const config::tree& user_config_,
|
||||
const pid_t pid_) :
|
||||
exec_data(test_program_, test_case_name_),
|
||||
interface(interface_), user_config(user_config_)
|
||||
interface(interface_), user_config(user_config_), pid(pid_)
|
||||
{
|
||||
const model::test_case& test_case = test_program->find(test_case_name);
|
||||
needs_cleanup = test_case.get_metadata().has_cleanup();
|
||||
needs_execenv_cleanup = test_case.get_metadata().has_execenv();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -266,6 +286,40 @@ struct cleanup_exec_data : public exec_data {
|
|||
};
|
||||
|
||||
|
||||
/// Maintenance data held while a test execenv cleanup is being executed.
|
||||
///
|
||||
/// Instances of this object are related to a previous test_exec_data, as
|
||||
/// cleanup routines can only exist once the test has been run.
|
||||
struct execenv_exec_data : public exec_data {
|
||||
/// The exit handle of the test. This is necessary so that we can return
|
||||
/// the correct exit_handle to the user of the scheduler.
|
||||
executor::exit_handle body_exit_handle;
|
||||
|
||||
/// The final result of the test's body. This is necessary to compute the
|
||||
/// right return value for a test with a cleanup routine: the body result is
|
||||
/// respected if it is a "bad" result; else the result of the cleanup
|
||||
/// routine is used if it has failed.
|
||||
model::test_result body_result;
|
||||
|
||||
/// Constructor.
|
||||
///
|
||||
/// \param test_program_ Test program data for this test case.
|
||||
/// \param test_case_name_ Name of the test case.
|
||||
/// \param body_exit_handle_ If not none, exit handle of the body
|
||||
/// corresponding to the cleanup routine represented by this exec_data.
|
||||
/// \param body_result_ If not none, result of the body corresponding to the
|
||||
/// cleanup routine represented by this exec_data.
|
||||
execenv_exec_data(const model::test_program_ptr test_program_,
|
||||
const std::string& test_case_name_,
|
||||
const executor::exit_handle& body_exit_handle_,
|
||||
const model::test_result& body_result_) :
|
||||
exec_data(test_program_, test_case_name_),
|
||||
body_exit_handle(body_exit_handle_), body_result(body_result_)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/// Shared pointer to exec_data.
|
||||
///
|
||||
/// We require this because we want exec_data to not be copyable, and thus we
|
||||
|
@ -492,6 +546,40 @@ class run_test_cleanup {
|
|||
};
|
||||
|
||||
|
||||
/// Functor to execute a test execenv cleanup in a child process.
|
||||
class run_execenv_cleanup {
|
||||
/// Test program to execute.
|
||||
const model::test_program _test_program;
|
||||
|
||||
/// Name of the test case to execute.
|
||||
const std::string& _test_case_name;
|
||||
|
||||
public:
|
||||
/// Constructor.
|
||||
///
|
||||
/// \param test_program Test program to execute.
|
||||
/// \param test_case_name Name of the test case to execute.
|
||||
run_execenv_cleanup(
|
||||
const model::test_program_ptr test_program,
|
||||
const std::string& test_case_name) :
|
||||
_test_program(force_absolute_paths(*test_program)),
|
||||
_test_case_name(test_case_name)
|
||||
{
|
||||
}
|
||||
|
||||
/// Body of the subprocess.
|
||||
///
|
||||
/// \param control_directory The testcase directory where cleanup will be
|
||||
/// run from.
|
||||
void
|
||||
operator()(const fs::path& /* control_directory */)
|
||||
{
|
||||
auto e = execenv::get(_test_program, _test_case_name);
|
||||
e->cleanup();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/// Obtains the right scheduler interface for a given test program.
|
||||
///
|
||||
/// \param name The name of the interface of the test program.
|
||||
|
@ -835,6 +923,22 @@ struct engine::scheduler::scheduler_handle::impl : utils::noncopyable {
|
|||
% test_data->test_case_name);
|
||||
}
|
||||
}
|
||||
|
||||
const test_exec_data_vector td = tests_needing_execenv_cleanup();
|
||||
|
||||
for (test_exec_data_vector::const_iterator iter = td.begin();
|
||||
iter != td.end(); ++iter) {
|
||||
const test_exec_data* test_data = *iter;
|
||||
|
||||
try {
|
||||
sync_execenv_cleanup(test_data);
|
||||
} catch (const std::runtime_error& e) {
|
||||
LW(F("Failed to run execenv cleanup routine for %s:%s on abrupt "
|
||||
"termination")
|
||||
% test_data->test_program->relative_path()
|
||||
% test_data->test_case_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Finds any pending exec_datas that correspond to tests needing cleanup.
|
||||
|
@ -856,6 +960,8 @@ struct engine::scheduler::scheduler_handle::impl : utils::noncopyable {
|
|||
if (test_data->needs_cleanup) {
|
||||
tests_data.push_back(test_data);
|
||||
test_data->needs_cleanup = false;
|
||||
if (!test_data->exit_handle)
|
||||
test_data->exit_handle = generic.reap(test_data->pid);
|
||||
}
|
||||
} catch (const std::bad_cast& e) {
|
||||
// Do nothing for cleanup_exec_data objects.
|
||||
|
@ -865,6 +971,37 @@ struct engine::scheduler::scheduler_handle::impl : utils::noncopyable {
|
|||
return tests_data;
|
||||
}
|
||||
|
||||
/// Finds any pending exec_datas that correspond to tests needing execenv
|
||||
/// cleanup.
|
||||
///
|
||||
/// \return The collection of test_exec_data objects that have their
|
||||
/// specific execenv property set.
|
||||
test_exec_data_vector
|
||||
tests_needing_execenv_cleanup(void)
|
||||
{
|
||||
test_exec_data_vector tests_data;
|
||||
|
||||
for (exec_data_map::const_iterator iter = all_exec_data.begin();
|
||||
iter != all_exec_data.end(); ++iter) {
|
||||
const exec_data_ptr data = (*iter).second;
|
||||
|
||||
try {
|
||||
test_exec_data* test_data = &dynamic_cast< test_exec_data& >(
|
||||
*data.get());
|
||||
if (test_data->needs_execenv_cleanup) {
|
||||
tests_data.push_back(test_data);
|
||||
test_data->needs_execenv_cleanup = false;
|
||||
if (!test_data->exit_handle)
|
||||
test_data->exit_handle = generic.reap(test_data->pid);
|
||||
}
|
||||
} catch (const std::bad_cast& e) {
|
||||
// Do nothing for other objects.
|
||||
}
|
||||
}
|
||||
|
||||
return tests_data;
|
||||
}
|
||||
|
||||
/// Cleans up a single test case synchronously.
|
||||
///
|
||||
/// \param test_data The data of the previously executed test case to be
|
||||
|
@ -926,6 +1063,61 @@ struct engine::scheduler::scheduler_handle::impl : utils::noncopyable {
|
|||
|
||||
return handle;
|
||||
}
|
||||
|
||||
/// Cleans up a single test case execenv synchronously.
|
||||
///
|
||||
/// \param test_data The data of the previously executed test case to be
|
||||
/// cleaned up.
|
||||
void
|
||||
sync_execenv_cleanup(const test_exec_data* test_data)
|
||||
{
|
||||
// The message in this result should never be seen by the user, but use
|
||||
// something reasonable just in case it leaks and we need to pinpoint
|
||||
// the call site.
|
||||
model::test_result result(model::test_result_broken,
|
||||
"Test case died abruptly");
|
||||
|
||||
const executor::exec_handle cleanup_handle = spawn_execenv_cleanup(
|
||||
test_data->test_program, test_data->test_case_name,
|
||||
test_data->exit_handle.get(), result);
|
||||
generic.wait(cleanup_handle);
|
||||
}
|
||||
|
||||
/// Forks and executes a test case execenv cleanup asynchronously.
|
||||
///
|
||||
/// \param test_program The container test program.
|
||||
/// \param test_case_name The name of the test case to run.
|
||||
/// \param body_handle The exit handle of the test case's corresponding
|
||||
/// body. The cleanup will be executed in the same context.
|
||||
/// \param body_result The result of the test case's corresponding body.
|
||||
///
|
||||
/// \return A handle for the background operation. Used to match the result
|
||||
/// of the execution returned by wait_any() with this invocation.
|
||||
executor::exec_handle
|
||||
spawn_execenv_cleanup(const model::test_program_ptr test_program,
|
||||
const std::string& test_case_name,
|
||||
const executor::exit_handle& body_handle,
|
||||
const model::test_result& body_result)
|
||||
{
|
||||
generic.check_interrupt();
|
||||
|
||||
LI(F("Spawning %s:%s (execenv cleanup)")
|
||||
% test_program->absolute_path() % test_case_name);
|
||||
|
||||
const executor::exec_handle handle = generic.spawn_followup(
|
||||
run_execenv_cleanup(test_program, test_case_name),
|
||||
body_handle, execenv_cleanup_timeout);
|
||||
|
||||
const exec_data_ptr data(new execenv_exec_data(
|
||||
test_program, test_case_name, body_handle, body_result));
|
||||
LD(F("Inserting %s into all_exec_data (execenv cleanup)") % handle.pid());
|
||||
INV_MSG(all_exec_data.find(handle.pid()) == all_exec_data.end(),
|
||||
F("PID %s already in all_exec_data; not properly cleaned "
|
||||
"up or reused too fast") % handle.pid());;
|
||||
all_exec_data.insert(exec_data_map::value_type(handle.pid(), data));
|
||||
|
||||
return handle;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
@ -1115,7 +1307,7 @@ scheduler::scheduler_handle::spawn_test(
|
|||
unprivileged_user);
|
||||
|
||||
const exec_data_ptr data(new test_exec_data(
|
||||
test_program, test_case_name, interface, user_config));
|
||||
test_program, test_case_name, interface, user_config, handle.pid()));
|
||||
LD(F("Inserting %s into all_exec_data") % handle.pid());
|
||||
INV_MSG(
|
||||
_pimpl->all_exec_data.find(handle.pid()) == _pimpl->all_exec_data.end(),
|
||||
|
@ -1150,6 +1342,8 @@ scheduler::scheduler_handle::wait_any(void)
|
|||
_pimpl->generic, handle);
|
||||
|
||||
optional< model::test_result > result;
|
||||
|
||||
// test itself
|
||||
try {
|
||||
test_exec_data* test_data = &dynamic_cast< test_exec_data& >(
|
||||
*data.get());
|
||||
|
@ -1185,6 +1379,7 @@ scheduler::scheduler_handle::wait_any(void)
|
|||
// if the test's body reports a skip (because actions could have
|
||||
// already been taken).
|
||||
test_data->needs_cleanup = false;
|
||||
test_data->needs_execenv_cleanup = false;
|
||||
}
|
||||
}
|
||||
if (!result) {
|
||||
|
@ -1209,7 +1404,6 @@ scheduler::scheduler_handle::wait_any(void)
|
|||
_pimpl->spawn_cleanup(test_data->test_program,
|
||||
test_data->test_case_name,
|
||||
test_data->user_config, handle, result.get());
|
||||
test_data->needs_cleanup = false;
|
||||
|
||||
// TODO(jmmv): Chaining this call is ugly. We'd be better off by
|
||||
// looping over terminated processes until we got a result suitable
|
||||
|
@ -1218,7 +1412,21 @@ scheduler::scheduler_handle::wait_any(void)
|
|||
// of test cases do not have cleanup routines.
|
||||
return wait_any();
|
||||
}
|
||||
|
||||
if (test_data->needs_execenv_cleanup) {
|
||||
INV(test_case.get_metadata().has_execenv());
|
||||
_pimpl->spawn_execenv_cleanup(test_data->test_program,
|
||||
test_data->test_case_name,
|
||||
handle, result.get());
|
||||
test_data->needs_execenv_cleanup = false;
|
||||
return wait_any();
|
||||
}
|
||||
} catch (const std::bad_cast& e) {
|
||||
// ok, let's check for another type
|
||||
}
|
||||
|
||||
// test cleanup
|
||||
try {
|
||||
const cleanup_exec_data* cleanup_data =
|
||||
&dynamic_cast< const cleanup_exec_data& >(*data.get());
|
||||
LD(F("Got %s from all_exec_data (cleanup)") % handle.original_pid());
|
||||
|
@ -1257,7 +1465,65 @@ scheduler::scheduler_handle::wait_any(void)
|
|||
_pimpl->all_exec_data.erase(handle.original_pid());
|
||||
|
||||
handle = cleanup_data->body_exit_handle;
|
||||
|
||||
const exec_data_map::iterator it = _pimpl->all_exec_data.find(
|
||||
handle.original_pid());
|
||||
if (it != _pimpl->all_exec_data.end()) {
|
||||
exec_data_ptr d = (*it).second;
|
||||
test_exec_data* test_data = &dynamic_cast< test_exec_data& >(
|
||||
*d.get());
|
||||
const model::test_case& test_case =
|
||||
cleanup_data->test_program->find(cleanup_data->test_case_name);
|
||||
test_data->needs_cleanup = false;
|
||||
|
||||
if (test_data->needs_execenv_cleanup) {
|
||||
INV(test_case.get_metadata().has_execenv());
|
||||
_pimpl->spawn_execenv_cleanup(cleanup_data->test_program,
|
||||
cleanup_data->test_case_name,
|
||||
handle, result.get());
|
||||
test_data->needs_execenv_cleanup = false;
|
||||
return wait_any();
|
||||
}
|
||||
}
|
||||
} catch (const std::bad_cast& e) {
|
||||
// ok, let's check for another type
|
||||
}
|
||||
|
||||
// execenv cleanup
|
||||
try {
|
||||
const execenv_exec_data* execenv_data =
|
||||
&dynamic_cast< const execenv_exec_data& >(*data.get());
|
||||
LD(F("Got %s from all_exec_data (execenv cleanup)") % handle.original_pid());
|
||||
|
||||
const model::test_result& body_result = execenv_data->body_result;
|
||||
if (body_result.good()) {
|
||||
if (!handle.status()) {
|
||||
result = model::test_result(model::test_result_broken,
|
||||
"Test case execenv cleanup timed out");
|
||||
} else {
|
||||
if (!handle.status().get().exited() ||
|
||||
handle.status().get().exitstatus() != EXIT_SUCCESS) {
|
||||
result = model::test_result(
|
||||
model::test_result_broken,
|
||||
"Test case execenv cleanup did not terminate successfully"); // ?
|
||||
} else {
|
||||
result = body_result;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
result = body_result;
|
||||
}
|
||||
|
||||
LD(F("Removing %s from all_exec_data (execenv cleanup) in favor of %s")
|
||||
% handle.original_pid()
|
||||
% execenv_data->body_exit_handle.original_pid());
|
||||
_pimpl->all_exec_data.erase(handle.original_pid());
|
||||
|
||||
handle = execenv_data->body_exit_handle;
|
||||
} catch (const std::bad_cast& e) {
|
||||
// ok, it was one of the types above
|
||||
}
|
||||
|
||||
INV(result);
|
||||
|
||||
std::shared_ptr< result_handle::bimpl > result_handle_bimpl(
|
||||
|
|
|
@ -262,6 +262,7 @@ class scheduler_handle {
|
|||
|
||||
|
||||
extern utils::datetime::delta cleanup_timeout;
|
||||
extern utils::datetime::delta execenv_cleanup_timeout;
|
||||
extern utils::datetime::delta list_timeout;
|
||||
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@ extern "C" {
|
|||
#include <cstdlib>
|
||||
|
||||
#include "engine/exceptions.hpp"
|
||||
#include "engine/execenv/execenv.hpp"
|
||||
#include "engine/tap_parser.hpp"
|
||||
#include "model/test_case.hpp"
|
||||
#include "model/test_program.hpp"
|
||||
|
@ -48,6 +49,7 @@ extern "C" {
|
|||
#include "utils/sanity.hpp"
|
||||
|
||||
namespace config = utils::config;
|
||||
namespace execenv = engine::execenv;
|
||||
namespace fs = utils::fs;
|
||||
namespace process = utils::process;
|
||||
|
||||
|
@ -151,7 +153,10 @@ engine::tap_interface::exec_test(
|
|||
}
|
||||
|
||||
process::args_vector args;
|
||||
process::exec(test_program.absolute_path(), args);
|
||||
|
||||
auto e = execenv::get(test_program, test_case_name);
|
||||
e->init();
|
||||
e->exec(args);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -43,6 +43,9 @@ syntax(2)
|
|||
-- Name of the system architecture (aka processor type).
|
||||
architecture = "x86_64"
|
||||
|
||||
-- List of execution environments.
|
||||
execenvs = "host jail"
|
||||
|
||||
-- Maximum number of jobs (such as test case runs) to execute concurrently.
|
||||
parallelism = 16
|
||||
|
||||
|
|
|
@ -42,6 +42,7 @@ all_body() {
|
|||
cat >"${HOME}/.kyua/kyua.conf" <<EOF
|
||||
syntax(2)
|
||||
architecture = "my-architecture"
|
||||
execenvs = "my-env1 my-env2"
|
||||
parallelism = 256
|
||||
platform = "my-platform"
|
||||
unprivileged_user = "$(id -u -n)"
|
||||
|
@ -51,6 +52,7 @@ EOF
|
|||
|
||||
cat >expout <<EOF
|
||||
architecture = my-architecture
|
||||
execenvs = my-env1 my-env2
|
||||
parallelism = 256
|
||||
platform = my-platform
|
||||
test_suites.suite1.the_variable = value1
|
||||
|
|
|
@ -97,6 +97,8 @@ CONTENTS STRIPPED BY TEST
|
|||
allowed_architectures is empty
|
||||
allowed_platforms is empty
|
||||
description is empty
|
||||
execenv is empty
|
||||
execenv_jail_params is empty
|
||||
has_cleanup = false
|
||||
is_exclusive = false
|
||||
required_configs is empty
|
||||
|
@ -135,6 +137,8 @@ Test case metadata
|
|||
allowed_architectures is empty
|
||||
allowed_platforms is empty
|
||||
description is empty
|
||||
execenv is empty
|
||||
execenv_jail_params is empty
|
||||
has_cleanup = false
|
||||
is_exclusive = false
|
||||
required_configs is empty
|
||||
|
@ -211,6 +215,8 @@ CONTENTS STRIPPED BY TEST
|
|||
allowed_architectures is empty
|
||||
allowed_platforms is empty
|
||||
description is empty
|
||||
execenv is empty
|
||||
execenv_jail_params is empty
|
||||
has_cleanup = false
|
||||
is_exclusive = false
|
||||
required_configs is empty
|
||||
|
@ -249,6 +255,8 @@ Test case metadata
|
|||
allowed_architectures is empty
|
||||
allowed_platforms is empty
|
||||
description is empty
|
||||
execenv is empty
|
||||
execenv_jail_params is empty
|
||||
has_cleanup = false
|
||||
is_exclusive = false
|
||||
required_configs is empty
|
||||
|
|
|
@ -251,6 +251,8 @@ Metadata:
|
|||
allowed_architectures is empty
|
||||
allowed_platforms is empty
|
||||
description is empty
|
||||
execenv is empty
|
||||
execenv_jail_params is empty
|
||||
has_cleanup = false
|
||||
is_exclusive = false
|
||||
required_configs is empty
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "cli/main.hpp"
|
||||
#include "os/freebsd/main.hpp"
|
||||
|
||||
|
||||
/// Program entry point.
|
||||
|
@ -46,5 +47,7 @@
|
|||
int
|
||||
main(const int argc, const char* const* const argv)
|
||||
{
|
||||
freebsd::main(argc, argv);
|
||||
|
||||
return cli::main(argc, argv);
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
|
||||
#include <memory>
|
||||
|
||||
#include "engine/execenv/execenv.hpp"
|
||||
#include "model/exceptions.hpp"
|
||||
#include "model/types.hpp"
|
||||
#include "utils/config/exceptions.hpp"
|
||||
|
@ -247,6 +248,8 @@ init_tree(config::tree& tree)
|
|||
tree.define< config::strings_set_node >("allowed_platforms");
|
||||
tree.define_dynamic("custom");
|
||||
tree.define< config::string_node >("description");
|
||||
tree.define< config::string_node >("execenv");
|
||||
tree.define< config::string_node >("execenv_jail_params");
|
||||
tree.define< config::bool_node >("has_cleanup");
|
||||
tree.define< config::bool_node >("is_exclusive");
|
||||
tree.define< config::strings_set_node >("required_configs");
|
||||
|
@ -270,6 +273,8 @@ set_defaults(config::tree& tree)
|
|||
tree.set< config::strings_set_node >("allowed_platforms",
|
||||
model::strings_set());
|
||||
tree.set< config::string_node >("description", "");
|
||||
tree.set< config::string_node >("execenv", "");
|
||||
tree.set< config::string_node >("execenv_jail_params", "");
|
||||
tree.set< config::bool_node >("has_cleanup", false);
|
||||
tree.set< config::bool_node >("is_exclusive", false);
|
||||
tree.set< config::strings_set_node >("required_configs",
|
||||
|
@ -464,6 +469,36 @@ model::metadata::description(void) const
|
|||
}
|
||||
|
||||
|
||||
/// Returns execution environment name.
|
||||
///
|
||||
/// \return Name of configured execution environment.
|
||||
const std::string&
|
||||
model::metadata::execenv(void) const
|
||||
{
|
||||
if (_pimpl->props.is_set("execenv")) {
|
||||
return _pimpl->props.lookup< config::string_node >("execenv");
|
||||
} else {
|
||||
return get_defaults().lookup< config::string_node >("execenv");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Returns execenv jail(8) parameters string to run a test with.
|
||||
///
|
||||
/// \return String of jail parameters.
|
||||
const std::string&
|
||||
model::metadata::execenv_jail_params(void) const
|
||||
{
|
||||
if (_pimpl->props.is_set("execenv_jail_params")) {
|
||||
return _pimpl->props.lookup< config::string_node >(
|
||||
"execenv_jail_params");
|
||||
} else {
|
||||
return get_defaults().lookup< config::string_node >(
|
||||
"execenv_jail_params");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Returns whether the test has a cleanup part or not.
|
||||
///
|
||||
/// \return True if there is a cleanup part; false otherwise.
|
||||
|
@ -478,6 +513,17 @@ model::metadata::has_cleanup(void) const
|
|||
}
|
||||
|
||||
|
||||
/// Returns whether the test has a specific execenv apart from default one.
|
||||
///
|
||||
/// \return True if there is a non-host execenv configured; false otherwise.
|
||||
bool
|
||||
model::metadata::has_execenv(void) const
|
||||
{
|
||||
const std::string& name = execenv();
|
||||
return !name.empty() && name != engine::execenv::default_execenv_name;
|
||||
}
|
||||
|
||||
|
||||
/// Returns whether the test is exclusive or not.
|
||||
///
|
||||
/// \return True if the test has to be run on its own, not concurrently with any
|
||||
|
@ -890,6 +936,36 @@ model::metadata_builder::set_description(const std::string& description)
|
|||
}
|
||||
|
||||
|
||||
/// Sets execution environment name.
|
||||
///
|
||||
/// \param name Execution environment name.
|
||||
///
|
||||
/// \return A reference to this builder.
|
||||
///
|
||||
/// \throw model::error If the value is invalid.
|
||||
model::metadata_builder&
|
||||
model::metadata_builder::set_execenv(const std::string& name)
|
||||
{
|
||||
set< config::string_node >(_pimpl->props, "execenv", name);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
/// Sets execenv jail(8) parameters string to run the test with.
|
||||
///
|
||||
/// \param params String of jail parameters.
|
||||
///
|
||||
/// \return A reference to this builder.
|
||||
///
|
||||
/// \throw model::error If the value is invalid.
|
||||
model::metadata_builder&
|
||||
model::metadata_builder::set_execenv_jail_params(const std::string& params)
|
||||
{
|
||||
set< config::string_node >(_pimpl->props, "execenv_jail_params", params);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
/// Sets whether the test has a cleanup part or not.
|
||||
///
|
||||
/// \param cleanup True if the test has a cleanup part; false otherwise.
|
||||
|
|
|
@ -67,7 +67,10 @@ class metadata {
|
|||
const strings_set& allowed_platforms(void) const;
|
||||
model::properties_map custom(void) const;
|
||||
const std::string& description(void) const;
|
||||
const std::string& execenv(void) const;
|
||||
const std::string& execenv_jail_params(void) const;
|
||||
bool has_cleanup(void) const;
|
||||
bool has_execenv(void) const;
|
||||
bool is_exclusive(void) const;
|
||||
const strings_set& required_configs(void) const;
|
||||
const utils::units::bytes& required_disk_space(void) const;
|
||||
|
@ -110,6 +113,8 @@ class metadata_builder : utils::noncopyable {
|
|||
metadata_builder& set_allowed_platforms(const strings_set&);
|
||||
metadata_builder& set_custom(const model::properties_map&);
|
||||
metadata_builder& set_description(const std::string&);
|
||||
metadata_builder& set_execenv(const std::string&);
|
||||
metadata_builder& set_execenv_jail_params(const std::string&);
|
||||
metadata_builder& set_has_cleanup(const bool);
|
||||
metadata_builder& set_is_exclusive(const bool);
|
||||
metadata_builder& set_required_configs(const strings_set&);
|
||||
|
|
|
@ -315,6 +315,8 @@ ATF_TEST_CASE_BODY(to_properties)
|
|||
props["allowed_platforms"] = "";
|
||||
props["custom.foo"] = "bar";
|
||||
props["description"] = "";
|
||||
props["execenv"] = "";
|
||||
props["execenv_jail_params"] = "";
|
||||
props["has_cleanup"] = "false";
|
||||
props["is_exclusive"] = "false";
|
||||
props["required_configs"] = "";
|
||||
|
@ -406,7 +408,8 @@ ATF_TEST_CASE_BODY(output__defaults)
|
|||
std::ostringstream str;
|
||||
str << model::metadata_builder().build();
|
||||
ATF_REQUIRE_EQ("metadata{allowed_architectures='', allowed_platforms='', "
|
||||
"description='', has_cleanup='false', is_exclusive='false', "
|
||||
"description='', execenv='', execenv_jail_params='', "
|
||||
"has_cleanup='false', is_exclusive='false', "
|
||||
"required_configs='', "
|
||||
"required_disk_space='0', required_files='', "
|
||||
"required_memory='0', "
|
||||
|
@ -428,7 +431,8 @@ ATF_TEST_CASE_BODY(output__some_values)
|
|||
.build();
|
||||
ATF_REQUIRE_EQ(
|
||||
"metadata{allowed_architectures='abc', allowed_platforms='', "
|
||||
"description='', has_cleanup='false', is_exclusive='true', "
|
||||
"description='', execenv='', execenv_jail_params='', "
|
||||
"has_cleanup='false', is_exclusive='true', "
|
||||
"required_configs='', "
|
||||
"required_disk_space='0', required_files='bar foo', "
|
||||
"required_memory='1.00K', "
|
||||
|
|
|
@ -200,7 +200,8 @@ ATF_TEST_CASE_BODY(test_case__output)
|
|||
ATF_REQUIRE_EQ(
|
||||
"test_case{name='the-name', "
|
||||
"metadata=metadata{allowed_architectures='', allowed_platforms='foo', "
|
||||
"custom.bar='baz', description='', has_cleanup='false', "
|
||||
"custom.bar='baz', description='', execenv='', execenv_jail_params='', "
|
||||
"has_cleanup='false', "
|
||||
"is_exclusive='false', "
|
||||
"required_configs='', required_disk_space='0', required_files='', "
|
||||
"required_memory='0', "
|
||||
|
|
|
@ -544,7 +544,8 @@ check_output__no_test_cases(void)
|
|||
"test_program{interface='plain', binary='binary/path', "
|
||||
"root='/the/root', test_suite='suite-name', "
|
||||
"metadata=metadata{allowed_architectures='a', allowed_platforms='', "
|
||||
"description='', has_cleanup='false', is_exclusive='false', "
|
||||
"description='', execenv='', execenv_jail_params='', "
|
||||
"has_cleanup='false', is_exclusive='false', "
|
||||
"required_configs='', required_disk_space='0', required_files='', "
|
||||
"required_memory='0', "
|
||||
"required_programs='', required_user='', timeout='300'}, "
|
||||
|
@ -593,21 +594,23 @@ check_output__some_test_cases(void)
|
|||
"test_program{interface='plain', binary='binary/path', "
|
||||
"root='/the/root', test_suite='suite-name', "
|
||||
"metadata=metadata{allowed_architectures='a', allowed_platforms='', "
|
||||
"description='', has_cleanup='false', is_exclusive='false', "
|
||||
"description='', execenv='', execenv_jail_params='', "
|
||||
"has_cleanup='false', is_exclusive='false', "
|
||||
"required_configs='', required_disk_space='0', required_files='', "
|
||||
"required_memory='0', "
|
||||
"required_programs='', required_user='', timeout='300'}, "
|
||||
"test_cases=map("
|
||||
"another-name=test_case{name='another-name', "
|
||||
"metadata=metadata{allowed_architectures='a', allowed_platforms='', "
|
||||
"description='', has_cleanup='false', is_exclusive='false', "
|
||||
"description='', execenv='', execenv_jail_params='', "
|
||||
"has_cleanup='false', is_exclusive='false', "
|
||||
"required_configs='', required_disk_space='0', required_files='', "
|
||||
"required_memory='0', "
|
||||
"required_programs='', required_user='', timeout='300'}}, "
|
||||
"the-name=test_case{name='the-name', "
|
||||
"metadata=metadata{allowed_architectures='a', allowed_platforms='foo', "
|
||||
"custom.bar='baz', description='', has_cleanup='false', "
|
||||
"is_exclusive='false', "
|
||||
"custom.bar='baz', description='', execenv='', execenv_jail_params='', "
|
||||
"has_cleanup='false', is_exclusive='false', "
|
||||
"required_configs='', required_disk_space='0', required_files='', "
|
||||
"required_memory='0', "
|
||||
"required_programs='', required_user='', timeout='300'}})}",
|
||||
|
|
78
contrib/kyua/os/freebsd/execenv_jail.cpp
Normal file
78
contrib/kyua/os/freebsd/execenv_jail.cpp
Normal file
|
@ -0,0 +1,78 @@
|
|||
// 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/execenv_jail.hpp"
|
||||
|
||||
#include "model/metadata.hpp"
|
||||
#include "model/test_case.hpp"
|
||||
#include "os/freebsd/utils/jail.hpp"
|
||||
#include "utils/fs/path.hpp"
|
||||
|
||||
|
||||
namespace freebsd {
|
||||
|
||||
|
||||
bool execenv_jail_supported = true;
|
||||
|
||||
|
||||
static utils::jail jail = utils::jail();
|
||||
|
||||
|
||||
void
|
||||
execenv_jail::init() const
|
||||
{
|
||||
auto test_case = _test_program.find(_test_case_name);
|
||||
|
||||
jail.create(
|
||||
jail.make_name(_test_program.absolute_path(), _test_case_name),
|
||||
test_case.get_metadata().execenv_jail_params()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
execenv_jail::cleanup() const
|
||||
{
|
||||
jail.remove(
|
||||
jail.make_name(_test_program.absolute_path(), _test_case_name)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
execenv_jail::exec(const args_vector& args) const
|
||||
{
|
||||
jail.exec(
|
||||
jail.make_name(_test_program.absolute_path(), _test_case_name),
|
||||
_test_program.absolute_path(),
|
||||
args
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
} // namespace freebsd
|
65
contrib/kyua/os/freebsd/execenv_jail.hpp
Normal file
65
contrib/kyua/os/freebsd/execenv_jail.hpp
Normal file
|
@ -0,0 +1,65 @@
|
|||
// 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 os/freebsd/execenv_jail.hpp
|
||||
/// FreeBSD jail execution environment.
|
||||
|
||||
#if !defined(FREEBSD_EXECENV_JAIL_HPP)
|
||||
#define FREEBSD_EXECENV_JAIL_HPP
|
||||
|
||||
#include "engine/execenv/execenv.hpp"
|
||||
|
||||
#include "utils/process/operations_fwd.hpp"
|
||||
|
||||
namespace execenv = engine::execenv;
|
||||
|
||||
using utils::process::args_vector;
|
||||
|
||||
|
||||
namespace freebsd {
|
||||
|
||||
|
||||
extern bool execenv_jail_supported;
|
||||
|
||||
|
||||
class execenv_jail : public execenv::interface {
|
||||
public:
|
||||
execenv_jail(const model::test_program& test_program,
|
||||
const std::string& test_case_name) :
|
||||
execenv::interface(test_program, test_case_name)
|
||||
{}
|
||||
|
||||
void init() const;
|
||||
void cleanup() const;
|
||||
void exec(const args_vector& args) const UTILS_NORETURN;
|
||||
};
|
||||
|
||||
|
||||
} // namespace freebsd
|
||||
|
||||
#endif // !defined(FREEBSD_EXECENV_JAIL_HPP)
|
63
contrib/kyua/os/freebsd/execenv_jail_manager.cpp
Normal file
63
contrib/kyua/os/freebsd/execenv_jail_manager.cpp
Normal file
|
@ -0,0 +1,63 @@
|
|||
// 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/execenv_jail_manager.hpp"
|
||||
|
||||
#include "model/metadata.hpp"
|
||||
#include "model/test_case.hpp"
|
||||
#include "os/freebsd/execenv_jail.hpp"
|
||||
|
||||
static const std::string execenv_name = "jail";
|
||||
|
||||
const std::string&
|
||||
freebsd::execenv_jail_manager::name() const
|
||||
{
|
||||
return execenv_name;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
freebsd::execenv_jail_manager::is_supported() const
|
||||
{
|
||||
return freebsd::execenv_jail_supported;
|
||||
}
|
||||
|
||||
|
||||
std::unique_ptr< execenv::interface >
|
||||
freebsd::execenv_jail_manager::probe(
|
||||
const model::test_program& test_program,
|
||||
const std::string& test_case_name) const
|
||||
{
|
||||
auto test_case = test_program.find(test_case_name);
|
||||
if (test_case.get_metadata().execenv() != execenv_name)
|
||||
return nullptr;
|
||||
|
||||
return std::unique_ptr< execenv::interface >(
|
||||
new freebsd::execenv_jail(test_program, test_case_name)
|
||||
);
|
||||
}
|
54
contrib/kyua/os/freebsd/execenv_jail_manager.hpp
Normal file
54
contrib/kyua/os/freebsd/execenv_jail_manager.hpp
Normal file
|
@ -0,0 +1,54 @@
|
|||
// 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 os/freebsd/execenv_jail_manager.hpp
|
||||
/// FreeBSD jail execution environment manager.
|
||||
|
||||
#if !defined(FREEBSD_EXECENV_JAIL_MANAGER_HPP)
|
||||
#define FREEBSD_EXECENV_JAIL_MANAGER_HPP
|
||||
|
||||
#include "engine/execenv/execenv.hpp"
|
||||
|
||||
namespace execenv = engine::execenv;
|
||||
|
||||
namespace freebsd {
|
||||
|
||||
|
||||
class execenv_jail_manager : public execenv::manager {
|
||||
public:
|
||||
const std::string& name() const;
|
||||
bool is_supported() const;
|
||||
std::unique_ptr< execenv::interface > probe(
|
||||
const model::test_program& test_program,
|
||||
const std::string& test_case_name) const;
|
||||
};
|
||||
|
||||
|
||||
} // namespace freebsd
|
||||
|
||||
#endif // !defined(FREEBSD_EXECENV_JAIL_MANAGER_HPP)
|
75
contrib/kyua/os/freebsd/execenv_jail_stub.cpp
Normal file
75
contrib/kyua/os/freebsd/execenv_jail_stub.cpp
Normal file
|
@ -0,0 +1,75 @@
|
|||
// 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/execenv_jail.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "utils/process/operations_fwd.hpp"
|
||||
|
||||
using utils::process::args_vector;
|
||||
|
||||
|
||||
static inline void requires_freebsd(void) UTILS_NORETURN;
|
||||
|
||||
static inline void
|
||||
requires_freebsd(void)
|
||||
{
|
||||
std::cerr << "execenv=\"jail\" requires FreeBSD with jail feature.\n";
|
||||
std::exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
|
||||
namespace freebsd {
|
||||
|
||||
|
||||
bool execenv_jail_supported = false;
|
||||
|
||||
|
||||
void
|
||||
execenv_jail::init() const
|
||||
{
|
||||
requires_freebsd();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
execenv_jail::cleanup() const
|
||||
{
|
||||
requires_freebsd();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
execenv_jail::exec(const args_vector&) const
|
||||
{
|
||||
requires_freebsd();
|
||||
}
|
||||
|
||||
|
||||
} // namespace freebsd
|
54
contrib/kyua/os/freebsd/main.cpp
Normal file
54
contrib/kyua/os/freebsd/main.cpp
Normal file
|
@ -0,0 +1,54 @@
|
|||
// 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/main.hpp"
|
||||
|
||||
#include "engine/execenv/execenv.hpp"
|
||||
#include "os/freebsd/execenv_jail_manager.hpp"
|
||||
|
||||
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)
|
||||
{
|
||||
execenv::register_execenv(
|
||||
std::shared_ptr< execenv::manager >(new freebsd::execenv_jail_manager())
|
||||
);
|
||||
|
||||
return 0;
|
||||
}
|
41
contrib/kyua/os/freebsd/main.hpp
Normal file
41
contrib/kyua/os/freebsd/main.hpp
Normal file
|
@ -0,0 +1,41 @@
|
|||
// 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 os/freebsd/main.hpp
|
||||
/// FreeBSD related features initialization.
|
||||
|
||||
#if !defined(FREEBSD_MAIN_HPP)
|
||||
#define FREEBSD_MAIN_HPP
|
||||
|
||||
namespace freebsd {
|
||||
|
||||
int main(const int argc, const char* const* const argv);
|
||||
|
||||
} // namespace freebsd
|
||||
|
||||
#endif // !defined(FREEBSD_MAIN_HPP)
|
306
contrib/kyua/os/freebsd/utils/jail.cpp
Normal file
306
contrib/kyua/os/freebsd/utils/jail.cpp
Normal file
|
@ -0,0 +1,306 @@
|
|||
// 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/utils/jail.hpp"
|
||||
|
||||
extern "C" {
|
||||
#include <limits.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
// FreeBSD sysctl facility
|
||||
#include <sys/sysctl.h>
|
||||
|
||||
// FreeBSD Jail syscalls
|
||||
#include <sys/param.h>
|
||||
#include <sys/jail.h>
|
||||
|
||||
// FreeBSD Jail library
|
||||
#include <jail.h>
|
||||
}
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <regex>
|
||||
|
||||
#include "model/metadata.hpp"
|
||||
#include "model/test_case.hpp"
|
||||
#include "model/test_program.hpp"
|
||||
#include "utils/fs/path.hpp"
|
||||
#include "utils/process/child.ipp"
|
||||
#include "utils/format/macros.hpp"
|
||||
#include "utils/process/operations.hpp"
|
||||
#include "utils/process/status.hpp"
|
||||
|
||||
namespace process = utils::process;
|
||||
namespace fs = utils::fs;
|
||||
|
||||
using utils::process::args_vector;
|
||||
using utils::process::child;
|
||||
|
||||
|
||||
static const size_t jail_name_max_len = MAXHOSTNAMELEN - 1;
|
||||
static const char* jail_name_prefix = "kyua";
|
||||
|
||||
|
||||
/// Functor to run a program.
|
||||
class run {
|
||||
/// Program binary absolute path.
|
||||
const utils::fs::path& _program;
|
||||
|
||||
/// Program arguments.
|
||||
const args_vector& _args;
|
||||
|
||||
public:
|
||||
/// Constructor.
|
||||
///
|
||||
/// \param program Program binary absolute path.
|
||||
/// \param args Program arguments.
|
||||
run(
|
||||
const utils::fs::path& program,
|
||||
const args_vector& args) :
|
||||
_program(program),
|
||||
_args(args)
|
||||
{
|
||||
}
|
||||
|
||||
/// Body of the subprocess.
|
||||
void
|
||||
operator()(void)
|
||||
{
|
||||
process::exec(_program, _args);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
namespace freebsd {
|
||||
namespace utils {
|
||||
|
||||
|
||||
std::vector< std::string >
|
||||
jail::parse_params_string(const std::string& str)
|
||||
{
|
||||
std::vector< std::string > params;
|
||||
std::string p;
|
||||
char quote = 0;
|
||||
|
||||
std::istringstream iss(str);
|
||||
while (iss >> p) {
|
||||
if (p.front() == '"' || p.front() == '\'') {
|
||||
quote = p.front();
|
||||
p.erase(p.begin());
|
||||
if (p.find(quote) == std::string::npos) {
|
||||
std::string rest;
|
||||
std::getline(iss, rest, quote);
|
||||
p += rest;
|
||||
iss.ignore();
|
||||
}
|
||||
if (p.back() == quote)
|
||||
p.erase(p.end() - 1);
|
||||
}
|
||||
params.push_back(p);
|
||||
}
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
|
||||
/// Constructs a jail name based on program and test case.
|
||||
///
|
||||
/// The formula is "kyua" + <program path> + "_" + <test case name>.
|
||||
/// All non-alphanumeric chars are replaced with "_".
|
||||
///
|
||||
/// If a resulting string exceeds maximum allowed length of a jail name,
|
||||
/// then it's shortened from the left side keeping the "kyua" prefix.
|
||||
///
|
||||
/// \param program The test program.
|
||||
/// \param test_case_name Name of the test case.
|
||||
///
|
||||
/// \return A jail name string.
|
||||
std::string
|
||||
jail::make_name(const fs::path& program,
|
||||
const std::string& test_case_name)
|
||||
{
|
||||
std::string name = std::regex_replace(
|
||||
program.str() + "_" + test_case_name,
|
||||
std::regex(R"([^A-Za-z0-9_])"),
|
||||
"_");
|
||||
|
||||
const std::string::size_type limit =
|
||||
jail_name_max_len - strlen(jail_name_prefix);
|
||||
if (name.length() > limit)
|
||||
name.erase(0, name.length() - limit);
|
||||
|
||||
return jail_name_prefix + name;
|
||||
}
|
||||
|
||||
|
||||
/// Create a jail with a given name and params string.
|
||||
///
|
||||
/// A new jail will always be 'persist', thus the caller is expected to remove
|
||||
/// the jail eventually via remove().
|
||||
///
|
||||
/// It's expected to be run in a subprocess.
|
||||
///
|
||||
/// \param jail_name Name of a new jail.
|
||||
/// \param jail_params String of jail parameters.
|
||||
void
|
||||
jail::create(const std::string& jail_name,
|
||||
const std::string& jail_params)
|
||||
{
|
||||
args_vector av;
|
||||
|
||||
// creation flag
|
||||
av.push_back("-qc");
|
||||
|
||||
// jail name
|
||||
av.push_back("name=" + jail_name);
|
||||
|
||||
// determine maximum allowed children.max
|
||||
const char* const oid = "security.jail.children.max";
|
||||
int max;
|
||||
size_t len = sizeof(max);
|
||||
if (::sysctlbyname(oid, &max, &len, NULL, 0) != 0) {
|
||||
std::cerr << "sysctlbyname(" << oid << ") errors: "
|
||||
<< strerror(errno) << ".\n";
|
||||
std::exit(EXIT_FAILURE);
|
||||
}
|
||||
if (len < sizeof(max)) {
|
||||
std::cerr << "sysctlbyname(" << oid << ") provides less "
|
||||
"data (" << len << ") than expected (" << sizeof(max) << ").\n";
|
||||
std::exit(EXIT_FAILURE);
|
||||
}
|
||||
if (max < 0) {
|
||||
std::cerr << "sysctlbyname(" << oid << ") yields "
|
||||
"abnormal " << max << ".\n";
|
||||
std::exit(EXIT_FAILURE);
|
||||
}
|
||||
if (max > 0)
|
||||
max--; // a child jail must have less than parent's children.max
|
||||
av.push_back("children.max=" + std::to_string(max));
|
||||
|
||||
// test defined jail params
|
||||
const std::vector< std::string > params = parse_params_string(jail_params);
|
||||
for (const std::string& p : params)
|
||||
av.push_back(p);
|
||||
|
||||
// it must be persist
|
||||
av.push_back("persist");
|
||||
|
||||
// invoke jail
|
||||
std::auto_ptr< process::child > child = child::fork_capture(
|
||||
run(fs::path("/usr/sbin/jail"), av));
|
||||
process::status status = child->wait();
|
||||
|
||||
// expect success
|
||||
if (status.exited() && status.exitstatus() == EXIT_SUCCESS)
|
||||
return;
|
||||
|
||||
// otherwise, let us know what jail thinks and fail fast
|
||||
std::cerr << child->output().rdbuf();
|
||||
std::exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
|
||||
/// Executes an external binary in a jail and replaces the current process.
|
||||
///
|
||||
/// \param jail_name Name of the jail to run within.
|
||||
/// \param program The test program binary absolute path.
|
||||
/// \param args The arguments to pass to the binary, without the program name.
|
||||
void
|
||||
jail::exec(const std::string& jail_name,
|
||||
const fs::path& program,
|
||||
const args_vector& args) throw()
|
||||
{
|
||||
// get work dir prepared by kyua
|
||||
char cwd[PATH_MAX];
|
||||
if (::getcwd(cwd, sizeof(cwd)) == NULL) {
|
||||
std::cerr << "jail::exec: getcwd() errors: "
|
||||
<< strerror(errno) << ".\n";
|
||||
std::exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// get jail id by its name
|
||||
int jid = ::jail_getid(jail_name.c_str());
|
||||
if (jid == -1) {
|
||||
std::cerr << "jail::exec: jail_getid() errors: "
|
||||
<< strerror(errno) << ": " << jail_errmsg << ".\n";
|
||||
std::exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// attach to the jail
|
||||
if (::jail_attach(jid) == -1) {
|
||||
std::cerr << "jail::exec: jail_attach() errors: "
|
||||
<< strerror(errno) << ".\n";
|
||||
std::exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// set back the expected work dir
|
||||
if (::chdir(cwd) == -1) {
|
||||
std::cerr << "jail::exec: chdir() errors: "
|
||||
<< strerror(errno) << ".\n";
|
||||
std::exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
process::exec(program, args);
|
||||
}
|
||||
|
||||
|
||||
/// Removes a jail with a given name.
|
||||
///
|
||||
/// It's expected to be run in a subprocess.
|
||||
///
|
||||
/// \param jail_name Name of a jail to remove.
|
||||
void
|
||||
jail::remove(const std::string& jail_name)
|
||||
{
|
||||
args_vector av;
|
||||
|
||||
// removal flag
|
||||
av.push_back("-r");
|
||||
|
||||
// jail name
|
||||
av.push_back(jail_name);
|
||||
|
||||
// invoke jail
|
||||
std::auto_ptr< process::child > child = child::fork_capture(
|
||||
run(fs::path("/usr/sbin/jail"), av));
|
||||
process::status status = child->wait();
|
||||
|
||||
// expect success
|
||||
if (status.exited() && status.exitstatus() == EXIT_SUCCESS)
|
||||
std::exit(EXIT_SUCCESS);
|
||||
|
||||
// otherwise, let us know what jail thinks and fail fast
|
||||
std::cerr << child->output().rdbuf();
|
||||
std::exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
|
||||
} // namespace utils
|
||||
} // namespace freebsd
|
64
contrib/kyua/os/freebsd/utils/jail.hpp
Normal file
64
contrib/kyua/os/freebsd/utils/jail.hpp
Normal file
|
@ -0,0 +1,64 @@
|
|||
// 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 os/freebsd/utils/jail.hpp
|
||||
/// FreeBSD jail utilities.
|
||||
|
||||
#if !defined(FREEBSD_UTILS_JAIL_HPP)
|
||||
#define FREEBSD_UTILS_JAIL_HPP
|
||||
|
||||
#include "utils/defs.hpp"
|
||||
#include "utils/fs/path_fwd.hpp"
|
||||
#include "utils/process/operations_fwd.hpp"
|
||||
|
||||
namespace fs = utils::fs;
|
||||
|
||||
using utils::process::args_vector;
|
||||
|
||||
namespace freebsd {
|
||||
namespace utils {
|
||||
|
||||
|
||||
class jail {
|
||||
public:
|
||||
std::vector< std::string > parse_params_string(const std::string& str);
|
||||
std::string make_name(const fs::path& program,
|
||||
const std::string& test_case_name);
|
||||
void create(const std::string& jail_name,
|
||||
const std::string& jail_params);
|
||||
void exec(const std::string& jail_name,
|
||||
const fs::path& program,
|
||||
const args_vector& args) throw() UTILS_NORETURN;
|
||||
void remove(const std::string& jail_name);
|
||||
};
|
||||
|
||||
|
||||
} // namespace utils
|
||||
} // namespace freebsd
|
||||
|
||||
#endif // !defined(FREEBSD_UTILS_JAIL_HPP)
|
|
@ -382,9 +382,14 @@ config::base_set_node< ValueType >::push_lua(lutok::state& /* state */) const
|
|||
template< typename ValueType >
|
||||
void
|
||||
config::base_set_node< ValueType >::set_lua(
|
||||
lutok::state& /* state */,
|
||||
const int /* value_index */)
|
||||
lutok::state& state,
|
||||
const int value_index)
|
||||
{
|
||||
if (state.is_string(value_index)) {
|
||||
set_string(state.to_string(value_index));
|
||||
return;
|
||||
}
|
||||
|
||||
UNREACHABLE;
|
||||
}
|
||||
|
||||
|
|
|
@ -689,6 +689,34 @@ struct utils::process::executor::executor_handle::impl : utils::noncopyable {
|
|||
data._pimpl->state_owners,
|
||||
all_exec_handles)));
|
||||
}
|
||||
|
||||
executor::exit_handle
|
||||
reap(const pid_t original_pid)
|
||||
{
|
||||
const exec_handles_map::iterator iter = all_exec_handles.find(
|
||||
original_pid);
|
||||
exec_handle& data = (*iter).second;
|
||||
data._pimpl->timer.unprogram();
|
||||
|
||||
if (!fs::exists(data.stdout_file())) {
|
||||
std::ofstream new_stdout(data.stdout_file().c_str());
|
||||
}
|
||||
if (!fs::exists(data.stderr_file())) {
|
||||
std::ofstream new_stderr(data.stderr_file().c_str());
|
||||
}
|
||||
|
||||
return exit_handle(std::shared_ptr< exit_handle::impl >(
|
||||
new exit_handle::impl(
|
||||
data.pid(),
|
||||
none,
|
||||
data._pimpl->unprivileged_user,
|
||||
data._pimpl->start_time, datetime::timestamp::now(),
|
||||
data.control_directory(),
|
||||
data.stdout_file(),
|
||||
data.stderr_file(),
|
||||
data._pimpl->state_owners,
|
||||
all_exec_handles)));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
@ -879,6 +907,20 @@ executor::executor_handle::wait_any(void)
|
|||
}
|
||||
|
||||
|
||||
/// Forms exit_handle for the given PID subprocess.
|
||||
///
|
||||
/// Can be used in the cases when we want to do cleanup(s) of a killed test
|
||||
/// subprocess, but we do not have exit handle as we usually do after normal
|
||||
/// wait mechanism.
|
||||
///
|
||||
/// \return A pointer to an object describing the subprocess.
|
||||
executor::exit_handle
|
||||
executor::executor_handle::reap(const int pid)
|
||||
{
|
||||
return _pimpl->reap(pid);
|
||||
}
|
||||
|
||||
|
||||
/// Checks if an interrupt has fired.
|
||||
///
|
||||
/// Calls to this function should be sprinkled in strategic places through the
|
||||
|
|
|
@ -215,6 +215,7 @@ class executor_handle {
|
|||
|
||||
exit_handle wait(const exec_handle);
|
||||
exit_handle wait_any(void);
|
||||
exit_handle reap(const pid_t);
|
||||
|
||||
void check_interrupt(void) const;
|
||||
};
|
||||
|
|
|
@ -127,7 +127,12 @@ SRCS+= engine/atf.cpp \
|
|||
engine/scanner.cpp \
|
||||
engine/tap.cpp \
|
||||
engine/tap_parser.cpp \
|
||||
engine/scheduler.cpp
|
||||
engine/scheduler.cpp \
|
||||
engine/execenv/execenv.cpp \
|
||||
engine/execenv/execenv_host.cpp
|
||||
|
||||
SRCS+= os/freebsd/execenv_jail_manager.cpp \
|
||||
os/freebsd/main.cpp
|
||||
|
||||
SRCS+= store/dbtypes.cpp \
|
||||
store/exceptions.cpp \
|
||||
|
@ -160,6 +165,14 @@ SRCS+= cli/cmd_about.cpp \
|
|||
cli/config.cpp \
|
||||
cli/main.cpp
|
||||
|
||||
.if ${MK_JAIL} == "no"
|
||||
SRCS+= os/freebsd/execenv_jail_stub.cpp
|
||||
.else
|
||||
SRCS+= os/freebsd/execenv_jail.cpp \
|
||||
os/freebsd/utils/jail.cpp
|
||||
LIBADD+= jail
|
||||
.endif
|
||||
|
||||
FILESGROUPS= DOCS MISC STORE
|
||||
|
||||
.if ${MK_EXAMPLES} != "no"
|
||||
|
|
Loading…
Reference in a new issue