2
0
mirror of https://gitlab.isc.org/isc-projects/kea synced 2025-08-31 14:05:33 +00:00

[#3025] add ability to inherit env in ProcessSpawn

This commit is contained in:
Andrei Pavel
2024-02-13 12:59:44 +02:00
parent 4a5bd3c9e0
commit 253eadd878
6 changed files with 137 additions and 34 deletions

View File

@@ -9,10 +9,12 @@
#include <asiolink/io_service_signal.h>
#include <asiolink/process_spawn.h>
#include <exceptions/exceptions.h>
#include <cstring>
#include <functional>
#include <map>
#include <mutex>
#include <cstring>
#include <signal.h>
#include <stdlib.h>
#include <errno.h>
@@ -25,6 +27,8 @@
using namespace std;
namespace ph = std::placeholders;
extern char **environ;
namespace isc {
namespace asiolink {
@@ -77,10 +81,13 @@ public:
/// @param executable A full path to the program to be executed.
/// @param args Arguments for the program to be executed.
/// @param vars Environment variables for the program to be executed.
/// @param inherit_env whether the spawned process will inherit the
/// environment before adding 'vars' on top.
ProcessSpawnImpl(IOServicePtr io_service,
const std::string& executable,
const ProcessArgs& args,
const ProcessEnvVars& vars);
const ProcessEnvVars& vars,
const bool inherit_env);
/// @brief Destructor.
~ProcessSpawnImpl();
@@ -238,9 +245,24 @@ void ProcessSpawnImpl::IOSignalSetInitializer::initIOSignalSet(IOServicePtr io_s
ProcessSpawnImpl::ProcessSpawnImpl(IOServicePtr io_service,
const std::string& executable,
const ProcessArgs& args,
const ProcessEnvVars& vars)
const ProcessEnvVars& vars,
const bool inherit_env)
: executable_(executable), args_(new char*[args.size() + 2]),
vars_(new char*[vars.size() + 1]), store_(false), io_service_(io_service) {
store_(false), io_service_(io_service) {
// Size of vars except the trailing null
size_t vars_size;
if (inherit_env) {
size_t i(0);
while (environ[i]) {
++i;
}
vars_size = i + vars.size();
} else {
vars_size = vars.size();
}
vars_ = boost::shared_ptr<char*[]>(new char*[vars_size + 1]);
struct stat st;
@@ -256,16 +278,23 @@ ProcessSpawnImpl::ProcessSpawnImpl(IOServicePtr io_service,
// all pointers within an array to NULL to indicate that they haven't
// been allocated yet.
memset(args_.get(), 0, (args.size() + 2) * sizeof(char*));
memset(vars_.get(), 0, (vars.size() + 1) * sizeof(char*));
memset(vars_.get(), 0, (vars_size + 1) * sizeof(char*));
// By convention, the first argument points to an executable name.
args_[0] = allocateInternal(executable_);
// Copy arguments to the array.
for (int i = 1; i <= args.size(); ++i) {
for (size_t i = 1; i <= args.size(); ++i) {
args_[i] = allocateInternal(args[i - 1]);
}
// Copy environment variables to the array.
for (int i = 0; i < vars.size(); ++i) {
vars_[i] = allocateInternal(vars[i]);
size_t i(0);
if (inherit_env) {
while (environ[i]) {
vars_[i] = allocateInternal(environ[i]);
++i;
}
}
for (size_t j = 0; j < vars.size(); ++j) {
vars_[i + j] = allocateInternal(vars[j]);
}
}
@@ -410,8 +439,9 @@ ProcessSpawnImpl::clearState(const pid_t pid) {
ProcessSpawn::ProcessSpawn(IOServicePtr io_service,
const std::string& executable,
const ProcessArgs& args,
const ProcessEnvVars& vars)
: impl_(new ProcessSpawnImpl(io_service, executable, args, vars)) {
const ProcessEnvVars& vars,
const bool inherit_env /* = false */)
: impl_(new ProcessSpawnImpl(io_service, executable, args, vars, inherit_env)) {
}
std::string

View File

@@ -67,10 +67,13 @@ public:
/// @param executable A full path to the program to be executed.
/// @param args Arguments for the program to be executed.
/// @param vars Environment variables for the program to be executed.
/// @param inherit_env whether the spawned process will inherit the
/// environment before adding 'vars' on top.
ProcessSpawn(isc::asiolink::IOServicePtr io_service,
const std::string& executable,
const ProcessArgs& args = ProcessArgs(),
const ProcessEnvVars& vars = ProcessEnvVars());
const ProcessEnvVars& vars = ProcessEnvVars(),
const bool inherit_env = false);
/// @brief Destructor.
~ProcessSpawn() = default;

View File

@@ -28,48 +28,45 @@
# used.
set -eu
exit_code=
exit_code=0
while test "${#}" -gt 0
do
# The exit code of 32 is returned when no args specified.
if test "${#}" = 0; then
exit_code=32
fi
while test "${#}" -gt 0; do
option=${1}
shift
case ${option} in
-p)
exit_code=${$}
;;
-e)
shift
exit_code=${1-}
shift
;;
-s)
sleep "${1-0}"
exit_code=12
shift
sleep "${1}"
;;
-v)
shift
VAR_NAME=${1}
shift
VAR_VALUE=${1}
EXPECTED=$(env | grep -E "^${VAR_NAME}=")
shift
EXPECTED=$(env | grep -E "^${VAR_NAME}=" || true)
if ! test "${VAR_NAME}=${VAR_VALUE}" = "${EXPECTED}"; then
exit 123
exit_code=34
else
exit_code=56
fi
;;
*)
exit 123
exit_code=78
;;
esac
# We've shifted in the loop so we may have run out of parameters in the
# meantime. Check again.
if test "${#}" -gt 0; then
shift
fi
done
# The exit code of 32 is returned when no args specified or
# when only the -s arg has been specified.
if [ -z "${exit_code}" ]; then
exit 32
fi
exit "${exit_code}"

View File

@@ -128,6 +128,7 @@ TEST_F(ProcessSpawnTest, spawnWithArgs) {
ASSERT_EQ(1, processed_signals_.size());
ASSERT_EQ(SIGCHLD, processed_signals_[0]);
// Exit code 64 as requested.
EXPECT_EQ(64, process.getExitStatus(pid));
}
@@ -161,7 +162,8 @@ TEST_F(ProcessSpawnTest, spawnWithArgsAndEnvVars) {
ASSERT_EQ(1, processed_signals_.size());
ASSERT_EQ(SIGCHLD, processed_signals_[0]);
EXPECT_EQ(32, process.getExitStatus(pid));
// 56 means successful comparison of env vars.
EXPECT_EQ(56, process.getExitStatus(pid));
}
// This test verifies that the single ProcessSpawn object can be used
@@ -245,6 +247,7 @@ TEST_F(ProcessSpawnTest, spawnNoArgs) {
ASSERT_EQ(1, processed_signals_.size());
ASSERT_EQ(SIGCHLD, processed_signals_[0]);
// 32 means no args.
EXPECT_EQ(32, process.getExitStatus(pid));
ASSERT_NO_THROW(pid = process.spawn(true));
@@ -347,4 +350,71 @@ TEST_F(ProcessSpawnTest, isRunning) {
ASSERT_EQ(SIGCHLD, processed_signals_[0]);
}
// This test verifies inheritance of environment.
TEST_F(ProcessSpawnTest, inheritEnv) {
// Run the process which sleeps for 10 seconds, so as we have
// enough time to check if it is running.
vector<string> args{"-v", "VAR", "value"};
ProcessEnvVars vars{"VAR=value"};
ProcessSpawn process(io_service_, TEST_SCRIPT_SH, args, vars,
/* inherit_env = */ true);
pid_t pid = 0;
ASSERT_NO_THROW(pid = process.spawn());
// Set test fail safe.
setTestTime(1000);
// The next handler executed is IOSignal's handler.
io_service_->runOne();
// The first handler executed is the IOSignal's internal timer expire
// callback.
io_service_->runOne();
// Polling once to be sure.
io_service_->poll();
ASSERT_EQ(1, processed_signals_.size());
ASSERT_EQ(SIGCHLD, processed_signals_[0]);
// 56 means successful comparison of env vars.
EXPECT_EQ(56, process.getExitStatus(pid));
}
// This test verifies inheritance of environment when a variable is inherited
// from parent. It assumes PATH is set.
TEST_F(ProcessSpawnTest, inheritEnvWithParentVar) {
// Run the process which sleeps for 10 seconds, so as we have
// enough time to check if it is running.
vector<string> args{"-v", "PATH", "value"};
ProcessEnvVars vars{"VAR=value"};
ProcessSpawn process(io_service_, TEST_SCRIPT_SH, args, vars,
/* inherit_env = */ true);
pid_t pid = 0;
ASSERT_NO_THROW(pid = process.spawn());
// Set test fail safe.
setTestTime(1000);
// The next handler executed is IOSignal's handler.
io_service_->runOne();
// The first handler executed is the IOSignal's internal timer expire
// callback.
io_service_->runOne();
// Polling once to be sure.
io_service_->poll();
ASSERT_EQ(1, processed_signals_.size());
ASSERT_EQ(SIGCHLD, processed_signals_[0]);
// 34 means failed comparison of env vars.
EXPECT_EQ(34, process.getExitStatus(pid));
}
} // end of anonymous namespace

View File

@@ -415,11 +415,13 @@ MySqlConnection::initializeSchema(const ParameterMap& parameters) {
// Convert parameters.
vector<string> kea_admin_parameters(toKeaAdminParameters(parameters));
ProcessEnvVars const vars;
kea_admin_parameters.insert(kea_admin_parameters.begin(), "db-init");
// Run.
IOServicePtr io_service(new IOService());
ProcessSpawn kea_admin(io_service, KEA_ADMIN, kea_admin_parameters);
ProcessSpawn kea_admin(io_service, KEA_ADMIN, kea_admin_parameters, vars,
/* inherit_env = */ true);
DB_LOG_INFO(MYSQL_INITIALIZE_SCHEMA).arg(kea_admin.getCommandLine());
pid_t const pid(kea_admin.spawn());
io_service->runOne();

View File

@@ -220,7 +220,8 @@ PgSqlConnection::initializeSchema(const ParameterMap& parameters) {
// Run.
IOServicePtr io_service(new IOService());
ProcessSpawn kea_admin(io_service, KEA_ADMIN, kea_admin_parameters, vars);
ProcessSpawn kea_admin(io_service, KEA_ADMIN, kea_admin_parameters, vars,
/* inherit_env = */ true);
DB_LOG_INFO(PGSQL_INITIALIZE_SCHEMA).arg(kea_admin.getCommandLine());
pid_t const pid(kea_admin.spawn());
io_service->runOne();