Asylo
exec_tester.h
Go to the documentation of this file.
1 /*
2  *
3  * Copyright 2018 Asylo authors
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  */
18 
19 #ifndef ASYLO_TEST_UTIL_EXEC_TESTER_H_
20 #define ASYLO_TEST_UTIL_EXEC_TESTER_H_
21 
22 #include <unistd.h>
23 #include <iostream>
24 #include <sstream>
25 #include <string>
26 #include <vector>
27 
28 #include "absl/types/span.h"
29 
30 namespace asylo {
31 namespace experimental {
32 
33 /// Executes a subprocess. Monitors its output to a given file descriptor
34 /// (stdout by default) and checks its exit code.
35 class ExecTester {
36  public:
37  /// Constructs an `ExecTester` that will monitor an `execve` call on `args`.
38  ///
39  /// \param args The command-line arguments to the subprocess. The first
40  /// argument should be the executable to be run.
41  /// \param fd_to_check The file descriptor from which output is sent to
42  /// CheckLine().
43  ExecTester(const std::vector<std::string> &args,
44  int fd_to_check = STDOUT_FILENO);
45  virtual ~ExecTester() = default;
46 
47  /// Forks and execs the subprocess with the configured arguments. Redirects
48  /// the subprocess's stdin from `input` if non-empty. Validates the
49  /// subprocess's output to `fd_to_check` (from the constructor) with
50  /// CheckLine() and FinalCheck(). Stores the process status in `status` after
51  /// exit or signal termination.
52  ///
53  /// \param input The input to give to the subprocess on its stdin.
54  /// \param[out] status An output argument that is set to the subprocess's exit
55  /// status information, as returned by `waitpid()`.
56  /// \return The logical "and" of all CheckLine() results on the subprocess's
57  /// output to the configured file descriptor.
58  bool Run(const std::string &input, int *status);
59 
60  /// Returns `file_name` qualified to be in the same directory as the file
61  /// specified by `path`. This utility helps find binaries in common use cases
62  /// in Asylo.
63  ///
64  /// \deprecated Use Bazel's runfiles library for file paths.
65  /// \param path A path to a file.
66  /// \param file_name A path to a file relative to the directory containing
67  /// `path`.
68  /// \return A path to `file_name` within the same directory as the file at
69  /// `path`. If `path` is a relative path, then the returned path is
70  /// relative to the same directory. If `path` is absolute, then so is
71  /// the returned path.
72  static std::string BuildSiblingPath(const std::string &path,
73  const std::string &file_name);
74 
75  protected:
76  /// Checks a line of the subprocess's output to the configured file descriptor
77  /// for an expected property.
78  ///
79  /// \param line The line to check.
80  /// \return `true` if the property holds and `false` otherwise.
81  virtual bool CheckLine(const std::string &line) { return true; }
82 
83  /// Returns the final result given the accumulated CheckLine() results. This
84  /// is useful e.g., for determining hard bounds that CheckLine() soft-checks.
85  ///
86  /// \param accumulated The conjunction (logical "and") of the return value of
87  /// CheckLine() on each line of the subprocess's output to
88  /// the given file descriptor.
89  /// \return Whether the test as a whole was successful.
90  virtual bool FinalCheck(bool accumulated) { return accumulated; }
91 
92  private:
93  // ASSERT_* statements expect a void return type.
94  void RunWithAsserts(const std::string &input, bool *result, int *status);
95 
96  // Redirects subprocess stdin and the configured fd with pipes and executes
97  // the subprocess.
98  void DoExec(int read_stdin, int write_stdin, int read_fd_to_check,
99  int write_fd_to_check);
100 
101  // Reads contents of `fd` into `buffer` and runs CheckLine() on each
102  // newline-terminated piece of `buffer` as written to `linebuf`. Stores any
103  // unfinished line in `linebuf`, i.e., the characters in `buffer` that follow
104  // the last newline. The accumulated CheckLine() results are stored in
105  // `result`.
106  void CheckFD(int fd, absl::Span<char> buffer, std::stringstream *linebuf,
107  bool *result);
108 
109  // Polls output from `pid` to `fd` and calls CheckFD() to accumulate
110  // CheckLine() results. Sets `status` to `pid`'s termination status.
111  void ReadCheckLoop(pid_t pid, int fd, bool *result, int *status);
112 
113  std::vector<std::string> args_;
114  int fd_to_check_;
115 };
116 
117 } // namespace experimental
118 } // namespace asylo
119 
120 #endif // ASYLO_TEST_UTIL_EXEC_TESTER_H_