streamlets: fix command name collisions, do not prepend $RELPIPE_IN_FILESYSTEM_STREAMLET_PATH to the $PATH
(a streamlet may call a command with the same name as the streamlet name, which will lead unwanted recursion)
--- a/src/StreamletAttributeFinder.h Sat Jan 18 20:09:34 2020 +0100
+++ b/src/StreamletAttributeFinder.h Sun Jan 19 00:57:22 2020 +0100
@@ -20,6 +20,7 @@
#include <filesystem>
#include <regex>
#include <memory>
+#include <sstream>
#include <relpipe/writer/typedefs.h>
#include <relpipe/writer/AttributeMetadata.h>
@@ -43,15 +44,31 @@
std::map<int, std::shared_ptr<SubProcess>> subProcesses;
std::map<int, std::vector<AttributeMetadata>> cachedMetadata;
- string_t getStreamletPath() {
- const char* originalPath = getenv("PATH");
- const char* streamletPath = getenv("RELPIPE_IN_FILESYSTEM_STREAMLET_PATH");
+ string_t getStreamletCommand(const RequestedField& field) {
+ const char* streamletPathChars = getenv("RELPIPE_IN_FILESYSTEM_STREAMLET_PATH");
+ if (streamletPathChars) {
+ std::wstringstream current;
+ string_t streamletPath = convertor.from_bytes(streamletPathChars);
+ for (int i = 0, streamletPathSize = streamletPath.size(); i < streamletPathSize; i++) {
+ if (streamletPath[i] == ':' || i == streamletPathSize - 1) { // FIXME: support \: and \\ escaping
+ current << L"/" << field.name;
+ fs::path streamletFile(current.str());
+ if (fs::exists(streamletFile)) { // FIXME: check executable bit
+ return current.str();
+ } else {
+ current.str(L"");
+ current.clear();
+ }
+ } else {
+ current.put(streamletPath[i]);
+ }
+ }
+ throw RelpipeWriterException(L"Streamlet „" + field.name + L"“ was not found at $RELPIPE_IN_FILESYSTEM_STREAMLET_PATH");
+ } else {
+ throw RelpipeWriterException(L"Missing environment variable RELPIPE_IN_FILESYSTEM_STREAMLET_PATH → unable to find streamlet.");
+ }
+ }
- if (originalPath && streamletPath) return convertor.from_bytes(std::string(streamletPath) + ":" + originalPath);
- else if (originalPath) return convertor.from_bytes(std::string(originalPath));
- else if (streamletPath) return convertor.from_bytes(std::string(streamletPath));
- else return L"";
- }
protected:
void startFile(const fs::path& file, const string& fileRaw, bool exists) override {
@@ -90,7 +107,7 @@
return cachedMetadata[field.id];
} else {
- std::vector<string_t> commandLine = {field.name};
+ std::vector<string_t> commandLine = {getStreamletCommand(field)};
std::map<string_t, string_t> environment;
for (auto mn : StreamletMsg::getMessageNames()) {
@@ -98,8 +115,6 @@
environment[L"EXEC_MSG_" + std::to_wstring(mn.first)] = mn.second;
}
- environment[L"PATH"] = getStreamletPath();
-
shared_ptr<SubProcess> subProcess(SubProcess::create(commandLine, environment));
subProcesses[field.id] = subProcess;
--- a/src/SubProcess.cpp Sat Jan 18 20:09:34 2020 +0100
+++ b/src/SubProcess.cpp Sun Jan 19 00:57:22 2020 +0100
@@ -76,7 +76,7 @@
for (size_t i = 0; i < args.size(); i++) a[i] = args[i].c_str();
a[args.size()] = nullptr;
- execvp(a[0], (char*const*) a);
+ execv(a[0], (char*const*) a);
delete[] a;
throw SubProcess::Exception(L"Unable to do execvp().");