diff --git a/src/common/process.cpp b/src/common/process.cpp new file mode 100644 index 00000000..4732edc7 --- /dev/null +++ b/src/common/process.cpp @@ -0,0 +1,70 @@ +// yabridge: a Wine VST bridge +// Copyright (C) 2020-2022 Robbert van der Helm +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "process.h" + +#include + +ProcessEnvironment::ProcessEnvironment(char** initial_env) { + // We'll need to read all strings from `initial_env`. They _should_ all be + // zero-terminated strings, with a null pointer to indicate the end of the + // array. + assert(initial_env); + while (*initial_env) { + variables_.push_back(*initial_env); + initial_env++; + } +} + +bool ProcessEnvironment::contains(const std::string_view& key) const { + for (const auto& variable : variables_) { + if (variable.starts_with(key) && variable.size() > key.size() && + variable[key.size()] == '=') { + return true; + } + } + + return false; +} + +std::optional ProcessEnvironment::get( + const std::string_view& key) const { + for (const auto& variable : variables_) { + if (variable.starts_with(key) && variable.size() > key.size() && + variable[key.size()] == '=') { + return std::string_view(variable).substr(key.size() + 1); + } + } + + return std::nullopt; +} + +// NOLINTNEXTLINE(bugprone-easily-swappable-parameters) +void ProcessEnvironment::insert(const std::string& key, + const std::string& value) { + variables_.push_back(key + "=" + value); +} + +char* const* ProcessEnvironment::make_environ() const { + recreated_environ_.clear(); + + for (const auto& variable : variables_) { + recreated_environ_.push_back(variable.c_str()); + } + recreated_environ_.push_back(nullptr); + + return const_cast(recreated_environ_.data()); +} diff --git a/src/common/process.h b/src/common/process.h new file mode 100644 index 00000000..55e4f2bb --- /dev/null +++ b/src/common/process.h @@ -0,0 +1,77 @@ +// yabridge: a Wine VST bridge +// Copyright (C) 2020-2022 Robbert van der Helm +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#pragma once + +#include +#include +#include + +// A minimal API akin to Boost.Process for launching and managing processes +// using plain Linux APIs. Needed so we can implement our chainloader without +// pulling in Boost.Process' Boost.Filesystem dependency (which would defeat the +// entire purpose). + +/** + * Helper to create an `environ`-like environment object for passing to the + * `exec*e()` family of functions. + */ +class ProcessEnvironment { + public: + /** + * Create a new environment object based on an existing environment + * described by an array of null-terminated strings, terminated by a null + * pointer. You'll want to pass `environ` here. + */ + ProcessEnvironment(char** initial_env); + + /** + * Check if an environment variable exists within this environment. Mostly + * useful for debugging. + */ + bool contains(const std::string_view& key) const; + + /** + * Get the value for an environment variable, if it exists in this + * environment. Mostly useful for debugging. + */ + std::optional get(const std::string_view& key) const; + + /** + * Add an environment variable to the environment or overwrite an + * existing one. + */ + void insert(const std::string& key, const std::string& value); + + /** + * Create an environ-like object from the updated environment that can be + * passed to the `exec*e()` functions. These pointers will be invalidated + * when this object changes or when gets dropped. + */ + char* const* make_environ() const; + + private: + /** + * All environment variables read from the constructor argument and those + * inserted through `insert()`. These should be in `key=value` format. + */ + std::vector variables_; + /** + * Contains pointers to the strings in `variables`, so we can return a + * `char**` in `make_environ()`. + */ + mutable std::vector recreated_environ_; +}; diff --git a/src/plugin/meson.build b/src/plugin/meson.build index 7c8cbe41..aeaee5dc 100644 --- a/src/plugin/meson.build +++ b/src/plugin/meson.build @@ -10,6 +10,7 @@ vst2_plugin_sources = files( '../common/logging/vst2.cpp', '../common/audio-shm.cpp', '../common/plugins.cpp', + '../common/process.cpp', '../common/utils.cpp', 'bridges/vst2.cpp', 'host-process.cpp', @@ -79,6 +80,7 @@ vst3_plugin_sources = files( '../common/audio-shm.cpp', '../common/configuration.cpp', '../common/plugins.cpp', + '../common/process.cpp', '../common/utils.cpp', 'bridges/vst3.cpp', 'bridges/vst3-impls/plugin-factory-proxy.cpp', diff --git a/src/wine-host/meson.build b/src/wine-host/meson.build index 3ca29216..ed357ae5 100644 --- a/src/wine-host/meson.build +++ b/src/wine-host/meson.build @@ -64,6 +64,7 @@ host_common_sources = files( '../common/logging/vst2.cpp', '../common/audio-shm.cpp', '../common/plugins.cpp', + '../common/process.cpp', '../common/utils.cpp', 'bridges/common.cpp', 'bridges/vst2.cpp',