ttf-console: Add syslog-handler to obmc-console
Add syslog-handler to allow enable the obmc-console-server log the
console data to syslog when syslogid is configured for the console.
This syslog-handler following obmc-console handler structure register as
another console data ringbuffer consumer like log-handler. Log the data
line by line, filtering out non-printable data. Break the line longer than
1K into multiple syslog message.
To mitigate the risk of potential delay of Megapede support ttf-console
feature, prepare this backup plan:
Support log the host serial console stream to syslog.
Tested:
The mvt19-n1 reboot console log can get from syslog dashboard:
http://dashboards/platforms_gbmc_devtools_plx_syslog_gbmc_syslog?p=TABLE_TYPE:20240518&p=HOSTNAME:mvt19-nfd11&f=program:eq:ttf-console-log
All build success:
http://fusion2/ci/kokoro/prod%3Agbmc%2Ffirmware-build%2Ffirmware%2Fredacted_gbmc_platforms/activity/72a9e8df-2a5f-412e-bee7-76007c8d1373
Google-Bug-Id: 340958958
Change-Id: I9c68cd47c9e2ad595346dee018cec43c180b7fab
Signed-off-by: Dan Zhang <zhdaniel@google.com>
(cherry picked from commit 4742f91800454f694eb863b2a28da0f5cd43a90d)
diff --git a/recipes-phosphor/console/obmc-console/0001-Add-syslog-handler.patch b/recipes-phosphor/console/obmc-console/0001-Add-syslog-handler.patch
new file mode 100644
index 0000000..53715a2
--- /dev/null
+++ b/recipes-phosphor/console/obmc-console/0001-Add-syslog-handler.patch
@@ -0,0 +1,257 @@
+From c1c9e1e782bac752adb9ae7ed4c930d17db933be Mon Sep 17 00:00:00 2001
+From: Dan Zhang <zhdaniel@google.com>
+Date: Mon, 20 May 2024 22:02:21 +0000
+Subject: [PATCH 1/2] Add syslog-handler
+
+Log the console data to syslog when syslogid configured.
+Will buffer small piece of data to avoid overwhelming the syslog with
+small message. The buffer delay threshold set to 1 second. log threshold
+is 1KB.
+
+The data will be logged line by line, line longer than 1K will be
+breaked into multiple log messages. Filter out non-printable characters.
+
+Change-Id: I21698a36b11b064d75b9846e54bb12848e2d8bfa
+Signed-off-by: Dan Zhang <zhdaniel@google.com>
+---
+ meson.build | 1 +
+ syslog-handler.c | 215 +++++++++++++++++++++++++++++++++++++++++++++++
+ 2 files changed, 216 insertions(+)
+ create mode 100644 syslog-handler.c
+
+diff --git a/meson.build b/meson.build
+index 06a7200..a604223 100644
+--- a/meson.build
++++ b/meson.build
+@@ -60,6 +60,7 @@ executable('obmc-console-server',
+ 'log-handler.c',
+ 'ringbuffer.c',
+ 'socket-handler.c',
++ 'syslog-handler.c',
+ 'tty-handler.c',
+ 'util.c',
+ c_args: [
+diff --git a/syslog-handler.c b/syslog-handler.c
+new file mode 100644
+index 0000000..a410dae
+--- /dev/null
++++ b/syslog-handler.c
+@@ -0,0 +1,215 @@
++/**
++ * Copyright © 2024 Google
++ *
++ * Licensed under the Apache License, Version 2.0 (the "License");
++ * you may not use this file except in compliance with the License.
++ * You may obtain a copy of the License at
++ *
++ * http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing, software
++ * distributed under the License is distributed on an "AS IS" BASIS,
++ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++ * See the License for the specific language governing permissions and
++ * limitations under the License.
++ */
++
++#include <ctype.h>
++#include <endian.h>
++#include <err.h>
++#include <fcntl.h>
++#include <stdbool.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++#include <sys/syslog.h>
++#include <unistd.h>
++#include <syslog.h>
++
++#include <sys/mman.h>
++
++#include <linux/types.h>
++
++#include "console-server.h"
++
++/* syslog data buffering size threshold 1KB */
++#define SYSLOG_HANDLER_BUF_SIZE_THRESHOLD 1024
++/* syslog data buffering time threshold 1s */
++#define SYSLOG_HANDLER_BUF_TIME_THRESHOLD_SEC 1
++
++#define min(a, b) ((a) < (b) ? (a) : (b))
++
++static struct timeval const syslog_handler_timeout = {
++ .tv_sec = SYSLOG_HANDLER_BUF_TIME_THRESHOLD_SEC,
++ .tv_usec = 0,
++};
++
++struct syslog_handler {
++ struct handler handler;
++ struct console *console;
++ struct poller *poller;
++ struct ringbuffer_consumer *rbc;
++ char *syslogid;
++ int pipefd[2];
++ size_t curser;
++ char line[SYSLOG_HANDLER_BUF_SIZE_THRESHOLD + 1];
++};
++
++static struct syslog_handler *to_syslog_handler(struct handler *handler)
++{
++ return container_of(handler, struct syslog_handler, handler);
++}
++
++/*
++ * Log the data line by line, line longer than 1K will be break into multiple lines.
++ * Take '\n','\r' or '\0' as line break, filter out non-printable characters.
++ */
++static void syslog_data_as_canonical_line(struct syslog_handler *lh,
++ const uint8_t *data,
++ size_t to_log_len)
++{
++ const char *p = (const char *)data;
++ const char *end = p + to_log_len;
++ char *line = lh->line;
++ size_t wi = lh->curser;
++ while (p < end) {
++ if (isprint(*p)) {
++ line[wi++] = *p;
++ } else {
++ if ((*p == '\0' || *p == '\n' || *p == '\r') && wi) {
++ /* log non-empty line */
++ line[wi] = '\0';
++ syslog(LOG_INFO, "%s", line);
++ wi = 0;
++ }
++ }
++ p++;
++ /* force line break */
++ if (wi == SYSLOG_HANDLER_BUF_SIZE_THRESHOLD) {
++ line[wi] = '\0';
++ syslog(LOG_INFO, "%s", line);
++ wi = 0;
++ }
++ }
++ lh->curser = wi;
++}
++
++static void syslog_drain_queue(struct syslog_handler *lh, size_t to_drain_len)
++{
++ uint8_t *buf;
++ size_t drained_len = 0;
++
++ while (to_drain_len) {
++ size_t len =
++ ringbuffer_dequeue_peek(lh->rbc, drained_len, &buf);
++ if (len == 0) {
++ break;
++ }
++ len = min(to_drain_len, len);
++ syslog_data_as_canonical_line(lh, buf, len);
++ ringbuffer_dequeue_commit(lh->rbc, len);
++ drained_len += len;
++ to_drain_len -= len;
++ }
++}
++
++static enum ringbuffer_poll_ret syslog_ringbuffer_poll(void *arg, size_t force_len)
++{
++ struct syslog_handler *lh = arg;
++ size_t len;
++
++ if (force_len) {
++ /* Drain only force_len when blocking ringbuf enque console input */
++ syslog_drain_queue(lh, force_len);
++ return RINGBUFFER_POLL_OK;
++ }
++
++ len = ringbuffer_len(lh->rbc);
++ if (len < SYSLOG_HANDLER_BUF_SIZE_THRESHOLD) {
++ /* Buffer the console data unitl more than 1K or idle 1 second
++ to push the data to syslog */
++ console_poller_set_timeout(lh->console, lh->poller,
++ &syslog_handler_timeout);
++ return RINGBUFFER_POLL_OK;
++ }
++ syslog_drain_queue(lh, len);
++ return RINGBUFFER_POLL_OK;
++}
++
++static enum poller_ret dummy_poll(struct handler *handler, int events,
++ void *data)
++{
++ /* shall not get called */
++ warn("Unexpected dummy_poll call handler = %p, event = 0x%8X, data = %p",
++ (void *)handler, events, data);
++ return POLLER_EXIT;
++}
++
++static enum poller_ret accum_timeout(struct handler *handler,
++ void *data __attribute__((unused)))
++{
++ struct syslog_handler *lh = to_syslog_handler(handler);
++ size_t len = ringbuffer_len(lh->rbc);
++
++ syslog_drain_queue(lh, len);
++ return POLLER_OK;
++}
++
++static int syslog_init(struct handler *handler, struct console *console,
++ struct config *config)
++{
++ struct syslog_handler *lh = to_syslog_handler(handler);
++ const char *syslogid;
++
++ lh->console = console;
++ lh->poller = NULL;
++ lh->rbc = NULL;
++ lh->syslogid = NULL;
++ lh->curser = 0;
++
++ syslogid = config_get_value(config, "syslogid");
++ if (syslogid == NULL) {
++ warn("syslogid is not configured, not emit console log to syslog");
++ return -1;
++ }
++
++ /* regsiter console_poller used for timeout only so create a dummy pipe */
++ if (pipe(lh->pipefd) == -1) {
++ warn("syslog handler create pipe failed");
++ return -1;
++ }
++
++ lh->syslogid = strdup(syslogid);
++ openlog(lh->syslogid, LOG_NOWAIT, LOG_USER);
++
++ lh->poller = console_poller_register(console, handler, dummy_poll,
++ accum_timeout, lh->pipefd[0], 0,
++ NULL);
++
++ lh->rbc = console_ringbuffer_consumer_register(console,
++ syslog_ringbuffer_poll, lh);
++
++ return 0;
++}
++
++static void syslog_fini(struct handler *handler)
++{
++ struct syslog_handler *lh = to_syslog_handler(handler);
++
++ ringbuffer_consumer_unregister(lh->rbc);
++ console_poller_unregister(lh->console, lh->poller);
++ closelog();
++ close(lh->pipefd[0]);
++ close(lh->pipefd[1]);
++ free(lh->syslogid);
++}
++
++static struct syslog_handler syslog_handler = {
++ .handler = {
++ .name = "syslog",
++ .init = syslog_init,
++ .fini = syslog_fini,
++ },
++};
++
++console_handler_register(&syslog_handler.handler);
+--
+2.45.1.288.g0e0cd299f1-goog
+
diff --git a/recipes-phosphor/console/obmc-console_%.bbappend b/recipes-phosphor/console/obmc-console_%.bbappend
index c680f24..f23d531 100644
--- a/recipes-phosphor/console/obmc-console_%.bbappend
+++ b/recipes-phosphor/console/obmc-console_%.bbappend
@@ -4,6 +4,7 @@
file://readonly-obmc-console-client \
file://40-console-client-override.conf \
file://ttf-console.sh \
+ file://0001-Add-syslog-handler.patch \
"
do_install:append:gbmc() {
install -d -m0644 ${D}${sysconfdir}/systemd/system/serial-to-host@.service.d/