ares-test-ns.cc
Go to the documentation of this file.
1 #include "ares-test.h"
2 
3 #ifdef HAVE_CONTAINER
4 
5 #include <sys/mount.h>
6 #include <sys/types.h>
7 #include <sys/stat.h>
8 #include <fcntl.h>
9 
10 #include <iostream>
11 #include <functional>
12 #include <string>
13 #include <sstream>
14 #include <vector>
15 
16 namespace ares {
17 namespace test {
18 
19 namespace {
20 
21 struct ContainerInfo {
22  ContainerFilesystem* fs_;
24  std::string domainname_;
25  VoidToIntFn fn_;
26 };
27 
28 int EnterContainer(void *data) {
29  ContainerInfo *container = (ContainerInfo*)data;
30 
31  if (verbose) {
32  std::cerr << "Running function in container {chroot='"
33  << container->fs_->root() << "', hostname='" << container->hostname_
34  << "', domainname='" << container->domainname_ << "'}"
35  << std::endl;
36  }
37 
38  // Ensure we are apparently root before continuing.
39  int count = 10;
40  while (getuid() != 0 && count > 0) {
41  usleep(100000);
42  count--;
43  }
44  if (getuid() != 0) {
45  std::cerr << "Child in user namespace has uid " << getuid() << std::endl;
46  return -1;
47  }
48  if (!container->fs_->mountpt().empty()) {
49  // We want to bind mount this inside the specified directory.
50  std::string innerdir = container->fs_->root() + container->fs_->mountpt();
51  if (verbose) std::cerr << " mount --bind " << container->fs_->mountpt()
52  << " " << innerdir << std::endl;
53  int rc = mount(container->fs_->mountpt().c_str(), innerdir.c_str(),
54  "none", MS_BIND, 0);
55  if (rc != 0) {
56  std::cerr << "Warning: failed to bind mount " << container->fs_->mountpt() << " at "
57  << innerdir << ", errno=" << errno << std::endl;
58  }
59  }
60 
61  // Move into the specified directory.
62  if (chdir(container->fs_->root().c_str()) != 0) {
63  std::cerr << "Failed to chdir('" << container->fs_->root()
64  << "'), errno=" << errno << std::endl;
65  return -1;
66  }
67  // And make it the new root directory;
68  char buffer[PATH_MAX + 1];
69  if (getcwd(buffer, PATH_MAX) == NULL) {
70  std::cerr << "failed to retrieve cwd, errno=" << errno << std::endl;
71  return -1;
72  }
73  buffer[PATH_MAX] = '\0';
74  if (chroot(buffer) != 0) {
75  std::cerr << "chroot('" << buffer << "') failed, errno=" << errno << std::endl;
76  return -1;
77  }
78 
79  // Set host/domainnames if specified
80  if (!container->hostname_.empty()) {
81  if (sethostname(container->hostname_.c_str(),
82  container->hostname_.size()) != 0) {
83  std::cerr << "Failed to sethostname('" << container->hostname_
84  << "'), errno=" << errno << std::endl;
85  return -1;
86  }
87  }
88  if (!container->domainname_.empty()) {
89  if (setdomainname(container->domainname_.c_str(),
90  container->domainname_.size()) != 0) {
91  std::cerr << "Failed to setdomainname('" << container->domainname_
92  << "'), errno=" << errno << std::endl;
93  return -1;
94  }
95  }
96 
97  return container->fn_();
98 }
99 
100 } // namespace
101 
102 // Run a function while:
103 // - chroot()ed into a particular directory
104 // - having a specified hostname/domainname
105 
106 int RunInContainer(ContainerFilesystem* fs, const std::string& hostname,
107  const std::string& domainname, VoidToIntFn fn) {
108  const int stack_size = 1024 * 1024;
109  std::vector<byte> stack(stack_size, 0);
110  ContainerInfo container = {fs, hostname, domainname, fn};
111 
112  // Start a child process in a new user and UTS namespace
113  pid_t child = clone(EnterContainer, stack.data() + stack_size,
114  CLONE_VM|CLONE_NEWNS|CLONE_NEWUSER|CLONE_NEWUTS|SIGCHLD,
115  (void *)&container);
116  if (child < 0) {
117  std::cerr << "Failed to clone(), errno=" << errno << std::endl;
118  return -1;
119  }
120 
121  // Build the UID map that makes us look like root inside the namespace.
122  std::stringstream mapfiless;
123  mapfiless << "/proc/" << child << "/uid_map";
124  std::string mapfile = mapfiless.str();
125  int fd = open(mapfile.c_str(), O_CREAT|O_WRONLY|O_TRUNC, 0644);
126  if (fd < 0) {
127  std::cerr << "Failed to create '" << mapfile << "'" << std::endl;
128  return -1;
129  }
130  std::stringstream contentss;
131  contentss << "0 " << getuid() << " 1" << std::endl;
132  std::string content = contentss.str();
133  int rc = write(fd, content.c_str(), content.size());
134  if (rc != (int)content.size()) {
135  std::cerr << "Failed to write uid map to '" << mapfile << "'" << std::endl;
136  }
137  close(fd);
138 
139  // Wait for the child process and retrieve its status.
140  int status;
141  waitpid(child, &status, 0);
142  if (rc <= 0) {
143  std::cerr << "Failed to waitpid(" << child << ")" << std::endl;
144  return -1;
145  }
146  if (!WIFEXITED(status)) {
147  std::cerr << "Child " << child << " did not exit normally" << std::endl;
148  return -1;
149  }
150  return status;
151 }
152 
153 ContainerFilesystem::ContainerFilesystem(NameContentList files, const std::string& mountpt) {
154  rootdir_ = TempNam(nullptr, "ares-chroot");
155  mkdir(rootdir_.c_str(), 0755);
156  dirs_.push_front(rootdir_);
157  for (const auto& nc : files) {
158  std::string fullpath = rootdir_ + nc.first;
159  int idx = fullpath.rfind('/');
160  std::string dir = fullpath.substr(0, idx);
161  EnsureDirExists(dir);
162  files_.push_back(std::unique_ptr<TransientFile>(
163  new TransientFile(fullpath, nc.second)));
164  }
165  if (!mountpt.empty()) {
166  char buffer[PATH_MAX + 1];
167  if (realpath(mountpt.c_str(), buffer)) {
168  mountpt_ = buffer;
169  std::string fullpath = rootdir_ + mountpt_;
170  EnsureDirExists(fullpath);
171  }
172  }
173 }
174 
175 ContainerFilesystem::~ContainerFilesystem() {
176  files_.clear();
177  for (const std::string& dir : dirs_) {
178  rmdir(dir.c_str());
179  }
180 }
181 
183  if (std::find(dirs_.begin(), dirs_.end(), dir) != dirs_.end()) {
184  return;
185  }
186  size_t idx = dir.rfind('/');
187  if (idx != std::string::npos) {
188  std::string prevdir = dir.substr(0, idx);
189  EnsureDirExists(prevdir);
190  }
191  // Ensure this directory is in the list before its ancestors.
192  mkdir(dir.c_str(), 0755);
193  dirs_.push_front(dir);
194 }
195 
196 } // namespace test
197 } // namespace ares
198 
199 #endif
check_grpcio_tools.content
content
Definition: check_grpcio_tools.py:26
hostname_
std::string hostname_
Definition: istio_echo_server_test.cc:71
rmdir
#define rmdir
Definition: test-fs.c:45
find
static void ** find(grpc_chttp2_stream_map *map, uint32_t key)
Definition: stream_map.cc:99
write
#define write
Definition: test-fs.c:47
test
Definition: spinlock_test.cc:36
testing::internal::string
::std::string string
Definition: bloaty/third_party/protobuf/third_party/googletest/googletest/include/gtest/internal/gtest-port.h:881
files_
std::map< std::string, std::unique_ptr< std::string > > files_
Definition: bloaty/third_party/protobuf/src/google/protobuf/compiler/cpp/cpp_bootstrap_unittest.cc:123
status
absl::Status status
Definition: rls.cc:251
generate-asm-lcov.fn
fn
Definition: generate-asm-lcov.py:146
update_clang.EnsureDirExists
def EnsureDirExists(path)
Definition: update_clang.py:86
ares::test::verbose
bool verbose
Definition: ares-test.cc:34
close
#define close
Definition: test-fs.c:48
googletest-filter-unittest.child
child
Definition: bloaty/third_party/googletest/googletest/test/googletest-filter-unittest.py:62
stack
NodeStack stack
Definition: cord_rep_btree.cc:356
ares::test::TempNam
std::string TempNam(const char *dir, const char *prefix)
Definition: ares-test.cc:783
data
char data[kBufferLength]
Definition: abseil-cpp/absl/strings/internal/str_format/float_conversion.cc:1006
buffer
char buffer[1024]
Definition: libuv/docs/code/idle-compute/main.c:8
ares-test.h
setup.idx
idx
Definition: third_party/bloaty/third_party/capstone/bindings/python/setup.py:197
count
int * count
Definition: bloaty/third_party/googletest/googlemock/test/gmock_stress_test.cc:96
open
#define open
Definition: test-fs.c:46
amalgamate.files
list files
Definition: amalgamate.py:115
ares
Definition: ares-test-ai.h:9
container
static struct async_container * container
Definition: benchmark-million-async.c:33


grpc
Author(s):
autogenerated on Fri May 16 2025 02:57:42