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 namespace asylo {
29 namespace experimental {
30 
31 /// Executes a subprocess. Monitors its output to a given file descriptor
32 /// (stdout by default) and checks its exit code.
33 class ExecTester {
34  public:
35  /// Constructs an `ExecTester` that will monitor an `execve` call on `args`.
36  ///
37  /// \param args The command-line arguments to the subprocess. The first
38  /// argument should be the executable to be run.
39  /// \param fd_to_check The file descriptor from which output is sent to
40  /// CheckLine().
41  ExecTester(const std::vector<std::string> &args, int fd_to_check = STDOUT_FILENO);
42  virtual ~ExecTester() = default;
43 
44  /// Forks and execs the subprocess with the configured arguments. Redirects
45  /// the subprocess's stdin from `input` if non-empty. Validates the
46  /// subprocess's output to `fd_to_check` (from the constructor) with
47  /// CheckLine() and FinalCheck(). Stores the process status in `status` after
48  /// exit or signal termination.
49  ///
50  /// \param input The input to give to the subprocess on its stdin.
51  /// \param[out] status An output argument that is set to the subprocess's exit
52  /// status information, as returned by `waitpid()`.
53  /// \return The logical "and" of all CheckLine() results on the subprocess's
54  /// output to the configured file descriptor.
55  bool Run(const std::string &input, int *status);
56 
57  /// Returns `file_name` qualified to be in the same directory as the file
58  /// specified by `path`. This utility helps find binaries in common use cases
59  /// in Asylo.
60  ///
61  /// \param path A path to a file.
62  /// \param file_name A path to a file relative to the directory containing
63  /// `path`.
64  /// \return A path to `file_name` within the same directory as the file at
65  /// `path`. If `path` is a relative path, then the returned path is
66  /// relative to the same directory. If `path` is absolute, then so is
67  /// the returned path.
68  static std::string BuildSiblingPath(const std::string &path, const std::string &file_name);
69 
70  protected:
71  /// Checks a line of the subprocess's output to the configured file descriptor
72  /// for an expected property.
73  ///
74  /// \param line The line to check.
75  /// \return `true` if the property holds and `false` otherwise.
76  virtual bool CheckLine(const std::string &line) { return true; }
77 
78  /// Returns the final result given the accumulated CheckLine() results. This
79  /// is useful e.g., for determining hard bounds that CheckLine() soft-checks.
80  ///
81  /// \param accumulated The conjunction (logical "and") of the return value of
82  /// CheckLine() on each line of the subprocess's output to
83  /// the given file descriptor.
84  /// \return Whether the test as a whole was successful.
85  virtual bool FinalCheck(bool accumulated) { return accumulated; }
86 
87  private:
88  // ASSERT_* statements expect a void return type.
89  void RunWithAsserts(const std::string &input, bool *result, int *status);
90 
91  // Redirects subprocess stdin and the configured fd with pipes and executes
92  // the subprocess.
93  void DoExec(int read_stdin, int write_stdin, int read_fd_to_check,
94  int write_fd_to_check);
95 
96  // Reads contents of `fd` into `buf` and runs CheckLine() on each
97  // newline-terminated piece of `buf` as written to `linebuf`. Stores any
98  // unfinished line in `linebuf`, i.e., the characters in `buf` that follow the
99  // last newline. The accumulated CheckLine() results are stored in `result`.
100  void CheckFD(int fd, char *buf, size_t bufsize, std::stringstream *linebuf,
101  bool *result);
102 
103  // Polls output from `pid` to `fd` and calls CheckFD() to accumulate
104  // CheckLine() results. Sets `status` to `pid`'s termination status.
105  void ReadCheckLoop(pid_t pid, int fd, bool *result, int *status);
106 
107  std::vector<std::string> args_;
108  int fd_to_check_;
109 };
110 
111 } // namespace experimental
112 } // namespace asylo
113 
114 #endif // ASYLO_TEST_UTIL_EXEC_TESTER_H_