From b24a5a890ccd19b0f1b50340c79c5087f08d9447 Mon Sep 17 00:00:00 2001
From: John Crispin <john@phrozen.org>
Date: Fri, 4 Mar 2022 15:56:30 +0100
Subject: [PATCH] ulog: add ringbuffer and log_event notification

Signed-off-by: John Crispin <john@phrozen.org>
---
 ucode.c | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 73 insertions(+), 2 deletions(-)

diff --git a/ucode.c b/ucode.c
index cef50e2..9e0373a 100644
--- a/ucode.c
+++ b/ucode.c
@@ -31,6 +31,17 @@ static const char *exception_types[] = {
 	[EXCEPTION_EXIT] = "Exit"
 };
 
+struct log_buffer {
+	struct list_head list;
+	int severity;
+	char entry[];
+};
+
+static LIST_HEAD(log_buffer);
+static int log_count;
+static int log_max = 100;
+static uc_value_t *log_event;
+
 static void
 ucode_handle_exception(uc_vm_t *vm, uc_exception_t *ex)
 {
@@ -287,6 +298,7 @@ static uc_value_t *
 uc_ulog(uc_vm_t *vm, size_t nargs, int severity)
 {
 	uc_value_t *res;
+	char *entry;
 
 	if (!fmtfn) {
 		fmtfn = (uc_cfunction_t *)ucv_object_get(uc_vm_scope_get(vm), "sprintf", NULL);
@@ -300,7 +312,37 @@ uc_ulog(uc_vm_t *vm, size_t nargs, int severity)
 	if (!res)
 		return ucv_int64_new(-1);
 
-	ulog(severity, "%s", ucv_string_get(res));
+	entry = ucv_string_get(res);
+
+	if (log_max) {
+		struct log_buffer *log = calloc(1, sizeof(*log) + strlen(entry) + 1);
+
+		strcpy(log->entry, entry);
+		log->severity = severity;
+		list_add_tail(&log->list, &log_buffer);
+
+		if (log_event) {
+			uc_value_t *event = ucv_array_new(vm);
+
+			ucv_array_push(event, ucv_int64_new(severity));
+			ucv_array_push(event, ucv_string_new(entry));
+
+			uc_vm_stack_push(vm, ucv_get(log_event));
+			uc_vm_stack_push(vm, ucv_get(event));
+			uc_vm_call(vm, false, 1);
+		}
+
+		if (log_count == log_max) {
+			struct log_buffer *first = list_first_entry(&log_buffer, struct log_buffer, list);
+
+			list_del(&first->list);
+			free(first);
+		} else {
+			log_count++;
+		}
+	}
+
+	ulog(severity, "%s", entry);
 	ucv_put(res);
 
 	return ucv_int64_new(0);
@@ -330,11 +372,27 @@ uc_ulog_err(uc_vm_t *vm, size_t nargs)
 	return uc_ulog(vm, nargs, LOG_ERR);
 }
 
+static uc_value_t *
+uc_ulog_dump(uc_vm_t *vm, size_t nargs)
+{
+	uc_value_t *log = ucv_array_new(vm);
+	struct log_buffer *iter;
+
+	list_for_each_entry(iter, &log_buffer, list) {
+		uc_value_t *entry = ucv_array_new(vm);
+		ucv_array_push(entry, ucv_int64_new(iter->severity));
+		ucv_array_push(entry, ucv_string_new(iter->entry));
+		ucv_array_push(log, entry);
+	}
+
+	return log;
+}
+
 static void
 ucode_init_ulog(ucrun_ctx_t *ucrun)
 {
 	uc_value_t *ulog = ucv_object_get(ucrun->scope, "ulog", NULL);
-	uc_value_t *identity, *channels;
+	uc_value_t *identity, *channels, *logsize;
 	int flags = 0, channel;
 
 	/* make sure the declartion is complete */
@@ -365,6 +423,18 @@ ucode_init_ulog(ucrun_ctx_t *ucrun)
 			flags |= ULOG_STDIO;
 	}
 
+	/* set the internal ring buffer size */
+	logsize = ucv_object_get(ulog, "channels", NULL);
+	if (ucv_type(logsize) == UC_INTEGER && ucv_int64_get(logsize))
+		log_max = ucv_int64_get(logsize);
+
+	/* find out if ucrun wants a notification when a new log entry is generated */
+	log_event = ucv_object_get(ulog, "event", NULL);
+	if (ucv_is_callable(log_event))
+		ucv_get(log_event);
+	else
+		log_event = NULL;
+
 	/* open the log */
 	ucrun->ulog_identity = strdup(ucv_string_get(identity));
 	ulog_open(flags, LOG_DAEMON, ucrun->ulog_identity);
@@ -404,6 +474,7 @@ ucode_init(ucrun_ctx_t *ucrun, int argc, const char **argv, int *rc)
 	uc_function_register(ucrun->scope, "ulog_note", uc_ulog_note);
 	uc_function_register(ucrun->scope, "ulog_warn", uc_ulog_warn);
 	uc_function_register(ucrun->scope, "ulog_err", uc_ulog_err);
+	uc_function_register(ucrun->scope, "ulog_dump", uc_ulog_dump);
 
 	/* add commandline parameters */
 	ARGV = ucv_array_new(&ucrun->vm);
-- 
2.25.1

