github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/test/util/multiprocess_util.cc (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  #include "test/util/multiprocess_util.h"
    16  
    17  #include <asm/unistd.h>
    18  #include <errno.h>
    19  #include <fcntl.h>
    20  #include <signal.h>
    21  #include <sys/prctl.h>
    22  #include <unistd.h>
    23  
    24  #include "absl/strings/str_cat.h"
    25  #include "test/util/cleanup.h"
    26  #include "test/util/file_descriptor.h"
    27  #include "test/util/posix_error.h"
    28  #include "test/util/save_util.h"
    29  #include "test/util/test_util.h"
    30  
    31  namespace gvisor {
    32  namespace testing {
    33  
    34  namespace {
    35  
    36  // exec_fn wraps a variant of the exec family, e.g. execve or execveat.
    37  PosixErrorOr<Cleanup> ForkAndExecHelper(const std::function<void()>& exec_fn,
    38                                          const std::function<void()>& fn,
    39                                          pid_t* child, int* execve_errno) {
    40    int pfds[2];
    41    int ret = pipe2(pfds, O_CLOEXEC);
    42    if (ret < 0) {
    43      return PosixError(errno, "pipe failed");
    44    }
    45    FileDescriptor rfd(pfds[0]);
    46    FileDescriptor wfd(pfds[1]);
    47  
    48    int parent_stdout = dup(STDOUT_FILENO);
    49    if (parent_stdout < 0) {
    50      return PosixError(errno, "dup stdout");
    51    }
    52    int parent_stderr = dup(STDERR_FILENO);
    53    if (parent_stdout < 0) {
    54      return PosixError(errno, "dup stderr");
    55    }
    56  
    57    pid_t pid = fork();
    58    if (pid < 0) {
    59      return PosixError(errno, "fork failed");
    60    } else if (pid == 0) {
    61      // Child.
    62      rfd.reset();
    63      if (dup2(parent_stdout, STDOUT_FILENO) < 0) {
    64        _exit(3);
    65      }
    66      if (dup2(parent_stderr, STDERR_FILENO) < 0) {
    67        _exit(4);
    68      }
    69      close(parent_stdout);
    70      close(parent_stderr);
    71  
    72      // Clean ourself up in case the parent doesn't.
    73      if (prctl(PR_SET_PDEATHSIG, SIGKILL)) {
    74        _exit(3);
    75      }
    76  
    77      if (fn) {
    78        fn();
    79      }
    80  
    81      // Call variant of exec function.
    82      exec_fn();
    83  
    84      int error = errno;
    85      if (WriteFd(pfds[1], &error, sizeof(error)) != sizeof(error)) {
    86        // We can't do much if the write fails, but we can at least exit with a
    87        // different code.
    88        _exit(2);
    89      }
    90      _exit(1);
    91    }
    92  
    93    // Parent.
    94    if (child) {
    95      *child = pid;
    96    }
    97  
    98    auto cleanup = Cleanup([pid] {
    99      kill(pid, SIGKILL);
   100      RetryEINTR(waitpid)(pid, nullptr, 0);
   101    });
   102  
   103    wfd.reset();
   104  
   105    int read_errno;
   106    ret = ReadFd(rfd.get(), &read_errno, sizeof(read_errno));
   107    if (ret == 0) {
   108      // Other end of the pipe closed, execve must have succeeded.
   109      read_errno = 0;
   110    } else if (ret < 0) {
   111      return PosixError(errno, "read pipe failed");
   112    } else if (ret != sizeof(read_errno)) {
   113      return PosixError(EPIPE, absl::StrCat("pipe read wrong size ", ret));
   114    }
   115  
   116    if (execve_errno) {
   117      *execve_errno = read_errno;
   118    }
   119  
   120    return std::move(cleanup);
   121  }
   122  
   123  }  // namespace
   124  
   125  PosixErrorOr<Cleanup> ForkAndExec(const std::string& filename,
   126                                    const ExecveArray& argv,
   127                                    const ExecveArray& envv,
   128                                    const std::function<void()>& fn, pid_t* child,
   129                                    int* execve_errno) {
   130    char* const* argv_data = argv.get();
   131    char* const* envv_data = envv.get();
   132    const std::function<void()> exec_fn = [=] {
   133      execve(filename.c_str(), argv_data, envv_data);
   134    };
   135    return ForkAndExecHelper(exec_fn, fn, child, execve_errno);
   136  }
   137  
   138  PosixErrorOr<Cleanup> ForkAndExecveat(const int32_t dirfd,
   139                                        const std::string& pathname,
   140                                        const ExecveArray& argv,
   141                                        const ExecveArray& envv, const int flags,
   142                                        const std::function<void()>& fn,
   143                                        pid_t* child, int* execve_errno) {
   144    char* const* argv_data = argv.get();
   145    char* const* envv_data = envv.get();
   146    const std::function<void()> exec_fn = [=] {
   147      syscall(__NR_execveat, dirfd, pathname.c_str(), argv_data, envv_data,
   148              flags);
   149    };
   150    return ForkAndExecHelper(exec_fn, fn, child, execve_errno);
   151  }
   152  
   153  PosixErrorOr<int> InForkedProcess(const std::function<void()>& fn) {
   154    pid_t pid = fork();
   155    if (pid == 0) {
   156      fn();
   157      TEST_CHECK_MSG(!::testing::Test::HasFailure(),
   158                     "EXPECT*/ASSERT* failed. These are not async-signal-safe "
   159                     "and must not be called from fn.");
   160      _exit(0);
   161    }
   162    MaybeSave();
   163    if (pid < 0) {
   164      return PosixError(errno, "fork failed");
   165    }
   166  
   167    int status;
   168    if (waitpid(pid, &status, 0) < 0) {
   169      return PosixError(errno, "waitpid failed");
   170    }
   171  
   172    return status;
   173  }
   174  
   175  }  // namespace testing
   176  }  // namespace gvisor