blob: 4c1d1b9e18e7c958ee71b2d6fcd7482f0499be3e [file] [log] [blame] [edit]
// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef SRC_SYS_FUZZING_COMMON_CHILD_PROCESS_H_
#define SRC_SYS_FUZZING_COMMON_CHILD_PROCESS_H_
#include <lib/fdio/spawn.h>
#include <lib/zx/process.h>
#include <stddef.h>
#include <stdint.h>
#include <array>
#include <condition_variable>
#include <memory>
#include <string>
#include <thread>
#include <vector>
#include "src/lib/fxl/synchronization/thread_annotations.h"
#include "src/sys/fuzzing/common/async-deque.h"
#include "src/sys/fuzzing/common/async-types.h"
#include "src/sys/fuzzing/common/status.h"
namespace fuzzing {
class ChildProcess final {
public:
explicit ChildProcess(ExecutorPtr executor);
~ChildProcess();
bool is_killed() const { return killed_; }
// Adds a command line argument for the process. The first |arg| added should be the executable
// path relative to the "/pkg" directory, i.e. the same value as might be found in
// `program.binary` field in a component manifest.
void AddArg(const std::string& arg);
// Adds all of the given |args|.
void AddArgs(std::initializer_list<const char*> args);
// Sets an environment variable. Setting the same variable multiple times updates the value.
void SetEnvVar(const std::string& name, const std::string& value);
// Adds a writeable pipe to the standard input of the spawned process.
// Returns ZX_ERR_BAD_STATE if called twice without `Reset`.
__WARN_UNUSED_RESULT zx_status_t AddStdinPipe();
// Adds a readable pipe from the indicated output file descriptors.
// Returns ZX_ERR_BAD_STATE if called twice without `Reset`.
__WARN_UNUSED_RESULT zx_status_t AddStdoutPipe();
__WARN_UNUSED_RESULT zx_status_t AddStderrPipe();
// Takes a `channel` to be passed as a startup channel to the child process with the given `id`.
void AddChannel(uint32_t id, zx::channel channel);
// Spawns the new child process. Returns an error if a previous process was spawned but has not
// been |Kill|ed and |Reset|, or if spawning fails.
__WARN_UNUSED_RESULT zx_status_t Spawn();
// Returns whether the child process has been spawned and is running.
bool IsAlive();
// Return a copy of the process.
__WARN_UNUSED_RESULT zx_status_t Duplicate(zx::process* out);
// Writes a line to process's stdin. Returns an error if the process is not alive of if stdin has
// been closed.
zx_status_t WriteToStdin(const std::string& line);
// Returns a promise to read from the process's stdout or stderr, respectively. The promise will
// read up to a newline or EOF, whichever comes first. The promise will return an error if the
// file descriptor is closed or was cloned, and will return |ZX_ERR_STOP| on EOF.
ZxPromise<std::string> ReadFromStdout();
ZxPromise<std::string> ReadFromStderr();
// Closes the indicated pipe to or from the spawned process. May be called before or after
// the process is `Spawn`ed.
void CloseStdin();
void CloseStdout();
void CloseStderr();
// Collect process-related statistics for the child process.
ZxResult<ProcessStats> GetStats();
// Promises to wait for the spawned process to terminate and return its return code.
ZxPromise<int64_t> Wait();
// Returns a promise that kills the spawned process and waits for it to fully terminate. This
// leaves the process in a "killed" state; it must be |Reset| before it can be reused.
ZxPromise<> Kill();
// Returns this object to an initial state, from which |Spawn| can be called again. This
// effectively kills the process, but does not wait for it to fully terminate. Callers should
// prefer to |Kill| and then |Reset|. |AddArgs|, |SetFdAction|, and/or |SetChannels| will need to
// be called again before respawning.
void Reset();
private:
// Creates a pipe and adds an `fdio_spawn_action_t` that installs one end of the pipe at the given
// `target_fd` when the process is `Spawn`ed.
//
// Exactly one of `out_rpipe` and `out_wpipe` must be non-null:
// * A non-null `out_rpipe` indicates the pipe should be from the process, and is used to return
// the readable end.
// * A non-null `out_wpipe` indicates the pipe should be to the process, and is used to return
// the writeable end.
//
// Returns an error if the process is already spawned or if the pipe couldn;t be created.
zx_status_t AddPipe(int target_fd, int* out_rpipe, int* out_wpipe);
// Kills the process and waits for the output threads to join.
void KillSync();
ExecutorPtr executor_;
bool spawned_ = false;
bool killed_ = false;
// Parameters for |fdio_spawn_etc|.
std::vector<std::string> args_;
std::unordered_map<std::string, std::string> envvars_;
std::vector<fdio_spawn_action_t> actions_;
// The handle to the spawned process.
zx::process process_;
zx_info_process_t info_;
// Variables to offload writing data to standard input to a dedicated thread.
std::mutex mutex_;
std::thread stdin_thread_;
bool input_closed_ FXL_GUARDED_BY(mutex_) = false;
std::vector<std::string> input_lines_ FXL_GUARDED_BY(mutex_);
std::condition_variable input_cond_;
// Variables to offload reading data from standard output to a dedicated thread.
AsyncReceiverPtr<std::string> stdout_;
std::thread stdout_thread_;
zx_status_t stdout_result_ = ZX_OK;
// Variables to offload reading data from standard error to a dedicated thread.
AsyncReceiverPtr<std::string> stderr_;
std::thread stderr_thread_;
zx_status_t stderr_result_ = ZX_OK;
Scope scope_;
FXL_DISALLOW_COPY_ASSIGN_AND_MOVE(ChildProcess);
};
} // namespace fuzzing
#endif // SRC_SYS_FUZZING_COMMON_CHILD_PROCESS_H_