blob: 8aa1db20cc47a67f5b8f7ddf1c6484a5821a9ac8 [file] [log] [blame]
// Copyright 2016 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 <string.h>
#include <zircon/process.h>
#include <zircon/processargs.h>
#include <zircon/syscalls.h>
#include <linenoise/linenoise.h>
static zx_handle_t ctrl_channel = ZX_HANDLE_INVALID;
// Maximum length of a history entry, including the ending '\n'.
static const size_t kMaxHistoryEntrySize = 1024;
static const char kGetHistoryCommand[] = "get_history";
static const size_t kGetHistoryCommandLen = sizeof(kGetHistoryCommand) - 1;
static const char kAddLocalEntryCommand[] = "add_local_entry:";
static const size_t kAddLocalEntryCommandLen = sizeof(kAddLocalEntryCommand) - 1;
static const char kAddRemoteEntryCommand[] = "add_remote_entry:";
static const size_t kAddRemoteEntryCommandLen = sizeof(kAddRemoteEntryCommand) - 1;
void controller_init() {
ctrl_channel = zx_get_startup_handle(PA_HND(PA_USER1, 0));
if (ctrl_channel == ZX_HANDLE_INVALID) {
// Running without a shell controller.
return;
}
// Initialize the shell history.
zx_status_t status = zx_channel_write(ctrl_channel, 0, kGetHistoryCommand,
kGetHistoryCommandLen, NULL, 0);
if (status != ZX_OK) {
fprintf(stderr,
"Failed to write the get_history command to the ctrl channel.\n");
return;
}
status = zx_object_wait_one(ctrl_channel,
ZX_CHANNEL_READABLE | ZX_CHANNEL_PEER_CLOSED,
zx_deadline_after(ZX_SEC(5)), NULL);
if (status != ZX_OK) {
fprintf(stderr, "Failed to wait on the ctrl channel.\n");
return;
}
zx_handle_t history_vmo;
uint32_t read_bytes = 0;
uint32_t read_handles = 0;
status = zx_channel_read(ctrl_channel, 0, NULL, &history_vmo, 0,
1, &read_bytes, &read_handles);
if (status != ZX_OK) {
fprintf(stderr,
"Failed to read the ctrl response to the get_history command.\n");
return;
}
uint64_t history_vmo_size = 0;
status = zx_vmo_get_size(history_vmo, &history_vmo_size);
if (status != ZX_OK) {
fprintf(stderr, "Failed to get the size of the history vmo.\n");
return;
}
uint64_t history_vmo_offset = 0;
while (history_vmo_offset < history_vmo_size) {
char buffer[kMaxHistoryEntrySize];
size_t actually_read = 0;
status = zx_vmo_read(history_vmo, &buffer, history_vmo_offset,
sizeof(buffer), &actually_read);
if (status != ZX_OK) {
fprintf(stderr, "Failed to read from the history vmo.\n");
return;
}
// We only move |history_vmo_offset| to the next byte after the previous
// '\n', so the beginning of buffer should always be the beginning of a new
// entry.
char* entry_start = buffer;
char* const buffer_end = buffer + actually_read;
bool found_an_entry = false;
for (char* it = buffer; it < buffer_end; it++) {
if (*it == '\n') {
*it = '\0';
linenoiseHistoryAdd(entry_start);
history_vmo_offset += it + 1 - entry_start;
entry_start = it + 1;
found_an_entry = true;
}
}
if (!found_an_entry) {
fprintf(stderr, "Incorrect format of the history vmo.\n");
return;
}
}
}
void controller_add_local_entry(const char* entry, size_t length) {
if (ctrl_channel == ZX_HANDLE_INVALID || length > kMaxHistoryEntrySize) {
return;
}
char buffer[kAddLocalEntryCommandLen + kMaxHistoryEntrySize];
memcpy(buffer, kAddLocalEntryCommand, kAddLocalEntryCommandLen);
memcpy(buffer + kAddLocalEntryCommandLen, entry, length);
zx_status_t status = zx_channel_write(ctrl_channel, 0,
buffer, kAddLocalEntryCommandLen + length,
NULL, 0);
if (status != ZX_OK) {
fprintf(stderr,
"Failed to write the add_to_history command to the ctrl channel\n");
zx_handle_close(ctrl_channel);
ctrl_channel = ZX_HANDLE_INVALID;
return;
}
}
void controller_pull_remote_entries() {
if (ctrl_channel == ZX_HANDLE_INVALID) {
return;
}
// The commands should not be bigger than the name of the command + max size
// of a history entry.
char buffer[kMaxHistoryEntrySize + 100];
while(true) {
uint32_t read_bytes = 0;
zx_status_t status = zx_channel_read(ctrl_channel, 0, buffer, NULL,
sizeof(buffer) - 1, 0, &read_bytes, NULL);
if (status == ZX_OK) {
if (strncmp(buffer, kAddRemoteEntryCommand, kAddRemoteEntryCommandLen) != 0) {
fprintf(stderr, "Unrecognized shell controller command.\n");
continue;
}
buffer[read_bytes] = '\0';
const char* start = buffer + kAddRemoteEntryCommandLen;
linenoiseHistoryAdd(start);
} else if (status == ZX_ERR_SHOULD_WAIT) {
return;
} else {
fprintf(stderr, "Failed to read the command from the ctrl channel, status: %u.\n", status);
return;
}
}
}
#ifdef mkinit
INCLUDE "controller.h"
INIT {
controller_init();
}
#endif