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_