| // Copyright 2022 The Centipede Authors. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // https://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #ifndef THIRD_PARTY_CENTIPEDE_COMMAND_H_ |
| #define THIRD_PARTY_CENTIPEDE_COMMAND_H_ |
| |
| #include <memory> |
| #include <optional> |
| #include <string> |
| #include <string_view> |
| #include <vector> |
| |
| #include "absl/status/status.h" |
| #include "absl/synchronization/mutex.h" |
| #include "absl/time/time.h" |
| |
| namespace fuzztest::internal { |
| |
| class Command final { |
| public: |
| struct Options { |
| // Arguments to pass to the executed command. The command is executed by the |
| // shell, so the arguments need to be shell-escaped. |
| // TODO(b/381910257): Escape the arguments for passing to the shell. |
| std::vector<std::string> args; |
| // Environment variables/values in the form "KEY=VALUE" to set in the |
| // subprocess executing the command. These are added to the environment |
| // variables inherited from the parent process. |
| std::vector<std::string> env_add; |
| // Environment variables to unset in the subprocess executing the command. |
| std::vector<std::string> env_remove; |
| // Redirect stdout to this file. If empty, use parent's STDOUT. |
| std::string stdout_file; |
| // Redirect stderr to this file. If empty, use parent's STDERR. If `out` == |
| // `err` and both are non-empty, stdout/stderr are combined. |
| std::string stderr_file; |
| // "@@" in the command will be replaced with `temp_file_path`. |
| std::string temp_file_path; |
| }; |
| |
| // Constructs a command to run the binary at `path` with the given `options`. |
| // The path can contain "@@" which will be replaced with |
| // `options.temp_file_path`. |
| explicit Command(std::string_view path, Options options); |
| |
| // Constructs a command to run the binary at `path` with default options. |
| explicit Command(std::string_view path); |
| |
| // Not movable or copyable to simplify the resource management logic. |
| Command(const Command& other) = delete; |
| Command& operator=(const Command& other) = delete; |
| Command(Command&& other) noexcept = delete; |
| Command& operator=(Command&& other) noexcept = delete; |
| |
| // Cleans up the fork server, if that was created. |
| ~Command(); |
| |
| // Returns a string representing the command, e.g. like this |
| // "env -u ENV1 ENV2=VAL2 path arg1 arg2 > out 2>& err" |
| std::string ToString() const; |
| |
| // Execute the command asynchronously. Returns true if it starts a new |
| // execution, false otherwise. Must be called only when the command |
| // is not executing. |
| bool ExecuteAsync(); |
| |
| // Returns whether the command is currently executing. |
| bool is_executing() const { return is_executing_; } |
| |
| // Waits for the command execution and returns the exit status if the |
| // execution finishes within `deadline`. Must be called only when the command |
| // is executing. execution or the execution times out. If interrupted, may |
| // call `RequestEarlyStop()` (see stop.h). |
| std::optional<int> Wait(absl::Time deadline); |
| |
| // Requests the command execution to stop. Must be called only when the |
| // command is executing. Note that after calling this, `Wait()` is still |
| // needed to complete the execution. |
| void RequestStop(); |
| |
| // Convenient method to execute synchronously. |
| int Execute() { |
| if (!ExecuteAsync()) return EXIT_FAILURE; |
| return Wait(absl::InfiniteFuture()).value_or(EXIT_FAILURE); |
| } |
| |
| // Attempts to start a fork server, returns true on success. |
| // Pipe files for the fork server are created in `temp_dir_path` |
| // with prefix `prefix`. |
| // See runner_fork_server.cc for details. |
| bool StartForkServer(std::string_view temp_dir_path, std::string_view prefix); |
| |
| // Accessors. |
| const std::string& path() const { return path_; } |
| |
| private: |
| struct ForkServerProps; |
| |
| int pid_ = -1; |
| bool is_executing_ = false; |
| |
| // Returns the status of the fork server process. Expects that the server was |
| // previously started using `StartForkServer()`. |
| absl::Status VerifyForkServerIsHealthy(); |
| |
| // Reads and returns the stdout of the command, if redirected to a file. If |
| // not redirected, returns a placeholder text. |
| std::string ReadRedirectedStdout() const; |
| // Reads and returns the stderr of the command, if redirected to a file that |
| // is also different from the redirected stdout. If not redirected, returns a |
| // placeholder text. |
| std::string ReadRedirectedStderr() const; |
| // Possibly logs information about a crash, starting with `message`, followed |
| // by the command line, followed by the redirected stdout and stderr read |
| // from `options_.out` and `options_.err` files, if any. |
| void LogProblemInfo(std::string_view message) const; |
| // Just as `LogCrashInfo()`, but logging occurs only when the FUZZTEST_VLOG |
| // level (set via `--v` or its equivalents) is >= `min_vlog`. |
| void VlogProblemInfo(std::string_view message, int vlog_level) const; |
| |
| const std::string path_; |
| const Options options_; |
| const std::string command_line_ = ToString(); |
| |
| std::unique_ptr<ForkServerProps> fork_server_; |
| }; |
| |
| // Get the shared mutex for execution logging for preventing confusing |
| // interlaced logs when multiple threads are logging at the same time. Note that |
| // the printing all log content at once is not viable due to the single log line |
| // length limit. |
| absl::Mutex& GetExecutionLoggingMutex(); |
| |
| } // namespace fuzztest::internal |
| |
| #endif // THIRD_PARTY_CENTIPEDE_COMMAND_H_ |