1// Copyright 2018 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <lib/logger/logger.h>
6
7#include <fbl/string_buffer.h>
8#include <fuchsia/logger/c/fidl.h>
9#include <lib/fidl/cpp/message_buffer.h>
10#include <lib/zx/channel.h>
11#include <stdint.h>
12#include <syslog/logger.h>
13#include <syslog/wire_format.h>
14#include <zircon/processargs.h>
15#include <zircon/status.h>
16
17namespace logger {
18namespace {
19
20static fx_log_packet_t packet;
21
22} // namespace
23
24LoggerImpl::LoggerImpl(zx::channel channel, int out_fd)
25    : channel_(fbl::move(channel)),
26      fd_(out_fd),
27      wait_(this, channel_.get(), ZX_CHANNEL_READABLE | ZX_CHANNEL_PEER_CLOSED),
28      socket_wait_(this) {
29}
30
31LoggerImpl::~LoggerImpl() {
32    fsync(fd_);
33}
34
35zx_status_t LoggerImpl::Begin(async_dispatcher_t* dispatcher) {
36    return wait_.Begin(dispatcher);
37}
38
39zx_status_t LoggerImpl::PrintLogMessage(const fx_log_packet_t* packet) {
40    fbl::StringBuffer<sizeof(fx_log_packet_t) + 100> buf; // should be enough to log message
41    buf.AppendPrintf("[%05ld.%06ld]", packet->metadata.time / 1000000000UL, (packet->metadata.time / 1000UL) % 1000000UL);
42    buf.AppendPrintf("[%ld]", packet->metadata.pid);
43    buf.AppendPrintf("[%ld]", packet->metadata.tid);
44
45    // print tags
46    size_t pos = 0;
47    buf.Append("[");
48    unsigned int tag_len = packet->data[pos];
49    int i = 1;
50    while (tag_len > 0) {
51        if (i > FX_LOG_MAX_TAGS || tag_len > FX_LOG_MAX_TAG_LEN) {
52            return ZX_ERR_INVALID_ARGS;
53        }
54        if (i > 1) {
55            buf.Append(", ");
56        }
57        pos = pos + 1;
58        buf.Append(packet->data + pos, tag_len);
59        pos = pos + tag_len;
60        tag_len = packet->data[pos];
61        i++;
62    }
63    buf.Append("]");
64
65    auto severity = packet->metadata.severity;
66    switch (severity) {
67    case FX_LOG_INFO:
68        buf.Append(" INFO");
69        break;
70    case FX_LOG_WARNING:
71        buf.Append(" WARNING");
72        break;
73    case FX_LOG_ERROR:
74        buf.Append(" ERROR");
75        break;
76    case FX_LOG_FATAL:
77        buf.Append(" FATAL");
78        break;
79    default:
80        buf.AppendPrintf(" VLOG(%d)", -severity);
81    }
82    buf.Append(": ");
83    pos++; //point to log msg
84    buf.Append(packet->data + pos);
85    buf.Append("\n");
86
87    ssize_t status = write(fd_, buf.data(), buf.size());
88    if (status < 0) {
89        return ZX_ERR_IO;
90    }
91    return ZX_OK;
92}
93
94void LoggerImpl::OnLogMessage(async_dispatcher_t* dispatcher, async::WaitBase* wait, zx_status_t status,
95                              const zx_packet_signal_t* signal) {
96    if (status != ZX_OK) {
97        NotifyError(status);
98        return;
99    }
100
101    if (signal->observed & ZX_SOCKET_READABLE) {
102        memset(&packet, 0, sizeof(packet));
103        status = socket_.read(0, &packet, sizeof(packet), nullptr);
104        if (status != ZX_OK) {
105            NotifyError(status);
106            return;
107        }
108        if (status != ZX_ERR_SHOULD_WAIT) {
109            // set last byte of packet to zero so that we don't overflow buffer while
110            // reading message.
111            packet.data[sizeof(packet.data) - 1] = 0;
112            status = PrintLogMessage(&packet);
113            if (status == ZX_ERR_INVALID_ARGS) {
114                NotifyError(status);
115                return;
116            }
117        }
118        status = wait->Begin(dispatcher);
119        if (status != ZX_OK) {
120            NotifyError(status);
121        }
122        return;
123    }
124
125    ZX_DEBUG_ASSERT(signal->observed & ZX_SOCKET_PEER_CLOSED);
126    NotifyError(ZX_ERR_PEER_CLOSED);
127}
128
129void LoggerImpl::OnHandleReady(async_dispatcher_t* dispatcher, async::WaitBase* wait, zx_status_t status,
130                               const zx_packet_signal_t* signal) {
131    if (status != ZX_OK) {
132        NotifyError(status);
133        return;
134    }
135
136    if (signal->observed & ZX_CHANNEL_READABLE) {
137        fidl::MessageBuffer buffer;
138        for (uint64_t i = 0; i < signal->count; i++) {
139            status = ReadAndDispatchMessage(&buffer, dispatcher);
140            if (status == ZX_ERR_SHOULD_WAIT)
141                break;
142            if (status != ZX_OK) {
143                NotifyError(status);
144                return;
145            }
146        }
147        status = wait->Begin(dispatcher);
148        if (status != ZX_OK) {
149            NotifyError(status);
150        }
151        return;
152    }
153
154    ZX_DEBUG_ASSERT(signal->observed & ZX_CHANNEL_PEER_CLOSED);
155    channel_.reset();
156    if (!socket_) {
157        // if there is no socket, it doesn't make sense to keep running this
158        // instance.
159        NotifyError(ZX_ERR_PEER_CLOSED);
160    }
161}
162
163zx_status_t LoggerImpl::ReadAndDispatchMessage(fidl::MessageBuffer* buffer, async_dispatcher_t* dispatcher) {
164    fidl::Message message = buffer->CreateEmptyMessage();
165    zx_status_t status = message.Read(channel_.get(), 0);
166    if (status != ZX_OK)
167        return status;
168    if (!message.has_header())
169        return ZX_ERR_INVALID_ARGS;
170    switch (message.ordinal()) {
171    case fuchsia_logger_LogSinkConnectOrdinal:
172        return Connect(fbl::move(message), dispatcher);
173    default:
174        fprintf(stderr, "logger: error: Unknown message ordinal: %d\n", message.ordinal());
175        return ZX_ERR_NOT_SUPPORTED;
176    }
177}
178
179zx_status_t LoggerImpl::Connect(fidl::Message message, async_dispatcher_t* dispatcher) {
180    if (socket_) {
181        return ZX_ERR_INVALID_ARGS;
182    }
183    const char* error_msg = nullptr;
184    zx_status_t status = message.Decode(&fuchsia_logger_LogSinkConnectRequestTable, &error_msg);
185    if (status != ZX_OK) {
186        fprintf(stderr, "logger: error: Connect: %s\n", error_msg);
187        return status;
188    }
189    auto* request = message.GetBytesAs<fuchsia_logger_LogSinkConnectRequest>();
190    socket_.reset(request->socket);
191    socket_wait_.set_object(socket_.get());
192    socket_wait_.set_trigger(ZX_SOCKET_READABLE | ZX_SOCKET_PEER_CLOSED);
193    socket_wait_.Begin(dispatcher);
194    return ZX_OK;
195}
196
197void LoggerImpl::NotifyError(zx_status_t error) {
198    socket_wait_.Cancel();
199    wait_.Cancel();
200    channel_.reset();
201    socket_.reset();
202    if (error_handler_)
203        error_handler_(error);
204}
205
206} // namespace logger
207