Page MenuHomeFreeBSD

D42350.diff
No OneTemporary

D42350.diff

diff --git a/contrib/kyua/Makefile.am b/contrib/kyua/Makefile.am
--- a/contrib/kyua/Makefile.am
+++ b/contrib/kyua/Makefile.am
@@ -59,6 +59,7 @@
include drivers/Makefile.am.inc
include engine/Makefile.am.inc
include examples/Makefile.am.inc
+include freebsd/Makefile.am.inc
include integration/Makefile.am.inc
include misc/Makefile.am.inc
include model/Makefile.am.inc
@@ -68,7 +69,7 @@
bin_PROGRAMS = kyua
kyua_SOURCES = main.cpp
kyua_CXXFLAGS = $(CLI_CFLAGS) $(ENGINE_CFLAGS) $(UTILS_CFLAGS)
-kyua_LDADD = $(CLI_LIBS) $(ENGINE_LIBS) $(UTILS_LIBS)
+kyua_LDADD = $(CLI_LIBS) $(ENGINE_LIBS) $(FREEBSD_LIBS) $(UTILS_LIBS)
CHECK_ENVIRONMENT = KYUA_CONFDIR="/non-existent" \
KYUA_DOCDIR="$(abs_top_srcdir)" \
diff --git a/contrib/kyua/cli/cmd_config_test.cpp b/contrib/kyua/cli/cmd_config_test.cpp
--- a/contrib/kyua/cli/cmd_config_test.cpp
+++ b/contrib/kyua/cli/cmd_config_test.cpp
@@ -61,6 +61,7 @@
{
config::tree user_config = engine::default_config();
user_config.set_string("architecture", "the-architecture");
+ user_config.set_string("execenv", "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 @@
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("execenv = 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());
}
diff --git a/contrib/kyua/configure.ac b/contrib/kyua/configure.ac
--- a/contrib/kyua/configure.ac
+++ b/contrib/kyua/configure.ac
@@ -169,5 +169,6 @@
AM_CONDITIONAL(TARGET_SRCDIR_EMPTY, [test -z "${target_srcdir}"])
AC_SUBST([target_srcdir])
+AM_CONDITIONAL([FreeBSD], [test "$(uname -o)" = "FreeBSD"])
AC_OUTPUT
diff --git a/contrib/kyua/doc/kyua.conf.5.in b/contrib/kyua/doc/kyua.conf.5.in
--- a/contrib/kyua/doc/kyua.conf.5.in
+++ b/contrib/kyua/doc/kyua.conf.5.in
@@ -1,4 +1,4 @@
-.\" Copyright 2012 The Kyua Authors.
+.\" Copyright 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 execenv ,
.Va platform ,
.Va test_suites ,
.Va unprivileged_user .
@@ -72,6 +73,10 @@
.Bl -tag -width XX -offset indent
.It Va architecture
Name of the system architecture (aka processor type).
+.It Va execenv
+Whitespace-separated list of execution environment names.
+.Pp
+Only tests which require one of the given execution environments will be run.
.It Va parallelism
Maximum number of test cases to execute concurrently.
.It Va platform
diff --git a/contrib/kyua/doc/kyuafile.5.in b/contrib/kyua/doc/kyuafile.5.in
--- a/contrib/kyua/doc/kyuafile.5.in
+++ b/contrib/kyua/doc/kyuafile.5.in
@@ -1,4 +1,4 @@
-.\" Copyright 2012 The Kyua Authors.
+.\" Copyright 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,38 @@
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
+jail environment.
+It creates a temporary jail to run the test and its optional cleanup logic
+within.
+.El
+.It Va execenv_jail
+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
+Kyua implicitly adds
+.Sq children.max
+.Xr jail 8
+parameter for a temporary jail with the maximum possible value according to
+the jail Kyua itself is running within.
+It allows tests to easily spawn their own sub-jails without additional
+configuration.
+It can be overridden via execenv_jail 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 +392,19 @@
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
+created by Kyua with VNET 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='vnet allow.raw_sockets',
+ required_user='root'}
+.Ed
.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
diff --git a/contrib/kyua/drivers/report_junit_test.cpp b/contrib/kyua/drivers/report_junit_test.cpp
--- a/contrib/kyua/drivers/report_junit_test.cpp
+++ b/contrib/kyua/drivers/report_junit_test.cpp
@@ -63,6 +63,8 @@
"allowed_architectures is empty\n"
"allowed_platforms is empty\n"
"description is empty\n"
+ "execenv is empty\n"
+ "execenv_jail is empty\n"
"has_cleanup = false\n"
"is_exclusive = false\n"
"required_configs is empty\n"
@@ -80,6 +82,8 @@
"allowed_architectures is empty\n"
"allowed_platforms is empty\n"
"description = Textual description\n"
+ "execenv is empty\n"
+ "execenv_jail is empty\n"
"has_cleanup = false\n"
"is_exclusive = false\n"
"required_configs is empty\n"
@@ -199,6 +203,8 @@
.add_allowed_architecture("arch1")
.add_allowed_platform("platform1")
.set_description("This is a test")
+ .set_execenv("jail")
+ .set_execenv_jail("vnet")
.set_has_cleanup(true)
.set_is_exclusive(true)
.add_required_config("config1")
@@ -215,6 +221,8 @@
+ "allowed_architectures = arch1\n"
+ "allowed_platforms = platform1\n"
+ "description = This is a test\n"
+ + "execenv = jail\n"
+ + "execenv_jail = vnet\n"
+ "has_cleanup = true\n"
+ "is_exclusive = true\n"
+ "required_configs = config1\n"
diff --git a/contrib/kyua/engine/Makefile.am.inc b/contrib/kyua/engine/Makefile.am.inc
--- a/contrib/kyua/engine/Makefile.am.inc
+++ b/contrib/kyua/engine/Makefile.am.inc
@@ -153,3 +153,5 @@
engine_scheduler_test_CXXFLAGS = $(ENGINE_CFLAGS) $(ATF_CXX_CFLAGS)
engine_scheduler_test_LDADD = $(ENGINE_LIBS) $(ATF_CXX_LIBS)
endif
+
+include engine/execenv/Makefile.am.inc
diff --git a/contrib/kyua/engine/atf.cpp b/contrib/kyua/engine/atf.cpp
--- a/contrib/kyua/engine/atf.cpp
+++ b/contrib/kyua/engine/atf.cpp
@@ -39,6 +39,7 @@
#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 @@
#include "utils/stream.hpp"
namespace config = utils::config;
+namespace execenv = engine::execenv;
namespace fs = utils::fs;
namespace process = utils::process;
@@ -190,7 +192,10 @@
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 @@
}
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);
}
diff --git a/contrib/kyua/engine/atf_list.cpp b/contrib/kyua/engine/atf_list.cpp
--- a/contrib/kyua/engine/atf_list.cpp
+++ b/contrib/kyua/engine/atf_list.cpp
@@ -121,6 +121,10 @@
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") {
+ mdbuilder.set_string("execenv_jail", value);
} else if (name == "require.config") {
mdbuilder.set_string("required_configs", value);
} else if (name == "require.files") {
diff --git a/contrib/kyua/engine/config.cpp b/contrib/kyua/engine/config.cpp
--- a/contrib/kyua/engine/config.cpp
+++ b/contrib/kyua/engine/config.cpp
@@ -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 @@
init_tree(config::tree& tree)
{
tree.define< config::string_node >("architecture");
+ tree.define< config::strings_set_node >("execenv");
tree.define< config::positive_int_node >("parallelism");
tree.define< config::string_node >("platform");
tree.define< engine::user_node >("unprivileged_user");
@@ -74,6 +77,14 @@
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("host");
+ tree.set< config::strings_set_node >("execenv", 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 @@
{
config::tree tree(false);
init_tree(tree);
+
+ // Tests of Kyua itself tend to use an empty config, and they want
+ // to allow running usual host based test cases.
+ std::set< std::string > supported;
+ supported.insert("host");
+ tree.set< config::strings_set_node >("execenv", supported);
+
return tree;
}
diff --git a/contrib/kyua/engine/execenv/Makefile.am.inc b/contrib/kyua/engine/execenv/Makefile.am.inc
new file mode 100644
--- /dev/null
+++ b/contrib/kyua/engine/execenv/Makefile.am.inc
@@ -0,0 +1,32 @@
+# 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.
+
+libengine_a_SOURCES += engine/execenv/execenv.hpp
+libengine_a_SOURCES += engine/execenv/execenv.cpp
+libengine_a_SOURCES += engine/execenv/execenv_host.hpp
+libengine_a_SOURCES += engine/execenv/execenv_host.cpp
diff --git a/contrib/kyua/engine/execenv/execenv.hpp b/contrib/kyua/engine/execenv/execenv.hpp
new file mode 100644
--- /dev/null
+++ b/contrib/kyua/engine/execenv/execenv.hpp
@@ -0,0 +1,144 @@
+// Copyright (c) 2023 Igor Ostapenko <pm@igoro.pro>
+//
+// 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 {
+
+/// 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)
diff --git a/contrib/kyua/engine/execenv/execenv.cpp b/contrib/kyua/engine/execenv/execenv.cpp
new file mode 100644
--- /dev/null
+++ b/contrib/kyua/engine/execenv/execenv.cpp
@@ -0,0 +1,70 @@
+// Copyright (c) 2023 Igor Ostapenko <pm@igoro.pro>
+//
+// 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;
+
+
+/// 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));
+}
diff --git a/contrib/kyua/engine/execenv/execenv_host.hpp b/contrib/kyua/engine/execenv/execenv_host.hpp
new file mode 100644
--- /dev/null
+++ b/contrib/kyua/engine/execenv/execenv_host.hpp
@@ -0,0 +1,62 @@
+// Copyright (c) 2024 Igor Ostapenko <pm@igoro.pro>
+//
+// 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)
diff --git a/contrib/kyua/engine/execenv/execenv_host.cpp b/contrib/kyua/engine/execenv/execenv_host.cpp
new file mode 100644
--- /dev/null
+++ b/contrib/kyua/engine/execenv/execenv_host.cpp
@@ -0,0 +1,51 @@
+// Copyright (c) 2024 Igor Ostapenko <pm@igoro.pro>
+//
+// 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);
+}
diff --git a/contrib/kyua/engine/plain.cpp b/contrib/kyua/engine/plain.cpp
--- a/contrib/kyua/engine/plain.cpp
+++ b/contrib/kyua/engine/plain.cpp
@@ -34,6 +34,7 @@
#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 @@
#include "utils/sanity.hpp"
namespace config = utils::config;
+namespace execenv = engine::execenv;
namespace fs = utils::fs;
namespace process = utils::process;
@@ -104,7 +106,10 @@
}
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);
}
diff --git a/contrib/kyua/engine/requirements.cpp b/contrib/kyua/engine/requirements.cpp
--- a/contrib/kyua/engine/requirements.cpp
+++ b/contrib/kyua/engine/requirements.cpp
@@ -100,6 +100,34 @@
}
+/// 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 = "host"; // if a test claims nothing then it's host based
+
+ std::set< std::string > execenvs;
+ try {
+ execenvs = user_config.lookup< config::strings_set_node >("execenv");
+ } 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 +291,10 @@
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;
diff --git a/contrib/kyua/engine/scheduler.hpp b/contrib/kyua/engine/scheduler.hpp
--- a/contrib/kyua/engine/scheduler.hpp
+++ b/contrib/kyua/engine/scheduler.hpp
@@ -262,6 +262,7 @@
extern utils::datetime::delta cleanup_timeout;
+extern utils::datetime::delta execenv_cleanup_timeout;
extern utils::datetime::delta list_timeout;
diff --git a/contrib/kyua/engine/scheduler.cpp b/contrib/kyua/engine/scheduler.cpp
--- a/contrib/kyua/engine/scheduler.cpp
+++ b/contrib/kyua/engine/scheduler.cpp
@@ -40,6 +40,7 @@
#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 @@
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 @@
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 @@
/// 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.
+ int 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 @@
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 int 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 @@
};
+/// 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 @@
};
+/// 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 @@
% 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 @@
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 @@
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 @@
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 @@
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 @@
_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 @@
// 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 @@
_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 @@
// 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 @@
_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(
diff --git a/contrib/kyua/engine/tap.cpp b/contrib/kyua/engine/tap.cpp
--- a/contrib/kyua/engine/tap.cpp
+++ b/contrib/kyua/engine/tap.cpp
@@ -35,6 +35,7 @@
#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 @@
#include "utils/sanity.hpp"
namespace config = utils::config;
+namespace execenv = engine::execenv;
namespace fs = utils::fs;
namespace process = utils::process;
@@ -151,7 +153,10 @@
}
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);
}
diff --git a/contrib/kyua/examples/kyua.conf b/contrib/kyua/examples/kyua.conf
--- a/contrib/kyua/examples/kyua.conf
+++ b/contrib/kyua/examples/kyua.conf
@@ -43,6 +43,9 @@
-- Name of the system architecture (aka processor type).
architecture = "x86_64"
+-- List of execution environments.
+execenv = "host jail"
+
-- Maximum number of jobs (such as test case runs) to execute concurrently.
parallelism = 16
diff --git a/contrib/kyua/freebsd/Makefile.am.inc b/contrib/kyua/freebsd/Makefile.am.inc
new file mode 100644
--- /dev/null
+++ b/contrib/kyua/freebsd/Makefile.am.inc
@@ -0,0 +1,46 @@
+# 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.
+
+FREEBSD_CFLAGS =
+FREEBSD_LIBS = libfreebsd.a
+
+noinst_LIBRARIES += libfreebsd.a
+libfreebsd_a_CPPFLAGS = -DGDB=\"$(GDB)\"
+libfreebsd_a_SOURCES = freebsd/main.hpp
+libfreebsd_a_SOURCES += freebsd/main.cpp
+libfreebsd_a_SOURCES += freebsd/execenv_jail.hpp
+libfreebsd_a_SOURCES += freebsd/execenv_jail_manager.hpp
+libfreebsd_a_SOURCES += freebsd/execenv_jail_manager.cpp
+
+if FreeBSD
+FREEBSD_LIBS += -ljail
+libfreebsd_a_SOURCES += freebsd/execenv_jail.cpp
+include freebsd/utils/Makefile.am.inc
+else
+libfreebsd_a_SOURCES += freebsd/execenv_jail_stub.cpp
+endif
diff --git a/contrib/kyua/freebsd/execenv_jail.hpp b/contrib/kyua/freebsd/execenv_jail.hpp
new file mode 100644
--- /dev/null
+++ b/contrib/kyua/freebsd/execenv_jail.hpp
@@ -0,0 +1,64 @@
+// Copyright (c) 2024 Igor Ostapenko <pm@igoro.pro>
+//
+// 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 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)
diff --git a/contrib/kyua/freebsd/execenv_jail.cpp b/contrib/kyua/freebsd/execenv_jail.cpp
new file mode 100644
--- /dev/null
+++ b/contrib/kyua/freebsd/execenv_jail.cpp
@@ -0,0 +1,76 @@
+// Copyright (c) 2024 Igor Ostapenko <pm@igoro.pro>
+//
+// 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 "freebsd/execenv_jail.hpp"
+
+#include "freebsd/utils/jail.hpp"
+#include "model/metadata.hpp"
+#include "model/test_case.hpp"
+#include "utils/fs/path.hpp"
+
+using freebsd::utils::jail;
+
+
+namespace freebsd {
+
+
+bool execenv_jail_supported = true;
+
+
+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()
+ );
+}
+
+
+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
diff --git a/contrib/kyua/freebsd/execenv_jail_manager.hpp b/contrib/kyua/freebsd/execenv_jail_manager.hpp
new file mode 100644
--- /dev/null
+++ b/contrib/kyua/freebsd/execenv_jail_manager.hpp
@@ -0,0 +1,53 @@
+// Copyright (c) 2024 Igor Ostapenko <pm@igoro.pro>
+//
+// 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 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)
diff --git a/contrib/kyua/freebsd/execenv_jail_manager.cpp b/contrib/kyua/freebsd/execenv_jail_manager.cpp
new file mode 100644
--- /dev/null
+++ b/contrib/kyua/freebsd/execenv_jail_manager.cpp
@@ -0,0 +1,62 @@
+// Copyright (c) 2024 Igor Ostapenko <pm@igoro.pro>
+//
+// 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 "freebsd/execenv_jail_manager.hpp"
+
+#include "model/metadata.hpp"
+#include "model/test_case.hpp"
+#include "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)
+ );
+}
diff --git a/contrib/kyua/freebsd/execenv_jail_stub.cpp b/contrib/kyua/freebsd/execenv_jail_stub.cpp
new file mode 100644
--- /dev/null
+++ b/contrib/kyua/freebsd/execenv_jail_stub.cpp
@@ -0,0 +1,74 @@
+// Copyright (c) 2024 Igor Ostapenko <pm@igoro.pro>
+//
+// 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 "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
diff --git a/contrib/kyua/freebsd/main.hpp b/contrib/kyua/freebsd/main.hpp
new file mode 100644
--- /dev/null
+++ b/contrib/kyua/freebsd/main.hpp
@@ -0,0 +1,40 @@
+// Copyright (c) 2024 Igor Ostapenko <pm@igoro.pro>
+//
+// 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 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)
diff --git a/contrib/kyua/freebsd/main.cpp b/contrib/kyua/freebsd/main.cpp
new file mode 100644
--- /dev/null
+++ b/contrib/kyua/freebsd/main.cpp
@@ -0,0 +1,53 @@
+// Copyright (c) 2024 Igor Ostapenko <pm@igoro.pro>
+//
+// 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 "freebsd/main.hpp"
+
+#include "engine/execenv/execenv.hpp"
+#include "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;
+}
diff --git a/contrib/kyua/freebsd/utils/Makefile.am.inc b/contrib/kyua/freebsd/utils/Makefile.am.inc
new file mode 100644
--- /dev/null
+++ b/contrib/kyua/freebsd/utils/Makefile.am.inc
@@ -0,0 +1,30 @@
+# 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.
+
+libfreebsd_a_SOURCES += freebsd/utils/jail.hpp
+libfreebsd_a_SOURCES += freebsd/utils/jail.cpp
diff --git a/contrib/kyua/freebsd/utils/jail.hpp b/contrib/kyua/freebsd/utils/jail.hpp
new file mode 100644
--- /dev/null
+++ b/contrib/kyua/freebsd/utils/jail.hpp
@@ -0,0 +1,63 @@
+// Copyright (c) 2024 Igor Ostapenko <pm@igoro.pro>
+//
+// 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 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)
diff --git a/contrib/kyua/freebsd/utils/jail.cpp b/contrib/kyua/freebsd/utils/jail.cpp
new file mode 100644
--- /dev/null
+++ b/contrib/kyua/freebsd/utils/jail.cpp
@@ -0,0 +1,323 @@
+// Copyright (c) 2024 Igor Ostapenko <pm@igoro.pro>
+//
+// 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 "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 int 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;
+
+ for (const char& c : str) {
+ // whitespace delimited parameter
+ if (quote == 0) {
+ if (std::isspace(c)) {
+ if (p.empty())
+ continue;
+ params.push_back(p);
+ p = "";
+ }
+ else if (c == '"' || c == '\'') {
+ if (!p.empty())
+ params.push_back(p);
+ p = "";
+ quote = c;
+ }
+ else
+ p += c;
+ }
+
+ // quoted parameter
+ else {
+ if (c == quote) {
+ if (!p.empty())
+ params.push_back(p);
+ p = "";
+ quote = 0;
+ }
+ else
+ p += c;
+ }
+ }
+
+ // leftovers
+ if (!p.empty())
+ 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
+ int max;
+ size_t len = sizeof(max);
+ if (::sysctlbyname("security.jail.children.max", &max, &len, NULL, 0) != 0) {
+ std::cerr << "sysctlbyname(security.jail.children.max) errors: "
+ << strerror(errno) << ".\n";
+ std::exit(EXIT_FAILURE);
+ }
+ if (len < sizeof(max)) {
+ std::cerr << "sysctlbyname(security.jail.children.max) provides less "
+ "data (" << len << ") than expected (" << sizeof(max) << ").\n";
+ std::exit(EXIT_FAILURE);
+ }
+ if (max < 0) {
+ std::cerr << "sysctlbyname(security.jail.children.max) 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 < 0) {
+ 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
diff --git a/contrib/kyua/integration/cmd_config_test.sh b/contrib/kyua/integration/cmd_config_test.sh
--- a/contrib/kyua/integration/cmd_config_test.sh
+++ b/contrib/kyua/integration/cmd_config_test.sh
@@ -42,6 +42,7 @@
cat >"${HOME}/.kyua/kyua.conf" <<EOF
syntax(2)
architecture = "my-architecture"
+execenv = "my-env1 my-env2"
parallelism = 256
platform = "my-platform"
unprivileged_user = "$(id -u -n)"
@@ -51,6 +52,7 @@
cat >expout <<EOF
architecture = my-architecture
+execenv = my-env1 my-env2
parallelism = 256
platform = my-platform
test_suites.suite1.the_variable = value1
diff --git a/contrib/kyua/integration/cmd_report_junit_test.sh b/contrib/kyua/integration/cmd_report_junit_test.sh
--- a/contrib/kyua/integration/cmd_report_junit_test.sh
+++ b/contrib/kyua/integration/cmd_report_junit_test.sh
@@ -97,6 +97,8 @@
allowed_architectures is empty
allowed_platforms is empty
description is empty
+execenv is empty
+execenv_jail is empty
has_cleanup = false
is_exclusive = false
required_configs is empty
@@ -135,6 +137,8 @@
allowed_architectures is empty
allowed_platforms is empty
description is empty
+execenv is empty
+execenv_jail is empty
has_cleanup = false
is_exclusive = false
required_configs is empty
@@ -211,6 +215,8 @@
allowed_architectures is empty
allowed_platforms is empty
description is empty
+execenv is empty
+execenv_jail is empty
has_cleanup = false
is_exclusive = false
required_configs is empty
@@ -249,6 +255,8 @@
allowed_architectures is empty
allowed_platforms is empty
description is empty
+execenv is empty
+execenv_jail is empty
has_cleanup = false
is_exclusive = false
required_configs is empty
diff --git a/contrib/kyua/integration/cmd_report_test.sh b/contrib/kyua/integration/cmd_report_test.sh
--- a/contrib/kyua/integration/cmd_report_test.sh
+++ b/contrib/kyua/integration/cmd_report_test.sh
@@ -251,6 +251,8 @@
allowed_architectures is empty
allowed_platforms is empty
description is empty
+ execenv is empty
+ execenv_jail is empty
has_cleanup = false
is_exclusive = false
required_configs is empty
diff --git a/contrib/kyua/main.cpp b/contrib/kyua/main.cpp
--- a/contrib/kyua/main.cpp
+++ b/contrib/kyua/main.cpp
@@ -27,6 +27,7 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "cli/main.hpp"
+#include "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);
}
diff --git a/contrib/kyua/model/metadata.hpp b/contrib/kyua/model/metadata.hpp
--- a/contrib/kyua/model/metadata.hpp
+++ b/contrib/kyua/model/metadata.hpp
@@ -69,6 +69,9 @@
const std::string& description(void) const;
bool has_cleanup(void) const;
bool is_exclusive(void) const;
+ const std::string& execenv(void) const;
+ bool has_execenv(void) const;
+ const std::string& execenv_jail(void) const;
const strings_set& required_configs(void) const;
const utils::units::bytes& required_disk_space(void) const;
const paths_set& required_files(void) const;
@@ -112,6 +115,8 @@
metadata_builder& set_description(const std::string&);
metadata_builder& set_has_cleanup(const bool);
metadata_builder& set_is_exclusive(const bool);
+ metadata_builder& set_execenv(const std::string&);
+ metadata_builder& set_execenv_jail(const std::string&);
metadata_builder& set_required_configs(const strings_set&);
metadata_builder& set_required_disk_space(const utils::units::bytes&);
metadata_builder& set_required_files(const paths_set&);
diff --git a/contrib/kyua/model/metadata.cpp b/contrib/kyua/model/metadata.cpp
--- a/contrib/kyua/model/metadata.cpp
+++ b/contrib/kyua/model/metadata.cpp
@@ -249,6 +249,8 @@
tree.define< config::string_node >("description");
tree.define< config::bool_node >("has_cleanup");
tree.define< config::bool_node >("is_exclusive");
+ tree.define< config::string_node >("execenv");
+ tree.define< config::string_node >("execenv_jail");
tree.define< config::strings_set_node >("required_configs");
tree.define< bytes_node >("required_disk_space");
tree.define< paths_set_node >("required_files");
@@ -272,6 +274,8 @@
tree.set< config::string_node >("description", "");
tree.set< config::bool_node >("has_cleanup", false);
tree.set< config::bool_node >("is_exclusive", false);
+ tree.set< config::string_node >("execenv", "");
+ tree.set< config::string_node >("execenv_jail", "");
tree.set< config::strings_set_node >("required_configs",
model::strings_set());
tree.set< bytes_node >("required_disk_space", units::bytes(0));
@@ -493,6 +497,45 @@
}
+/// 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 whether the test has any specific execenv apart from "host" 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 != "host";
+}
+
+
+/// Returns execenv jail parameters string to run a test with.
+///
+/// \return String of jail parameters.
+const std::string&
+model::metadata::execenv_jail(void) const
+{
+ if (_pimpl->props.is_set("execenv_jail")) {
+ return _pimpl->props.lookup< config::string_node >("execenv_jail");
+ } else {
+ return get_defaults().lookup< config::string_node >("execenv_jail");
+ }
+}
+
+
/// Returns the list of configuration variables needed by the test.
///
/// \return Set of configuration variables.
@@ -920,6 +963,36 @@
}
+/// 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 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(const std::string& params)
+{
+ set< config::string_node >(_pimpl->props, "execenv_jail", params);
+ return *this;
+}
+
+
/// Sets the list of configuration variables needed by the test.
///
/// \param vars Set of configuration variables.
diff --git a/contrib/kyua/model/metadata_test.cpp b/contrib/kyua/model/metadata_test.cpp
--- a/contrib/kyua/model/metadata_test.cpp
+++ b/contrib/kyua/model/metadata_test.cpp
@@ -315,6 +315,8 @@
props["allowed_platforms"] = "";
props["custom.foo"] = "bar";
props["description"] = "";
+ props["execenv"] = "";
+ props["execenv_jail"] = "";
props["has_cleanup"] = "false";
props["is_exclusive"] = "false";
props["required_configs"] = "";
@@ -406,7 +408,8 @@
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='', "
+ "has_cleanup='false', is_exclusive='false', "
"required_configs='', "
"required_disk_space='0', required_files='', "
"required_memory='0', "
@@ -428,7 +431,8 @@
.build();
ATF_REQUIRE_EQ(
"metadata{allowed_architectures='abc', allowed_platforms='', "
- "description='', has_cleanup='false', is_exclusive='true', "
+ "description='', execenv='', execenv_jail='', "
+ "has_cleanup='false', is_exclusive='true', "
"required_configs='', "
"required_disk_space='0', required_files='bar foo', "
"required_memory='1.00K', "
diff --git a/contrib/kyua/model/test_case_test.cpp b/contrib/kyua/model/test_case_test.cpp
--- a/contrib/kyua/model/test_case_test.cpp
+++ b/contrib/kyua/model/test_case_test.cpp
@@ -200,7 +200,8 @@
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='', "
+ "has_cleanup='false', "
"is_exclusive='false', "
"required_configs='', required_disk_space='0', required_files='', "
"required_memory='0', "
diff --git a/contrib/kyua/model/test_program_test.cpp b/contrib/kyua/model/test_program_test.cpp
--- a/contrib/kyua/model/test_program_test.cpp
+++ b/contrib/kyua/model/test_program_test.cpp
@@ -544,7 +544,8 @@
"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='', "
+ "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 @@
"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='', "
+ "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='', "
+ "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='', "
+ "has_cleanup='false', is_exclusive='false', "
"required_configs='', required_disk_space='0', required_files='', "
"required_memory='0', "
"required_programs='', required_user='', timeout='300'}})}",
diff --git a/contrib/kyua/utils/config/nodes.ipp b/contrib/kyua/utils/config/nodes.ipp
--- a/contrib/kyua/utils/config/nodes.ipp
+++ b/contrib/kyua/utils/config/nodes.ipp
@@ -382,9 +382,14 @@
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;
}
diff --git a/contrib/kyua/utils/process/executor.hpp b/contrib/kyua/utils/process/executor.hpp
--- a/contrib/kyua/utils/process/executor.hpp
+++ b/contrib/kyua/utils/process/executor.hpp
@@ -215,6 +215,7 @@
exit_handle wait(const exec_handle);
exit_handle wait_any(void);
+ exit_handle reap(const int);
void check_interrupt(void) const;
};
diff --git a/contrib/kyua/utils/process/executor.cpp b/contrib/kyua/utils/process/executor.cpp
--- a/contrib/kyua/utils/process/executor.cpp
+++ b/contrib/kyua/utils/process/executor.cpp
@@ -689,6 +689,34 @@
data._pimpl->state_owners,
all_exec_handles)));
}
+
+ executor::exit_handle
+ reap(const int 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 @@
}
+/// 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
diff --git a/usr.bin/kyua/Makefile b/usr.bin/kyua/Makefile
--- a/usr.bin/kyua/Makefile
+++ b/usr.bin/kyua/Makefile
@@ -128,7 +128,12 @@
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+= freebsd/execenv_jail_manager.cpp\
+ freebsd/main.cpp
SRCS+= store/dbtypes.cpp \
store/exceptions.cpp \
@@ -161,6 +166,14 @@
cli/config.cpp \
cli/main.cpp
+.if ${MK_JAIL} == "no"
+SRCS+= freebsd/execenv_jail_stub.cpp
+.else
+SRCS+= freebsd/execenv_jail.cpp \
+ freebsd/utils/jail.cpp
+LIBADD+= jail
+.endif
+
FILESGROUPS= DOCS MISC STORE
.if ${MK_EXAMPLES} != "no"

File Metadata

Mime Type
text/plain
Expires
Mon, Nov 18, 6:22 AM (21 h, 53 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
14691174
Default Alt Text
D42350.diff (93 KB)

Event Timeline