| // 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 |