quickjs-tart

quickjs-based runtime for wallet-core logic
Log | Files | Refs | README | LICENSE

ares-test-ns.cc (7382B)


      1 /* MIT License
      2  *
      3  * Copyright (c) The c-ares project and its contributors
      4  *
      5  * Permission is hereby granted, free of charge, to any person obtaining a copy
      6  * of this software and associated documentation files (the "Software"), to deal
      7  * in the Software without restriction, including without limitation the rights
      8  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
      9  * copies of the Software, and to permit persons to whom the Software is
     10  * furnished to do so, subject to the following conditions:
     11  *
     12  * The above copyright notice and this permission notice (including the next
     13  * paragraph) shall be included in all copies or substantial portions of the
     14  * Software.
     15  *
     16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
     22  * SOFTWARE.
     23  *
     24  * SPDX-License-Identifier: MIT
     25  */
     26 #include "ares-test.h"
     27 
     28 #ifdef HAVE_CONTAINER
     29 
     30 #include <sys/mount.h>
     31 #include <sys/types.h>
     32 #include <sys/stat.h>
     33 #include <fcntl.h>
     34 
     35 #include <iostream>
     36 #include <functional>
     37 #include <string>
     38 #include <sstream>
     39 #include <vector>
     40 
     41 namespace ares {
     42 namespace test {
     43 
     44 namespace {
     45 
     46 struct ContainerInfo {
     47   ContainerFilesystem* fs_;
     48   std::string hostname_;
     49   std::string domainname_;
     50   VoidToIntFn fn_;
     51 };
     52 
     53 int EnterContainer(void *data) {
     54   ContainerInfo *container = (ContainerInfo*)data;
     55 
     56   if (verbose) {
     57     std::cerr << "Running function in container {chroot='"
     58               << container->fs_->root() << "', hostname='" << container->hostname_
     59               << "', domainname='" << container->domainname_ << "'}"
     60               << std::endl;
     61   }
     62 
     63   // Ensure we are apparently root before continuing.
     64   int count = 10;
     65   while (getuid() != 0 && count > 0) {
     66     std::this_thread::sleep_for(std::chrono::milliseconds(100));
     67     count--;
     68   }
     69   if (getuid() != 0) {
     70     std::cerr << "Child in user namespace has uid " << getuid() << std::endl;
     71     return -1;
     72   }
     73   if (!container->fs_->mountpt().empty()) {
     74     // We want to bind mount this inside the specified directory.
     75     std::string innerdir = container->fs_->root() + container->fs_->mountpt();
     76     if (verbose) std::cerr << " mount --bind " << container->fs_->mountpt()
     77                            << " " << innerdir << std::endl;
     78     int rc = mount(container->fs_->mountpt().c_str(), innerdir.c_str(),
     79                    "none", MS_BIND, 0);
     80     if (rc != 0) {
     81       std::cerr << "Warning: failed to bind mount " << container->fs_->mountpt() << " at "
     82                 << innerdir << ", errno=" << errno << std::endl;
     83     }
     84   }
     85 
     86   // Move into the specified directory.
     87   if (chdir(container->fs_->root().c_str()) != 0) {
     88     std::cerr << "Failed to chdir('" << container->fs_->root()
     89               << "'), errno=" << errno << std::endl;
     90     return -1;
     91   }
     92   // And make it the new root directory;
     93   char buffer[PATH_MAX + 1];
     94   if (getcwd(buffer, PATH_MAX) == NULL) {
     95     std::cerr << "failed to retrieve cwd, errno=" << errno << std::endl;
     96     return -1;
     97   }
     98   buffer[PATH_MAX] = '\0';
     99   if (chroot(buffer) != 0) {
    100     std::cerr << "chroot('" << buffer << "') failed, errno=" << errno << std::endl;
    101     return -1;
    102   }
    103 
    104   // Set host/domainnames if specified
    105   if (!container->hostname_.empty()) {
    106     if (sethostname(container->hostname_.c_str(),
    107                     container->hostname_.size()) != 0) {
    108       std::cerr << "Failed to sethostname('" << container->hostname_
    109                 << "'), errno=" << errno << std::endl;
    110       return -1;
    111     }
    112   }
    113   if (!container->domainname_.empty()) {
    114     if (setdomainname(container->domainname_.c_str(),
    115                       container->domainname_.size()) != 0) {
    116       std::cerr << "Failed to setdomainname('" << container->domainname_
    117                 << "'), errno=" << errno << std::endl;
    118       return -1;
    119     }
    120   }
    121 
    122   return container->fn_();
    123 }
    124 
    125 }  // namespace
    126 
    127 // Run a function while:
    128 //  - chroot()ed into a particular directory
    129 //  - having a specified hostname/domainname
    130 
    131 int RunInContainer(ContainerFilesystem* fs, const std::string& hostname,
    132                    const std::string& domainname, VoidToIntFn fn) {
    133   const int stack_size = 1024 * 1024;
    134   std::vector<byte> stack(stack_size, 0);
    135   ContainerInfo container = {fs, hostname, domainname, fn};
    136 
    137   // Start a child process in a new user and UTS namespace
    138   pid_t child = clone(EnterContainer, stack.data() + stack_size,
    139                       CLONE_VM|CLONE_NEWNS|CLONE_NEWUSER|CLONE_NEWUTS|SIGCHLD,
    140                       (void *)&container);
    141   if (child < 0) {
    142     std::cerr << "Failed to clone(), errno=" << errno << std::endl;
    143     return -1;
    144   }
    145 
    146   // Build the UID map that makes us look like root inside the namespace.
    147   std::stringstream mapfiless;
    148   mapfiless << "/proc/" << child << "/uid_map";
    149   std::string mapfile = mapfiless.str();
    150   int fd = open(mapfile.c_str(), O_CREAT|O_WRONLY|O_TRUNC, 0644);
    151   if (fd < 0) {
    152     std::cerr << "Failed to create '" << mapfile << "'" << std::endl;
    153     return -1;
    154   }
    155   std::stringstream contentss;
    156   contentss << "0 " << getuid() << " 1" << std::endl;
    157   std::string content = contentss.str();
    158   ssize_t rc = write(fd, content.c_str(), content.size());
    159   if (rc != (ssize_t)content.size()) {
    160     std::cerr << "Failed to write uid map to '" << mapfile << "'" << std::endl;
    161   }
    162   close(fd);
    163 
    164   // Wait for the child process and retrieve its status.
    165   int status;
    166   waitpid(child, &status, 0);
    167   if (rc <= 0) {
    168     std::cerr << "Failed to waitpid(" << child << ")" << std::endl;
    169     return -1;
    170   }
    171   if (!WIFEXITED(status)) {
    172     std::cerr << "Child " << child << " did not exit normally" << std::endl;
    173     return -1;
    174   }
    175   return status;
    176 }
    177 
    178 ContainerFilesystem::ContainerFilesystem(NameContentList files, const std::string& mountpt) {
    179   rootdir_ = TempNam(nullptr, "ares-chroot");
    180   mkdir(rootdir_.c_str(), 0755);
    181   dirs_.push_front(rootdir_);
    182   for (const auto& nc : files) {
    183     std::string fullpath = rootdir_ + nc.first;
    184     size_t idx = fullpath.rfind('/');
    185     std::string dir;
    186     if (idx != SIZE_MAX) {
    187       dir = fullpath.substr(0, idx);
    188     } else {
    189       dir = fullpath;
    190     }
    191     EnsureDirExists(dir);
    192     files_.push_back(std::unique_ptr<TransientFile>(
    193         new TransientFile(fullpath, nc.second)));
    194   }
    195   if (!mountpt.empty()) {
    196     char buffer[PATH_MAX + 1];
    197     if (realpath(mountpt.c_str(), buffer)) {
    198       mountpt_ = buffer;
    199       std::string fullpath = rootdir_ + mountpt_;
    200       EnsureDirExists(fullpath);
    201     }
    202   }
    203 }
    204 
    205 ContainerFilesystem::~ContainerFilesystem() {
    206   files_.clear();
    207   for (const std::string& dir : dirs_) {
    208     rmdir(dir.c_str());
    209   }
    210 }
    211 
    212 void ContainerFilesystem::EnsureDirExists(const std::string& dir) {
    213   if (std::find(dirs_.begin(), dirs_.end(), dir) != dirs_.end()) {
    214     return;
    215   }
    216   size_t idx = dir.rfind('/');
    217   if (idx != SIZE_MAX) {
    218     std::string prevdir = dir.substr(0, idx);
    219     EnsureDirExists(prevdir);
    220   }
    221   // Ensure this directory is in the list before its ancestors.
    222   mkdir(dir.c_str(), 0755);
    223   dirs_.push_front(dir);
    224 }
    225 
    226 }  // namespace test
    227 }  // namespace ares
    228 
    229 #endif