-
Notifications
You must be signed in to change notification settings - Fork 42
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add FreeBSD Jail execution environment support
Two new test case metadata are introduced: - execenv: it can be "host" by default or "jail" - execenv_jail: additional jail creation parameters to jail(8) Kyua implicitly adds "children.max" to execenv_jail parameters with the maximum possible value. A test case can override it. If a test is marked with is_exclusive="true" and execenv="jail" then exclusive flag will be ignored and such test will be run in parallel if possible. A test case lifecycle is extended: - 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")
- Loading branch information
Showing
16 changed files
with
1,118 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
// 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/jail.hpp" | ||
#include "model/metadata.hpp" | ||
#include "model/test_case.hpp" | ||
#include "model/test_program.hpp" | ||
#include "utils/fs/path.hpp" | ||
#include "utils/process/operations.hpp" | ||
|
||
namespace execenv = engine::execenv; | ||
namespace process = utils::process; | ||
|
||
using utils::process::args_vector; | ||
|
||
|
||
/// Initialize execution environment. | ||
/// | ||
/// It's expected to be called inside a fork which runs 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. | ||
/// | ||
/// \param program The test program binary absolute path. | ||
/// \param test_case_name Name of the test case. | ||
void | ||
execenv::init(const model::test_program& test_program, | ||
const std::string& test_case_name) | ||
{ | ||
const model::test_case& test_case = test_program.find(test_case_name); | ||
|
||
if (test_case.get_metadata().is_execenv_jail()) { | ||
return execenv::jail::init(test_program, test_case_name); | ||
} else { | ||
// host environment by default | ||
return; | ||
} | ||
} | ||
|
||
|
||
/// Execute within an execution environment. | ||
/// | ||
/// It's expected to be called inside a fork which runs interface::exec_test(). | ||
/// | ||
/// \param program The test program binary absolute path. | ||
/// \param test_case_name Name of the test case. | ||
void | ||
execenv::exec(const model::test_program& test_program, | ||
const std::string& test_case_name, | ||
const args_vector& args) throw() | ||
{ | ||
const model::test_case& test_case = test_program.find(test_case_name); | ||
|
||
if (test_case.get_metadata().is_execenv_jail()) { | ||
execenv::jail::exec(test_program, test_case_name, args); | ||
} else { | ||
// host environment by default | ||
process::exec(test_program.absolute_path(), args); | ||
} | ||
} | ||
|
||
|
||
/// Cleanup execution environment. | ||
/// | ||
/// It's expected to be called inside a fork for execenv cleanup. | ||
/// | ||
/// \param program The test program binary absolute path. | ||
/// \param test_case_name Name of the test case. | ||
void | ||
execenv::cleanup(const model::test_program& test_program, | ||
const std::string& test_case_name) | ||
{ | ||
const model::test_case& test_case = test_program.find(test_case_name); | ||
|
||
if (test_case.get_metadata().is_execenv_jail()) { | ||
return execenv::jail::cleanup(test_program, test_case_name); | ||
} else { | ||
// cleanup is not expected to be called for host environment | ||
std::exit(EXIT_SUCCESS); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
// 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 multiplexer. | ||
/// | ||
/// A test case may ask for a specific execution environment like running in | ||
/// a jail, what needs initialization before the test run and cleanup after. | ||
/// | ||
/// By default, there is no specific execution environment, so called host | ||
/// environment, and no additional initialization or cleanup is done. | ||
|
||
#if !defined(ENGINE_EXECENV_EXECENV_HPP) | ||
#define ENGINE_EXECENV_EXECENV_HPP | ||
|
||
#include "model/test_program.hpp" | ||
#include "utils/defs.hpp" | ||
#include "utils/process/operations_fwd.hpp" | ||
|
||
namespace engine { | ||
namespace execenv { | ||
|
||
|
||
void init(const model::test_program&, const std::string&); | ||
|
||
void exec(const model::test_program&, const std::string&, | ||
const utils::process::args_vector&) throw() UTILS_NORETURN; | ||
|
||
void cleanup(const model::test_program&, const std::string&); | ||
|
||
|
||
} // namespace execenv | ||
} // namespace engine | ||
|
||
#endif // !defined(ENGINE_EXECENV_EXECENV_HPP) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
// 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/jail.hpp" | ||
|
||
extern "C" { | ||
// FreeBSD Jail | ||
#include <sys/param.h> | ||
} | ||
|
||
#include <regex> | ||
|
||
#include "model/metadata.hpp" | ||
#include "model/test_case.hpp" | ||
#include "model/test_program.hpp" | ||
#include "utils/fs/path.hpp" | ||
#include "utils/process/jail.hpp" | ||
#include "utils/process/operations.hpp" | ||
|
||
namespace execenv = engine::execenv; | ||
namespace process = utils::process; | ||
namespace fs = utils::fs; | ||
|
||
using utils::process::args_vector; | ||
|
||
|
||
namespace { | ||
|
||
|
||
static const int jail_name_max_len = MAXHOSTNAMELEN - 1; | ||
static const char* jail_name_prefix = "kyua"; | ||
|
||
/// 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. | ||
static std::string | ||
make_jail_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; | ||
} | ||
|
||
|
||
} // anonymous namespace | ||
|
||
|
||
/// Initialize execution environment. | ||
/// | ||
/// It's expected to be called inside a fork which runs interface::exec_test(), | ||
/// so we can fail a test fast if its execution environment setup fails. | ||
/// | ||
/// \param program The test program. | ||
/// \param test_case_name Name of the test case. | ||
void | ||
execenv::jail::init(const model::test_program& test_program, | ||
const std::string& test_case_name) | ||
{ | ||
const model::test_case& test_case = test_program.find(test_case_name); | ||
|
||
process::jail::create( | ||
make_jail_name(test_program.absolute_path(), test_case_name), | ||
test_case.get_metadata().execenv_jail()); | ||
} | ||
|
||
|
||
/// Execute within an execution environment. | ||
/// | ||
/// It's expected to be called inside a fork which runs interface::exec_test(). | ||
/// | ||
/// \param program The test program. | ||
/// \param test_case_name Name of the test case. | ||
/// \param args The arguments to pass to the binary, without the program name. | ||
void | ||
execenv::jail::exec(const model::test_program& test_program, | ||
const std::string& test_case_name, | ||
const args_vector& args) throw() | ||
{ | ||
process::jail::exec( | ||
make_jail_name(test_program.absolute_path(), test_case_name), | ||
test_program.absolute_path(), args); | ||
} | ||
|
||
|
||
/// Cleanup execution environment. | ||
/// | ||
/// It's expected to be called inside a fork for execenv cleanup. | ||
/// | ||
/// \param program The test program. | ||
/// \param test_case_name Name of the test case. | ||
void | ||
execenv::jail::cleanup(const model::test_program& test_program, | ||
const std::string& test_case_name) | ||
{ | ||
process::jail::remove( | ||
make_jail_name(test_program.absolute_path(), test_case_name)); | ||
} |
Oops, something went wrong.