blob: fc3314b41851d11e6726150a8c0e439b857fa305 [file] [log] [blame]
// 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/console.h"
#include <errno.h>
#include <fcntl.h>
#include <poll.h>
#include <unistd.h>
#include <zircon/assert.h>
#include <utility>
namespace cmd {
Console::Client::~Client() = default;
Console::Console(Client* client, async_dispatcher_t* dispatcher, int input_fd)
: client_(client),
input_fd_(input_fd),
input_waiter_(dispatcher),
interrupt_waiter_(dispatcher) {}
Console::~Console() {
if (tty_) {
fdio_unsafe_release(tty_);
tty_ = nullptr;
}
}
void Console::Init(std::string prompt) {
fcntl(input_fd_, F_SETFL, fcntl(input_fd_, F_GETFL, 0) | O_NONBLOCK);
if (isatty(input_fd_)) {
tty_ = fdio_unsafe_fd_to_io(input_fd_);
WaitForInterruptAsynchronously();
}
line_input_.Init([this](const std::string& line) { OnAccept(line); }, std::move(prompt));
line_input_.SetEofCallback([this] { OnError(ZX_ERR_PEER_CLOSED); });
line_input_.SetAutocompleteCallback([this](const std::string& line) {
Autocomplete autocomplete(line);
client_->OnConsoleAutocomplete(&autocomplete);
return autocomplete.TakeCompletions();
});
}
void Console::GetNextCommand() {
ZX_DEBUG_ASSERT(!should_read_);
line_input_.Show();
should_read_ = true;
WaitForInputAsynchronously();
}
void Console::WaitForInputAsynchronously() {
input_waiter_.Wait(
[this](zx_status_t status, uint32_t observed) {
ZX_DEBUG_ASSERT(should_read_);
if (status != ZX_OK) {
OnError(status);
return;
}
for (;;) {
char ch = 0;
ssize_t rv = read(input_fd_, &ch, 1);
if (rv < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
WaitForInputAsynchronously();
return;
}
OnError(ZX_ERR_IO);
return;
}
if (rv == 0) {
OnError(ZX_ERR_PEER_CLOSED);
return;
}
line_input_.OnInput(ch);
if (!should_read_) {
return;
}
}
},
input_fd_, POLLIN);
}
void Console::WaitForInterruptAsynchronously() {
interrupt_waiter_.Wait(
[this](zx_status_t status, uint32_t observed) {
if (status != ZX_OK) {
return;
}
uint32_t events = 0;
zx_status_t pty_status = ZX_OK;
status = fuchsia_hardware_pty_DeviceReadEvents(fdio_unsafe_borrow_channel(tty_),
&pty_status, &events);
WaitForInterruptAsynchronously();
if (events & fuchsia_hardware_pty_EVENT_INTERRUPT) {
if (should_read_) {
line_input_.OnInput(line_input::SpecialCharacters::kKeyControlC);
} else {
client_->OnConsoleInterrupt();
}
}
},
input_fd_, POLLPRI);
}
void Console::OnAccept(const std::string& line) {
ZX_DEBUG_ASSERT(should_read_);
line_input_.AddToHistory(line);
Command command;
command.Parse(line);
zx_status_t status = client_->OnConsoleCommand(std::move(command));
ZX_DEBUG_ASSERT(status == ZX_ERR_STOP || status == ZX_ERR_NEXT || status == ZX_ERR_ASYNC);
if (status != ZX_ERR_NEXT) {
line_input_.Hide();
should_read_ = false;
}
}
void Console::OnError(zx_status_t status) {
ZX_DEBUG_ASSERT(should_read_);
line_input_.Hide();
should_read_ = false;
client_->OnConsoleError(status);
}
} // namespace cmd