blob: d7814ff36005f098b617991736cdd0f71557dbc5 [file] [log] [blame]
// Copyright 2018 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.
#include <stdio.h>
#include <unistd.h>
#include <iostream>
#include <vector>
#include <lib/fdio/spawn.h>
#include <lib/fxl/arraysize.h>
#include <lib/fxl/logging.h>
#include <lib/fxl/strings/string_printf.h>
#include <lib/zx/job.h>
#include <zircon/status.h>
#include <zircon/syscalls.h>
#include <zircon/syscalls/object.h>
#include <zircon/processargs.h>
// ProcessSpawner is a simple utility that waits for user input on stdin and
// creates a new process when anything that doens't say "exit" in it is entered.
//
// This is useful for debugging process attaching and similar functionality.
namespace {
uint64_t GetKoidForHandle(zx_handle_t handle) {
zx_info_handle_basic_t info;
zx_status_t res = zx_object_get_info(handle, ZX_INFO_HANDLE_BASIC, &info,
sizeof(info), NULL, NULL);
if (res != ZX_OK)
return 0;
return static_cast<uint64_t>(info.koid);
}
zx_status_t LaunchProcess(zx_handle_t job, const std::vector<const char*>& argv,
const char* name, int outfd, zx_handle_t* proc) {
std::vector<const char*> normalized_argv = argv;
normalized_argv.push_back(nullptr);
fdio_spawn_action_t actions[] = {
{.action = FDIO_SPAWN_ACTION_CLONE_FD,
.fd = {.local_fd = outfd, .target_fd = STDOUT_FILENO}},
{.action = FDIO_SPAWN_ACTION_CLONE_FD,
.fd = {.local_fd = STDIN_FILENO, .target_fd = STDIN_FILENO}},
{.action = FDIO_SPAWN_ACTION_CLONE_FD,
.fd = {.local_fd = STDERR_FILENO, .target_fd = STDERR_FILENO}},
{.action = FDIO_SPAWN_ACTION_SET_NAME, .name = {.data = name}}};
char err_msg[FDIO_SPAWN_ERR_MSG_MAX_LENGTH];
zx_status_t status =
fdio_spawn_etc(job, FDIO_SPAWN_CLONE_ALL, normalized_argv.front(),
normalized_argv.data(), nullptr, arraysize(actions),
actions, proc, err_msg);
return status;
}
} // namespace
const char kBinaryPath[] =
"/pkgfs/packages/debug_agent_tests/0/bin/process_loop";
int main() {
zx_handle_t default_job = zx_job_default();
zx_handle_t child_job;
if (zx_status_t status = zx_job_create(default_job, 0u, &child_job);
status != ZX_OK) {
FXL_LOG(ERROR) << "Could not create a child job.";
exit(1);
}
FXL_LOG(INFO) << "Parent job: " << GetKoidForHandle(default_job)
<< ", Created job: " << GetKoidForHandle(child_job);
// We're going to keep a list of the created processes.
struct Process {
std::string name;
zx_handle_t proc_handle;
zx_handle_t vmar_handle;
};
std::vector<Process> processes;
FXL_LOG(INFO) << "Waiting for output.";
std::vector<char> current_line;
while (true) {
char c = getc(stdin);
printf("%c", c);
fflush(stdout);
// We acumulate characters to that the user can write exit if they want to
// exit. Not a very good UI, but works for the testing purposes.
if (c >= 'a' && c <= 'z') {
current_line.push_back(c);
continue;
}
current_line.push_back(0);
// If the user wrote exit somewhere, we exit.
std::string cmd(current_line.data());
if (cmd.find("exit") != std::string::npos) {
FXL_LOG(INFO) << "Found \"exit\" in the input. Exiting.";
exit(0);
}
// Spawn a process the fdio way.
int pipe_fds[2];
if (pipe(pipe_fds) != 0) {
FXL_LOG(ERROR) << "Could not create pipes!";
exit(1);
}
FXL_LOG(INFO) << "Creating process.";
Process process;
process.name = fxl::StringPrintf("process-%lu", processes.size());
zx_status_t status =
LaunchProcess(child_job, {kBinaryPath}, process.name.data(),
pipe_fds[0], &process.proc_handle);
if (status != ZX_OK) {
FXL_LOG(ERROR) << "Could not create process " << process.name << ": "
<< zx_status_get_string(status);
exit(1);
}
FXL_LOG(INFO) << "Created process " << process.name
<< " with KOID: " << GetKoidForHandle(process.proc_handle);
processes.push_back(std::move(process));
current_line.clear();
}
return 0;
}