blob: 584145086b7ca901947f9669519aa007e335221d [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/logger/logger.h>
#include <fbl/string_buffer.h>
#include <fuchsia/logger/c/fidl.h>
#include <lib/fidl/cpp/message_buffer.h>
#include <lib/zx/channel.h>
#include <stdint.h>
#include <lib/syslog/logger.h>
#include <lib/syslog/wire_format.h>
#include <zircon/processargs.h>
#include <zircon/status.h>
#include <utility>
namespace logger {
namespace {
static fx_log_packet_t packet;
} // namespace
LoggerImpl::LoggerImpl(zx::channel channel, int out_fd)
: channel_(std::move(channel)),
fd_(out_fd),
wait_(this, channel_.get(), ZX_CHANNEL_READABLE | ZX_CHANNEL_PEER_CLOSED),
socket_wait_(this) {
}
LoggerImpl::~LoggerImpl() {
fsync(fd_);
}
zx_status_t LoggerImpl::Begin(async_dispatcher_t* dispatcher) {
return wait_.Begin(dispatcher);
}
zx_status_t LoggerImpl::PrintLogMessage(const fx_log_packet_t* packet) {
fbl::StringBuffer<sizeof(fx_log_packet_t) + 100> buf; // should be enough to log message
buf.AppendPrintf("[%05ld.%06ld]", packet->metadata.time / 1000000000UL, (packet->metadata.time / 1000UL) % 1000000UL);
buf.AppendPrintf("[%ld]", packet->metadata.pid);
buf.AppendPrintf("[%ld]", packet->metadata.tid);
// print tags
size_t pos = 0;
buf.Append("[");
unsigned int tag_len = packet->data[pos];
int i = 1;
while (tag_len > 0) {
if (i > FX_LOG_MAX_TAGS || tag_len > FX_LOG_MAX_TAG_LEN) {
return ZX_ERR_INVALID_ARGS;
}
if (i > 1) {
buf.Append(", ");
}
pos = pos + 1;
buf.Append(packet->data + pos, tag_len);
pos = pos + tag_len;
tag_len = packet->data[pos];
i++;
}
buf.Append("]");
auto severity = packet->metadata.severity;
switch (severity) {
case FX_LOG_INFO:
buf.Append(" INFO");
break;
case FX_LOG_WARNING:
buf.Append(" WARNING");
break;
case FX_LOG_ERROR:
buf.Append(" ERROR");
break;
case FX_LOG_FATAL:
buf.Append(" FATAL");
break;
default:
buf.AppendPrintf(" VLOG(%d)", -severity);
}
buf.Append(": ");
pos++; //point to log msg
buf.Append(packet->data + pos);
buf.Append("\n");
ssize_t status = write(fd_, buf.data(), buf.size());
if (status < 0) {
return ZX_ERR_IO;
}
return ZX_OK;
}
void LoggerImpl::OnLogMessage(async_dispatcher_t* dispatcher, async::WaitBase* wait, zx_status_t status,
const zx_packet_signal_t* signal) {
if (status != ZX_OK) {
NotifyError(status);
return;
}
if (signal->observed & ZX_SOCKET_READABLE) {
memset(&packet, 0, sizeof(packet));
status = socket_.read(0, &packet, sizeof(packet), nullptr);
if (status != ZX_OK) {
NotifyError(status);
return;
}
if (status != ZX_ERR_SHOULD_WAIT) {
// set last byte of packet to zero so that we don't overflow buffer while
// reading message.
packet.data[sizeof(packet.data) - 1] = 0;
status = PrintLogMessage(&packet);
if (status == ZX_ERR_INVALID_ARGS) {
NotifyError(status);
return;
}
}
status = wait->Begin(dispatcher);
if (status != ZX_OK) {
NotifyError(status);
}
return;
}
ZX_DEBUG_ASSERT(signal->observed & ZX_SOCKET_PEER_CLOSED);
NotifyError(ZX_ERR_PEER_CLOSED);
}
void LoggerImpl::OnHandleReady(async_dispatcher_t* dispatcher, async::WaitBase* wait, zx_status_t status,
const zx_packet_signal_t* signal) {
if (status != ZX_OK) {
NotifyError(status);
return;
}
if (signal->observed & ZX_CHANNEL_READABLE) {
fidl::MessageBuffer buffer;
for (uint64_t i = 0; i < signal->count; i++) {
status = ReadAndDispatchMessage(&buffer, dispatcher);
if (status == ZX_ERR_SHOULD_WAIT)
break;
if (status != ZX_OK) {
NotifyError(status);
return;
}
}
status = wait->Begin(dispatcher);
if (status != ZX_OK) {
NotifyError(status);
}
return;
}
ZX_DEBUG_ASSERT(signal->observed & ZX_CHANNEL_PEER_CLOSED);
channel_.reset();
if (!socket_) {
// if there is no socket, it doesn't make sense to keep running this
// instance.
NotifyError(ZX_ERR_PEER_CLOSED);
}
}
zx_status_t LoggerImpl::ReadAndDispatchMessage(fidl::MessageBuffer* buffer, async_dispatcher_t* dispatcher) {
fidl::Message message = buffer->CreateEmptyMessage();
zx_status_t status = message.Read(channel_.get(), 0);
if (status != ZX_OK)
return status;
if (!message.has_header())
return ZX_ERR_INVALID_ARGS;
// This is an if statement because, depending on the state of the ordinal
// migration, GenOrdinal and Ordinal may be the same value. See FIDL-372
if (message.ordinal() == fuchsia_logger_LogSinkConnectOrdinal ||
message.ordinal() == fuchsia_logger_LogSinkConnectGenOrdinal) {
return Connect(std::move(message), dispatcher);
} else {
fprintf(stderr, "logger: error: Unknown message ordinal: %d\n", message.ordinal());
return ZX_ERR_NOT_SUPPORTED;
}
}
zx_status_t LoggerImpl::Connect(fidl::Message message, async_dispatcher_t* dispatcher) {
if (socket_) {
return ZX_ERR_INVALID_ARGS;
}
const char* error_msg = nullptr;
zx_status_t status = message.Decode(&fuchsia_logger_LogSinkConnectRequestTable, &error_msg);
if (status != ZX_OK) {
fprintf(stderr, "logger: error: Connect: %s\n", error_msg);
return status;
}
auto* request = message.GetBytesAs<fuchsia_logger_LogSinkConnectRequest>();
socket_.reset(request->socket);
socket_wait_.set_object(socket_.get());
socket_wait_.set_trigger(ZX_SOCKET_READABLE | ZX_SOCKET_PEER_CLOSED);
socket_wait_.Begin(dispatcher);
return ZX_OK;
}
void LoggerImpl::NotifyError(zx_status_t error) {
socket_wait_.Cancel();
wait_.Cancel();
channel_.reset();
socket_.reset();
if (error_handler_)
error_handler_(error);
}
} // namespace logger