mirror of
https://gitlab.isc.org/isc-projects/kea
synced 2025-08-31 22:15:23 +00:00
[#3025] add ability to inherit env in ProcessSpawn
This commit is contained in:
@@ -9,10 +9,12 @@
|
|||||||
#include <asiolink/io_service_signal.h>
|
#include <asiolink/io_service_signal.h>
|
||||||
#include <asiolink/process_spawn.h>
|
#include <asiolink/process_spawn.h>
|
||||||
#include <exceptions/exceptions.h>
|
#include <exceptions/exceptions.h>
|
||||||
#include <cstring>
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
@@ -25,6 +27,8 @@
|
|||||||
using namespace std;
|
using namespace std;
|
||||||
namespace ph = std::placeholders;
|
namespace ph = std::placeholders;
|
||||||
|
|
||||||
|
extern char **environ;
|
||||||
|
|
||||||
namespace isc {
|
namespace isc {
|
||||||
namespace asiolink {
|
namespace asiolink {
|
||||||
|
|
||||||
@@ -77,10 +81,13 @@ public:
|
|||||||
/// @param executable A full path to the program to be executed.
|
/// @param executable A full path to the program to be executed.
|
||||||
/// @param args Arguments for 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 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,
|
ProcessSpawnImpl(IOServicePtr io_service,
|
||||||
const std::string& executable,
|
const std::string& executable,
|
||||||
const ProcessArgs& args,
|
const ProcessArgs& args,
|
||||||
const ProcessEnvVars& vars);
|
const ProcessEnvVars& vars,
|
||||||
|
const bool inherit_env);
|
||||||
|
|
||||||
/// @brief Destructor.
|
/// @brief Destructor.
|
||||||
~ProcessSpawnImpl();
|
~ProcessSpawnImpl();
|
||||||
@@ -238,9 +245,24 @@ void ProcessSpawnImpl::IOSignalSetInitializer::initIOSignalSet(IOServicePtr io_s
|
|||||||
ProcessSpawnImpl::ProcessSpawnImpl(IOServicePtr io_service,
|
ProcessSpawnImpl::ProcessSpawnImpl(IOServicePtr io_service,
|
||||||
const std::string& executable,
|
const std::string& executable,
|
||||||
const ProcessArgs& args,
|
const ProcessArgs& args,
|
||||||
const ProcessEnvVars& vars)
|
const ProcessEnvVars& vars,
|
||||||
|
const bool inherit_env)
|
||||||
: executable_(executable), args_(new char*[args.size() + 2]),
|
: 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;
|
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
|
// all pointers within an array to NULL to indicate that they haven't
|
||||||
// been allocated yet.
|
// been allocated yet.
|
||||||
memset(args_.get(), 0, (args.size() + 2) * sizeof(char*));
|
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.
|
// By convention, the first argument points to an executable name.
|
||||||
args_[0] = allocateInternal(executable_);
|
args_[0] = allocateInternal(executable_);
|
||||||
// Copy arguments to the array.
|
// 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]);
|
args_[i] = allocateInternal(args[i - 1]);
|
||||||
}
|
}
|
||||||
// Copy environment variables to the array.
|
// Copy environment variables to the array.
|
||||||
for (int i = 0; i < vars.size(); ++i) {
|
size_t i(0);
|
||||||
vars_[i] = allocateInternal(vars[i]);
|
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,
|
ProcessSpawn::ProcessSpawn(IOServicePtr io_service,
|
||||||
const std::string& executable,
|
const std::string& executable,
|
||||||
const ProcessArgs& args,
|
const ProcessArgs& args,
|
||||||
const ProcessEnvVars& vars)
|
const ProcessEnvVars& vars,
|
||||||
: impl_(new ProcessSpawnImpl(io_service, executable, args, vars)) {
|
const bool inherit_env /* = false */)
|
||||||
|
: impl_(new ProcessSpawnImpl(io_service, executable, args, vars, inherit_env)) {
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string
|
std::string
|
||||||
|
@@ -67,10 +67,13 @@ public:
|
|||||||
/// @param executable A full path to the program to be executed.
|
/// @param executable A full path to the program to be executed.
|
||||||
/// @param args Arguments for 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 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,
|
ProcessSpawn(isc::asiolink::IOServicePtr io_service,
|
||||||
const std::string& executable,
|
const std::string& executable,
|
||||||
const ProcessArgs& args = ProcessArgs(),
|
const ProcessArgs& args = ProcessArgs(),
|
||||||
const ProcessEnvVars& vars = ProcessEnvVars());
|
const ProcessEnvVars& vars = ProcessEnvVars(),
|
||||||
|
const bool inherit_env = false);
|
||||||
|
|
||||||
/// @brief Destructor.
|
/// @brief Destructor.
|
||||||
~ProcessSpawn() = default;
|
~ProcessSpawn() = default;
|
||||||
|
@@ -28,48 +28,45 @@
|
|||||||
# used.
|
# used.
|
||||||
set -eu
|
set -eu
|
||||||
|
|
||||||
exit_code=
|
exit_code=0
|
||||||
|
|
||||||
while test "${#}" -gt 0
|
# The exit code of 32 is returned when no args specified.
|
||||||
do
|
if test "${#}" = 0; then
|
||||||
|
exit_code=32
|
||||||
|
fi
|
||||||
|
|
||||||
|
while test "${#}" -gt 0; do
|
||||||
option=${1}
|
option=${1}
|
||||||
|
shift
|
||||||
case ${option} in
|
case ${option} in
|
||||||
-p)
|
-p)
|
||||||
exit_code=${$}
|
exit_code=${$}
|
||||||
;;
|
;;
|
||||||
-e)
|
-e)
|
||||||
shift
|
|
||||||
exit_code=${1-}
|
exit_code=${1-}
|
||||||
|
shift
|
||||||
;;
|
;;
|
||||||
-s)
|
-s)
|
||||||
|
sleep "${1-0}"
|
||||||
|
exit_code=12
|
||||||
shift
|
shift
|
||||||
sleep "${1}"
|
|
||||||
;;
|
;;
|
||||||
-v)
|
-v)
|
||||||
shift
|
|
||||||
VAR_NAME=${1}
|
VAR_NAME=${1}
|
||||||
shift
|
shift
|
||||||
VAR_VALUE=${1}
|
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
|
if ! test "${VAR_NAME}=${VAR_VALUE}" = "${EXPECTED}"; then
|
||||||
exit 123
|
exit_code=34
|
||||||
|
else
|
||||||
|
exit_code=56
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
exit 123
|
exit_code=78
|
||||||
;;
|
;;
|
||||||
esac
|
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
|
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}"
|
exit "${exit_code}"
|
||||||
|
@@ -128,6 +128,7 @@ TEST_F(ProcessSpawnTest, spawnWithArgs) {
|
|||||||
ASSERT_EQ(1, processed_signals_.size());
|
ASSERT_EQ(1, processed_signals_.size());
|
||||||
ASSERT_EQ(SIGCHLD, processed_signals_[0]);
|
ASSERT_EQ(SIGCHLD, processed_signals_[0]);
|
||||||
|
|
||||||
|
// Exit code 64 as requested.
|
||||||
EXPECT_EQ(64, process.getExitStatus(pid));
|
EXPECT_EQ(64, process.getExitStatus(pid));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,7 +162,8 @@ TEST_F(ProcessSpawnTest, spawnWithArgsAndEnvVars) {
|
|||||||
ASSERT_EQ(1, processed_signals_.size());
|
ASSERT_EQ(1, processed_signals_.size());
|
||||||
ASSERT_EQ(SIGCHLD, processed_signals_[0]);
|
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
|
// 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(1, processed_signals_.size());
|
||||||
ASSERT_EQ(SIGCHLD, processed_signals_[0]);
|
ASSERT_EQ(SIGCHLD, processed_signals_[0]);
|
||||||
|
|
||||||
|
// 32 means no args.
|
||||||
EXPECT_EQ(32, process.getExitStatus(pid));
|
EXPECT_EQ(32, process.getExitStatus(pid));
|
||||||
|
|
||||||
ASSERT_NO_THROW(pid = process.spawn(true));
|
ASSERT_NO_THROW(pid = process.spawn(true));
|
||||||
@@ -347,4 +350,71 @@ TEST_F(ProcessSpawnTest, isRunning) {
|
|||||||
ASSERT_EQ(SIGCHLD, processed_signals_[0]);
|
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
|
} // end of anonymous namespace
|
||||||
|
@@ -415,11 +415,13 @@ MySqlConnection::initializeSchema(const ParameterMap& parameters) {
|
|||||||
|
|
||||||
// Convert parameters.
|
// Convert parameters.
|
||||||
vector<string> kea_admin_parameters(toKeaAdminParameters(parameters));
|
vector<string> kea_admin_parameters(toKeaAdminParameters(parameters));
|
||||||
|
ProcessEnvVars const vars;
|
||||||
kea_admin_parameters.insert(kea_admin_parameters.begin(), "db-init");
|
kea_admin_parameters.insert(kea_admin_parameters.begin(), "db-init");
|
||||||
|
|
||||||
// Run.
|
// Run.
|
||||||
IOServicePtr io_service(new IOService());
|
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());
|
DB_LOG_INFO(MYSQL_INITIALIZE_SCHEMA).arg(kea_admin.getCommandLine());
|
||||||
pid_t const pid(kea_admin.spawn());
|
pid_t const pid(kea_admin.spawn());
|
||||||
io_service->runOne();
|
io_service->runOne();
|
||||||
|
@@ -220,7 +220,8 @@ PgSqlConnection::initializeSchema(const ParameterMap& parameters) {
|
|||||||
|
|
||||||
// Run.
|
// Run.
|
||||||
IOServicePtr io_service(new IOService());
|
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());
|
DB_LOG_INFO(PGSQL_INITIALIZE_SCHEMA).arg(kea_admin.getCommandLine());
|
||||||
pid_t const pid(kea_admin.spawn());
|
pid_t const pid(kea_admin.spawn());
|
||||||
io_service->runOne();
|
io_service->runOne();
|
||||||
|
Reference in New Issue
Block a user