[flang] runtime: Read environment variables directly

Add support for reading environment variables directly, via std::getenv.
This needs to allocate a C-style string to pass into std::getenv. If the
memory allocation for that fails, we terminate.

This also changes the interface for EnvVariableLength to receive the
source file and line so we can crash gracefully.

Note that we are now completely ignoring the envp pointer passed into
ProgramStart, since that could go stale if the environment is modified
during execution.

Differential Revision: https://reviews.llvm.org/D111785
This commit is contained in:
Diana Picus 2021-10-12 12:40:48 +00:00
parent 177176f75c
commit 824bf90819
6 changed files with 56 additions and 35 deletions

View file

@ -48,8 +48,8 @@ std::int32_t RTNAME(EnvVariableValue)(const Descriptor &name,
// Try to get the significant length of the environment variable specified by
// NAME. Returns 0 if it doesn't manage.
std::int64_t RTNAME(EnvVariableLength)(
const Descriptor &name, bool trim_name = true);
std::int64_t RTNAME(EnvVariableLength)(const Descriptor &name,
bool trim_name = true, const char *sourceFile = nullptr, int line = 0);
}
} // namespace Fortran::runtime

View file

@ -9,6 +9,7 @@
#include "flang/Runtime/command.h"
#include "environment.h"
#include "stat.h"
#include "terminator.h"
#include "flang/Runtime/descriptor.h"
#include <cstdlib>
#include <limits>
@ -89,15 +90,17 @@ static std::size_t LengthWithoutTrailingSpaces(const Descriptor &d) {
return s + 1;
}
std::int64_t RTNAME(EnvVariableLength)(const Descriptor &name, bool trim_name) {
std::int64_t RTNAME(EnvVariableLength)(
const Descriptor &name, bool trim_name, const char *sourceFile, int line) {
std::size_t nameLength{
trim_name ? LengthWithoutTrailingSpaces(name) : name.ElementBytes()};
if (nameLength == 0) {
return 0;
}
const char *value{
executionEnvironment.GetEnv(name.OffsetElement(), nameLength)};
Terminator terminator{sourceFile, line};
const char *value{executionEnvironment.GetEnv(
name.OffsetElement(), nameLength, terminator)};
if (!value) {
return 0;
}

View file

@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
#include "environment.h"
#include "memory.h"
#include "tools.h"
#include <cstdio>
#include <cstdlib>
@ -70,26 +71,13 @@ void ExecutionEnvironment::Configure(
}
const char *ExecutionEnvironment::GetEnv(
const char *name, std::size_t name_length) {
if (!envp) {
// TODO: Ask std::getenv.
return nullptr;
}
const char *name, std::size_t name_length, const Terminator &terminator) {
RUNTIME_CHECK(terminator, name && name_length);
// envp is an array of strings of the form "name=value".
for (const char **var{envp}; *var != nullptr; ++var) {
const char *eq{std::strchr(*var, '=')};
if (!eq) {
// Found a malformed environment string, just ignore it.
continue;
}
if (static_cast<std::size_t>(eq - *var) != name_length) {
continue;
}
if (std::memcmp(*var, name, name_length) == 0) {
return eq + 1;
}
}
return nullptr;
OwningPtr<char> cStyleName{
SaveDefaultCharacter(name, name_length, terminator)};
RUNTIME_CHECK(terminator, cStyleName);
return std::getenv(cStyleName.get());
}
} // namespace Fortran::runtime

View file

@ -14,6 +14,8 @@
namespace Fortran::runtime {
class Terminator;
#if FLANG_BIG_ENDIAN
constexpr bool isHostLittleEndian{false};
#elif FLANG_LITTLE_ENDIAN
@ -29,7 +31,8 @@ std::optional<Convert> GetConvertFromString(const char *, std::size_t);
struct ExecutionEnvironment {
void Configure(int argc, const char *argv[], const char *envp[]);
const char *GetEnv(const char *name, std::size_t name_length);
const char *GetEnv(
const char *name, std::size_t name_length, const Terminator &terminator);
int argc;
const char **argv;

View file

@ -10,6 +10,7 @@
#include "terminator.h"
#include <algorithm>
#include <cstdint>
#include <cstdlib>
#include <cstring>
namespace Fortran::runtime {

View file

@ -11,6 +11,7 @@
#include "gtest/gtest.h"
#include "flang/Runtime/descriptor.h"
#include "flang/Runtime/main.h"
#include <cstdlib>
using namespace Fortran::runtime;
@ -41,8 +42,6 @@ protected:
RTNAME(ProgramStart)(argc, argv, {});
}
CommandFixture(const char *envp[]) { RTNAME(ProgramStart)(0, nullptr, envp); }
std::string GetPaddedStr(const char *text, std::size_t len) const {
std::string res{text};
assert(res.length() <= len && "No room to pad");
@ -189,19 +188,46 @@ TEST_F(SeveralArguments, ErrMsgTooShort) {
CheckDescriptorEqStr(errMsg.get(), "Inv");
}
static const char *env[]{"NAME=value", nullptr};
class EnvironmentVariables : public CommandFixture {
protected:
EnvironmentVariables() : CommandFixture(env) {}
EnvironmentVariables() : CommandFixture(0, nullptr) {
SetEnv("NAME", "VALUE");
}
// If we have access to setenv, we can run some more fine-grained tests.
template <typename ParamType = char>
void SetEnv(const ParamType *name, const ParamType *value,
decltype(setenv(name, value, 1)) *Enabled = nullptr) {
ASSERT_EQ(0, setenv(name, value, /*overwrite=*/1));
canSetEnv = true;
}
// Fallback method if setenv is not available.
template <typename Unused = void> void SetEnv(const void *, const void *) {}
bool EnableFineGrainedTests() const { return canSetEnv; }
private:
bool canSetEnv{false};
};
TEST_F(EnvironmentVariables, Length) {
EXPECT_EQ(5, RTNAME(EnvVariableLength)(*CharDescriptor("NAME")));
EXPECT_EQ(0, RTNAME(EnvVariableLength)(*CharDescriptor("DOESNT_EXIST")));
EXPECT_EQ(5, RTNAME(EnvVariableLength)(*CharDescriptor("NAME ")));
EXPECT_EQ(0,
RTNAME(EnvVariableLength)(*CharDescriptor("NAME "), /*trim_name=*/false));
EXPECT_EQ(0, RTNAME(EnvVariableLength)(*CharDescriptor(" ")));
EXPECT_EQ(0, RTNAME(EnvVariableLength)(*CharDescriptor("")));
// Test a variable that's expected to exist in the environment.
char *path{std::getenv("PATH")};
auto expectedLen{static_cast<int64_t>(std::strlen(path))};
EXPECT_EQ(expectedLen, RTNAME(EnvVariableLength)(*CharDescriptor("PATH")));
if (EnableFineGrainedTests()) {
EXPECT_EQ(5, RTNAME(EnvVariableLength)(*CharDescriptor("NAME")));
EXPECT_EQ(5, RTNAME(EnvVariableLength)(*CharDescriptor("NAME ")));
EXPECT_EQ(0,
RTNAME(EnvVariableLength)(
*CharDescriptor("NAME "), /*trim_name=*/false));
}
}