blob: aa9983e97fa26816d474de3c47f13650f6c308d3 [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/zxio/inception.h>
#include <lib/zxio/null.h>
#include <lib/zxio/ops.h>
#include <stdlib.h>
#include <string.h>
#include <zircon/syscalls.h>
#include <zircon/syscalls/log.h>
#define LOGBUF_MAX (ZX_LOG_RECORD_MAX - sizeof(zx_log_record_t))
typedef struct zxio_debuglog_buffer {
unsigned next;
char pending[LOGBUF_MAX];
} zxio_debuglog_buffer_t;
// A |zxio_t| backend that uses a debuglog.
//
// The |handle| handle is a Zircon debuglog object.
typedef struct zxio_debuglog {
zxio_t io;
zx_handle_t handle;
zxio_debuglog_buffer_t* buffer;
} zxio_debuglog_t;
static_assert(sizeof(zxio_debuglog_t) <= sizeof(zxio_storage_t),
"zxio_debuglog_t must fit inside zxio_storage_t.");
static zx_status_t zxio_debuglog_close(zxio_t* io) {
zxio_debuglog_t* debuglog = reinterpret_cast<zxio_debuglog_t*>(io);
zx_handle_t handle = debuglog->handle;
debuglog->handle = ZX_HANDLE_INVALID;
zx_handle_close(handle);
if (debuglog->buffer != nullptr) {
free(debuglog->buffer);
debuglog->buffer = nullptr;
}
return ZX_OK;
}
zx_status_t zxio_debuglog_clone(zxio_t* io, zx_handle_t* out_handle) {
zxio_debuglog_t* debuglog = reinterpret_cast<zxio_debuglog_t*>(io);
return zx_handle_duplicate(debuglog->handle, ZX_RIGHT_SAME_RIGHTS,
out_handle);
}
static zx_status_t zxio_debuglog_write(zxio_t* io, const void* buffer, size_t capacity,
size_t* out_actual) {
zxio_debuglog_t* debuglog = reinterpret_cast<zxio_debuglog_t*>(io);
if (debuglog->buffer == nullptr) {
debuglog->buffer = static_cast<zxio_debuglog_buffer_t*>(
calloc(1, sizeof(*debuglog->buffer)));
if (debuglog->buffer == nullptr) {
*out_actual = capacity;
return ZX_OK;
}
}
zxio_debuglog_buffer_t* outgoing = debuglog->buffer;
const uint8_t* data = static_cast<const uint8_t*>(buffer);
size_t n = capacity;
while (n-- > 0u) {
char c = *data++;
if (c == '\n') {
zx_debuglog_write(debuglog->handle, 0u, outgoing->pending,
outgoing->next);
outgoing->next = 0u;
continue;
}
if (c < ' ') {
continue;
}
outgoing->pending[outgoing->next++] = c;
if (outgoing->next == LOGBUF_MAX) {
zx_debuglog_write(debuglog->handle, 0u, outgoing->pending,
outgoing->next);
outgoing->next = 0u;
continue;
}
}
*out_actual = capacity;
return ZX_OK;
}
static zx_status_t zxio_debuglog_isatty(zxio_t* io, bool* tty) {
// debuglog needs to be a tty in order to tell stdio
// to use line-buffering semantics - bunching up log messages
// for an arbitrary amount of time makes for confusing results!
*tty = true;
return ZX_OK;
}
static constexpr zxio_ops_t zxio_debuglog_ops = ([]() {
zxio_ops_t ops = zxio_default_ops;
ops.close = zxio_debuglog_close;
ops.clone = zxio_debuglog_clone;
ops.write = zxio_debuglog_write;
ops.isatty = zxio_debuglog_isatty;
return ops;
})();
zx_status_t zxio_debuglog_init(zxio_storage_t* storage, zx_handle_t handle) {
zxio_debuglog_t* debuglog = reinterpret_cast<zxio_debuglog_t*>(storage);
zxio_init(&debuglog->io, &zxio_debuglog_ops);
debuglog->handle = handle;
debuglog->buffer = nullptr;
return ZX_OK;
}