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  /// \param path A path to a file.
65  /// \param file_name A path to a file relative to the directory containing
66  /// `path`.
67  /// \return A path to `file_name` within the same directory as the file at
68  /// `path`. If `path` is a relative path, then the returned path is
69  /// relative to the same directory. If `path` is absolute, then so is
70  /// the returned path.
71  static std::string BuildSiblingPath(const std::string &path,
72  const std::string &file_name);
73 
74  protected:
75  /// Checks a line of the subprocess's output to the configured file descriptor
76  /// for an expected property.
77  ///
78  /// \param line The line to check.
79  /// \return `true` if the property holds and `false` otherwise.
80  virtual bool CheckLine(const std::string &line) { return true; }
81 
82  /// Returns the final result given the accumulated CheckLine() results. This
83  /// is useful e.g., for determining hard bounds that CheckLine() soft-checks.
84  ///
85  /// \param accumulated The conjunction (logical "and") of the return value of
86  /// CheckLine() on each line of the subprocess's output to
87  /// the given file descriptor.
88  /// \return Whether the test as a whole was successful.
89  virtual bool FinalCheck(bool accumulated) { return accumulated; }
90 
91  private:
92  // ASSERT_* statements expect a void return type.
93  void RunWithAsserts(const std::string &input, bool *result, int *status);
94 
95  // Redirects subprocess stdin and the configured fd with pipes and executes
96  // the subprocess.
97  void DoExec(int read_stdin, int write_stdin, int read_fd_to_check,
98  int write_fd_to_check);
99 
100  // Reads contents of `fd` into `buffer` and runs CheckLine() on each
101  // newline-terminated piece of `buffer` as written to `linebuf`. Stores any
102  // unfinished line in `linebuf`, i.e., the characters in `buffer` that follow
103  // the last newline. The accumulated CheckLine() results are stored in
104  // `result`.
105  void CheckFD(int fd, absl::Span<char> buffer, std::stringstream *linebuf,
106  bool *result);
107 
108  // Polls output from `pid` to `fd` and calls CheckFD() to accumulate
109  // CheckLine() results. Sets `status` to `pid`'s termination status.
110  void ReadCheckLoop(pid_t pid, int fd, bool *result, int *status);
111 
112  std::vector<std::string> args_;
113  int fd_to_check_;
114 };
115 
116 } // namespace experimental
117 } // namespace asylo
118 
119 #endif // ASYLO_TEST_UTIL_EXEC_TESTER_H_