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/