// Copyright 2018 The gVisor Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef GVISOR_TEST_UTIL_MULTIPROCESS_UTIL_H_ #define GVISOR_TEST_UTIL_MULTIPROCESS_UTIL_H_ #include <unistd.h> #include <algorithm> #include <string> #include <utility> #include <vector> #include "absl/strings/string_view.h" #include "test/util/cleanup.h" #include "test/util/posix_error.h" namespace gvisor { namespace testing { // Immutable holder for a dynamically-sized array of pointers to mutable char, // terminated by a null pointer, as required for the argv and envp arguments to // execve(2). class ExecveArray { public: // Constructs an empty ExecveArray. ExecveArray() = default; // Constructs an ExecveArray by copying strings from the given range. T must // be a range over ranges of char. template <typename T> explicit ExecveArray(T const& strs) : ExecveArray(strs.begin(), strs.end()) {} // Constructs an ExecveArray by copying strings from [first, last). InputIt // must be an input iterator over a range over char. template <typename InputIt> ExecveArray(InputIt first, InputIt last) { std::vector<size_t> offsets; auto output_it = std::back_inserter(str_); for (InputIt it = first; it != last; ++it) { offsets.push_back(str_.size()); auto const& s = *it; std::copy(s.begin(), s.end(), output_it); str_.push_back('\0'); } ptrs_.reserve(offsets.size() + 1); for (auto offset : offsets) { ptrs_.push_back(str_.data() + offset); } ptrs_.push_back(nullptr); } // Constructs an ExecveArray by copying strings from list. This overload must // exist independently of the single-argument template constructor because // std::initializer_list does not participate in template argument deduction // (i.e. cannot be type-inferred in an invocation of the templated // constructor). /* implicit */ ExecveArray(std::initializer_list<absl::string_view> list) : ExecveArray(list.begin(), list.end()) {} // Disable move construction and assignment since ptrs_ points into str_. ExecveArray(ExecveArray&&) = delete; ExecveArray& operator=(ExecveArray&&) = delete; char* const* get() const { return ptrs_.data(); } size_t get_size() { return str_.size(); } private: std::vector<char> str_; std::vector<char*> ptrs_; }; // Simplified version of SubProcess. Returns OK and a cleanup function to kill // the child if it made it to execve. // // fn is run between fork and exec. If it needs to fail, it should exit the // process. // // The child pid is returned via child, if provided. // execve's error code is returned via execve_errno, if provided. PosixErrorOr<Cleanup> ForkAndExec(const std::string& filename, const ExecveArray& argv, const ExecveArray& envv, const std::function<void()>& fn, pid_t* child, int* execve_errno); inline PosixErrorOr<Cleanup> ForkAndExec(const std::string& filename, const ExecveArray& argv, const ExecveArray& envv, pid_t* child, int* execve_errno) { return ForkAndExec(filename, argv, envv, [] {}, child, execve_errno); } // Calls fn in a forked subprocess and returns the exit status of the // subprocess. // // fn must be async-signal-safe. PosixErrorOr<int> InForkedProcess(const std::function<void()>& fn); } // namespace testing } // namespace gvisor #endif // GVISOR_TEST_UTIL_MULTIPROCESS_UTIL_H_