blob: 51505e6692a4032788ecf9878f341d7fc2214a4f [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 <lib/fdio/spawn.h>
#include <lib/syslog/cpp/macros.h>
#include <lib/zx/job.h>
#include <stdio.h>
#include <unistd.h>
#include <zircon/processargs.h>
#include <zircon/status.h>
#include <zircon/syscalls.h>
#include <zircon/syscalls/object.h>
#include <iostream>
#include <iterator>
#include <vector>
#include "src/lib/fxl/strings/string_printf.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, std::size(actions), actions, proc, err_msg);
return status;
}
} // namespace
const char kBinaryPath[] = "/pkgfs/packages/debug_agent_helpers/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) {
FX_LOGS(ERROR) << "Could not create a child job.";
exit(1);
}
FX_LOGS(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;
};
std::vector<Process> processes;
FX_LOGS(INFO) << "Waiting for output.";
std::vector<char> current_line;
while (true) {
int i = getc(stdin);
if (i < 0)
break;
char c = static_cast<char>(i);
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) {
FX_LOGS(INFO) << "Found \"exit\" in the input. Exiting.";
exit(0);
}
// Spawn a process the fdio way.
int pipe_fds[2];
if (pipe(pipe_fds) != 0) {
FX_LOGS(ERROR) << "Could not create pipes!";
exit(1);
}
FX_LOGS(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) {
FX_LOGS(ERROR) << "Could not create process " << process.name << ": "
<< zx_status_get_string(status);
exit(1);
}
FX_LOGS(INFO) << "Created process " << process.name
<< " with KOID: " << GetKoidForHandle(process.proc_handle);
processes.push_back(std::move(process));
current_line.clear();
}
return 0;
}