| // Copyright 2019 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 "src/developer/cmd/tasks/process_task.h" | |
| #include <lib/fdio/spawn.h> | |
| #include <lib/zircon-internal/paths.h> | |
| #include <lib/zx/process.h> | |
| #include <sys/stat.h> | |
| #include <sys/types.h> | |
| #include <unistd.h> | |
| #include <memory> | |
| #include <sstream> | |
| #include <vector> | |
| #include "src/developer/cmd/autocomplete.h" | |
| namespace cmd { | |
| namespace { | |
| const char kDefaultPath[] = ZX_SHELL_ENV_PATH_VALUE; | |
| const char* GetPath() { | |
| const char* path = getenv("PATH"); | |
| if (!path) { | |
| path = kDefaultPath; | |
| } | |
| return path; | |
| } | |
| void EnumeratePath(fit::function<bool(const std::string&)> callback) { | |
| const char* path = GetPath(); | |
| std::istringstream input; | |
| input.str(path); | |
| for (std::string entry; std::getline(input, entry, ':');) { | |
| if (!callback(entry)) { | |
| return; | |
| } | |
| } | |
| } | |
| } // namespace | |
| ProcessTask::ProcessTask(async_dispatcher_t* dispatcher) | |
| : Task(dispatcher), waiter_(ZX_HANDLE_INVALID, ZX_PROCESS_TERMINATED) { | |
| waiter_.set_handler([this](async_dispatcher_t* dispatcher, async::Wait* wait, zx_status_t status, | |
| const zx_packet_signal_t* signal) { OnProcessTerminated(status); }); | |
| } | |
| ProcessTask::~ProcessTask() { | |
| if (job_) { | |
| job_.kill(); | |
| } | |
| } | |
| zx_status_t ProcessTask::Execute(Command command, Task::CompletionCallback callback) { | |
| callback_ = std::move(callback); | |
| zx_status_t status = zx::job::create(*zx::job::default_job(), 0, &job_); | |
| if (status != ZX_OK) { | |
| return status; | |
| } | |
| std::string executable = SearchPath(command.args()[0]); | |
| if (executable.empty()) { | |
| return ZX_ERR_NOT_FOUND; | |
| } | |
| std::vector<const char*> argv; | |
| argv.reserve(command.args().size()); | |
| for (const auto& arg : command.args()) { | |
| argv.push_back(arg.c_str()); | |
| } | |
| argv.push_back(nullptr); | |
| status = fdio_spawn(job_.get(), FDIO_SPAWN_CLONE_ALL, executable.c_str(), argv.data(), | |
| process_.reset_and_get_address()); | |
| if (status != ZX_OK) { | |
| return status; | |
| } | |
| waiter_.set_object(process_.get()); | |
| waiter_.Begin(dispatcher()); | |
| return ZX_ERR_ASYNC; | |
| } | |
| std::string ProcessTask::SearchPath(const std::string& name) { | |
| if (name.find('/') != std::string::npos) { | |
| return name; | |
| } | |
| std::string result; | |
| EnumeratePath([&name, &result](const std::string& directory) { | |
| std::string fullname = directory + '/' + name; | |
| struct stat sb = {}; | |
| if (stat(fullname.c_str(), &sb) < 0 || !S_ISREG(sb.st_mode)) { | |
| return true; | |
| } | |
| result = fullname; | |
| return false; | |
| }); | |
| return result; | |
| } | |
| void ProcessTask::OnProcessTerminated(zx_status_t status) { | |
| job_.kill(); | |
| job_.wait_one(ZX_JOB_TERMINATED, zx::time::infinite(), nullptr); | |
| job_.reset(); | |
| process_.reset(); | |
| auto callback = std::move(callback_); | |
| callback(); | |
| } | |
| void ProcessTask::CompleteCommand(Autocomplete* autocomplete) { | |
| if (autocomplete->fragment().find('/') != std::string::npos) { | |
| autocomplete->CompleteAsPath(); | |
| } else { | |
| EnumeratePath([autocomplete](const std::string& directory) { | |
| autocomplete->CompleteAsDirectoryEntry(directory); | |
| return true; | |
| }); | |
| } | |
| } | |
| } // namespace cmd |