gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/test/util/multiprocess_util.h (about) 1 // Copyright 2018 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #ifndef GVISOR_TEST_UTIL_MULTIPROCESS_UTIL_H_ 16 #define GVISOR_TEST_UTIL_MULTIPROCESS_UTIL_H_ 17 18 #include <unistd.h> 19 20 #include <algorithm> 21 #include <functional> 22 #include <string> 23 #include <utility> 24 #include <vector> 25 26 #include "absl/strings/string_view.h" 27 #include "test/util/cleanup.h" 28 #include "test/util/posix_error.h" 29 30 namespace gvisor { 31 namespace testing { 32 33 // Immutable holder for a dynamically-sized array of pointers to mutable char, 34 // terminated by a null pointer, as required for the argv and envp arguments to 35 // execve(2). 36 class ExecveArray { 37 public: 38 // Constructs an empty ExecveArray. 39 ExecveArray() = default; 40 41 // Constructs an ExecveArray by copying strings from the given range. T must 42 // be a range over ranges of char. 43 template <typename T> 44 explicit ExecveArray(T const& strs) : ExecveArray(strs.begin(), strs.end()) {} 45 46 // Constructs an ExecveArray by copying strings from [first, last). InputIt 47 // must be an input iterator over a range over char. 48 template <typename InputIt> 49 ExecveArray(InputIt first, InputIt last) { 50 std::vector<size_t> offsets; 51 auto output_it = std::back_inserter(str_); 52 for (InputIt it = first; it != last; ++it) { 53 offsets.push_back(str_.size()); 54 auto const& s = *it; 55 std::copy(s.begin(), s.end(), output_it); 56 str_.push_back('\0'); 57 } 58 ptrs_.reserve(offsets.size() + 1); 59 for (auto offset : offsets) { 60 ptrs_.push_back(str_.data() + offset); 61 } 62 ptrs_.push_back(nullptr); 63 } 64 65 // Constructs an ExecveArray by copying strings from list. This overload must 66 // exist independently of the single-argument template constructor because 67 // std::initializer_list does not participate in template argument deduction 68 // (i.e. cannot be type-inferred in an invocation of the templated 69 // constructor). 70 /* implicit */ ExecveArray(std::initializer_list<absl::string_view> list) 71 : ExecveArray(list.begin(), list.end()) {} 72 73 // Disable move construction and assignment since ptrs_ points into str_. 74 ExecveArray(ExecveArray&&) = delete; 75 ExecveArray& operator=(ExecveArray&&) = delete; 76 77 char* const* get() const { return ptrs_.data(); } 78 size_t get_size() { return str_.size(); } 79 80 private: 81 std::vector<char> str_; 82 std::vector<char*> ptrs_; 83 }; 84 85 // Simplified version of SubProcess. Returns OK and a cleanup function to kill 86 // the child if it made it to execve. 87 // 88 // fn is run between fork and exec. If it needs to fail, it should exit the 89 // process. 90 // 91 // The child pid is returned via child, if provided. 92 // execve's error code is returned via execve_errno, if provided. 93 PosixErrorOr<Cleanup> ForkAndExec(const std::string& filename, 94 const ExecveArray& argv, 95 const ExecveArray& envv, 96 const std::function<void()>& fn, pid_t* child, 97 int* execve_errno); 98 99 inline PosixErrorOr<Cleanup> ForkAndExec(const std::string& filename, 100 const ExecveArray& argv, 101 const ExecveArray& envv, pid_t* child, 102 int* execve_errno) { 103 return ForkAndExec( 104 filename, argv, envv, [] {}, child, execve_errno); 105 } 106 107 // Equivalent to ForkAndExec, except using dirfd and flags with execveat. 108 PosixErrorOr<Cleanup> ForkAndExecveat(int32_t dirfd, 109 const std::string& pathname, 110 const ExecveArray& argv, 111 const ExecveArray& envv, int flags, 112 const std::function<void()>& fn, 113 pid_t* child, int* execve_errno); 114 115 inline PosixErrorOr<Cleanup> ForkAndExecveat(int32_t dirfd, 116 const std::string& pathname, 117 const ExecveArray& argv, 118 const ExecveArray& envv, int flags, 119 pid_t* child, int* execve_errno) { 120 return ForkAndExecveat( 121 dirfd, pathname, argv, envv, flags, [] {}, child, execve_errno); 122 } 123 124 // Calls fn in a forked subprocess and returns the exit status of the 125 // subprocess. 126 // 127 // fn must be async-signal-safe. Use of ASSERT/EXPECT functions is prohibited. 128 // Use TEST_CHECK variants instead. 129 PosixErrorOr<int> InForkedProcess(const std::function<void()>& fn); 130 131 // Sets up a new user and mount namespace in a forked subprocess using unshare, 132 // then runs the parent function in the parent subprocess. Once that returns, it 133 // runs the child function in the child process and returns the exit status of 134 // the child process. 135 // 136 // All calls must be async-signal-safe in the child function. Use of 137 // ASSERT/EXPECT functions is prohibited. Use TEST_CHECK variants instead. 138 PosixErrorOr<int> InForkedUserMountNamespace( 139 const std::function<void()>& parent, const std::function<void()>& child); 140 141 } // namespace testing 142 } // namespace gvisor 143 144 #endif // GVISOR_TEST_UTIL_MULTIPROCESS_UTIL_H_