summaryrefslogtreecommitdiffhomepage
path: root/test/util/multiprocess_util.h
blob: 342e73a5208851ff38203d680927ca02eea4ce6e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
// 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);
}

// Equivalent to ForkAndExec, except using dirfd and flags with execveat.
PosixErrorOr<Cleanup> ForkAndExecveat(int32 dirfd, const std::string& pathname,
                                      const ExecveArray& argv,
                                      const ExecveArray& envv, int flags,
                                      const std::function<void()>& fn,
                                      pid_t* child, int* execve_errno);

inline PosixErrorOr<Cleanup> ForkAndExecveat(int32 dirfd,
                                             const std::string& pathname,
                                             const ExecveArray& argv,
                                             const ExecveArray& envv, int flags,
                                             pid_t* child, int* execve_errno) {
  return ForkAndExecveat(
      dirfd, pathname, argv, envv, flags, [] {}, 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_