From mboxrd@z Thu Jan  1 00:00:00 1970
Return-Path: <d.csapak@proxmox.com>
Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68])
 (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)
 key-exchange X25519 server-signature RSA-PSS (2048 bits))
 (No client certificate requested)
 by lists.proxmox.com (Postfix) with ESMTPS id 2BD4F81754
 for <pve-devel@lists.proxmox.com>; Wed, 24 Nov 2021 15:48:20 +0100 (CET)
Received: from firstgate.proxmox.com (localhost [127.0.0.1])
 by firstgate.proxmox.com (Proxmox) with ESMTP id 255FA21750
 for <pve-devel@lists.proxmox.com>; Wed, 24 Nov 2021 15:47:50 +0100 (CET)
Received: from proxmox-new.maurer-it.com (proxmox-new.maurer-it.com
 [94.136.29.106])
 (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)
 key-exchange X25519 server-signature RSA-PSS (2048 bits))
 (No client certificate requested)
 by firstgate.proxmox.com (Proxmox) with ESMTPS id 2A9852173D
 for <pve-devel@lists.proxmox.com>; Wed, 24 Nov 2021 15:47:49 +0100 (CET)
Received: from proxmox-new.maurer-it.com (localhost.localdomain [127.0.0.1])
 by proxmox-new.maurer-it.com (Proxmox) with ESMTP id 04E88447C8
 for <pve-devel@lists.proxmox.com>; Wed, 24 Nov 2021 15:47:49 +0100 (CET)
From: Dominik Csapak <d.csapak@proxmox.com>
To: pve-devel@lists.proxmox.com
Date: Wed, 24 Nov 2021 15:47:46 +0100
Message-Id: <20211124144748.68687-2-d.csapak@proxmox.com>
X-Mailer: git-send-email 2.30.2
In-Reply-To: <20211124144748.68687-1-d.csapak@proxmox.com>
References: <20211124144748.68687-1-d.csapak@proxmox.com>
MIME-Version: 1.0
Content-Transfer-Encoding: 8bit
X-SPAM-LEVEL: Spam detection results:  0
 AWL 0.201 Adjusted score from AWL reputation of From: address
 BAYES_00                 -1.9 Bayes spam probability is 0 to 1%
 KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment
 SPF_HELO_NONE           0.001 SPF: HELO does not publish an SPF Record
 SPF_PASS               -0.001 SPF: sender matches SPF record
Subject: [pve-devel] [PATCH mini-journalreader 1/1] add '-j' flag to output
 json
X-BeenThere: pve-devel@lists.proxmox.com
X-Mailman-Version: 2.1.29
Precedence: list
List-Id: Proxmox VE development discussion <pve-devel.lists.proxmox.com>
List-Unsubscribe: <https://lists.proxmox.com/cgi-bin/mailman/options/pve-devel>, 
 <mailto:pve-devel-request@lists.proxmox.com?subject=unsubscribe>
List-Archive: <http://lists.proxmox.com/pipermail/pve-devel/>
List-Post: <mailto:pve-devel@lists.proxmox.com>
List-Help: <mailto:pve-devel-request@lists.proxmox.com?subject=help>
List-Subscribe: <https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel>, 
 <mailto:pve-devel-request@lists.proxmox.com?subject=subscribe>
X-List-Received-Date: Wed, 24 Nov 2021 14:48:20 -0000

in the format:
{"data":[... log lines ...],"success":1}

this is chosen so that we can achieve api compatibility when we stream
this output to an api client

strings are escaped by replacing '"', '\' and all values <= 0x1F by their
\uXXXX representation

invalid utf8 sequences will be returned as they are
(jq and the browser can handle that)

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 src/mini-journalreader.c | 66 ++++++++++++++++++++++++++++++++++++++--
 1 file changed, 63 insertions(+), 3 deletions(-)

diff --git a/src/mini-journalreader.c b/src/mini-journalreader.c
index 92176ac..7ce7857 100644
--- a/src/mini-journalreader.c
+++ b/src/mini-journalreader.c
@@ -32,6 +32,8 @@
 #define BUFSIZE 4096
 
 static char BUF[BUFSIZE];
+bool json = false;
+bool first_line = true;
 
 static uint64_t get_timestamp(sd_journal *j) {
     uint64_t timestamp;
@@ -63,8 +65,19 @@ static void print_cursor(sd_journal *j) {
         fprintf(stderr, "Failed to get cursor: %s\n", strerror(-r));
         exit(1);
     }
+    if (json) {
+	if (!first_line) {
+	    print_to_buf(",\"", 2);
+	} else {
+	    print_to_buf("\"", 1);
+	}
+    }
     print_to_buf(cursor, strlen(cursor));
+    if (json) {
+	print_to_buf("\"", 1);
+    }
     print_to_buf("\n", 1);
+    first_line = false;
     free(cursor);
 }
 
@@ -93,7 +106,15 @@ static void print_reboot(sd_journal *j) {
     if (bootid[0] != '\0') { // we have some bootid
         if (memcmp(bootid, d, l)) { // a new bootid found
             memcpy(bootid, d, l);
-            print_to_buf("-- Reboot --\n", 13);
+            if (json) {
+                if (!first_line) {
+                    print_to_buf(",", 1);
+                }
+                print_to_buf("\"-- Reboot --\"\n", 15);
+                first_line = false;
+            } else {
+                print_to_buf("-- Reboot --\n", 13);
+            }
         }
     } else {
         memcpy(bootid, d, l);
@@ -149,13 +170,35 @@ static bool print_field(sd_journal *j, const char *field) {
     size_t fieldlen = strlen(field)+1;
     d += fieldlen;
     l -= fieldlen;
-    print_to_buf(d, l);
+
+    if (json) {
+	char tmp[7];
+	for (size_t i = 0; i < l;i++) {
+	    if (d[i] == '"' || d[i] == '\\' || (d[i] >= 0 && d[i] <= 0x1F)) {
+		sprintf(tmp, "\\u%04X", d[i]);
+		print_to_buf(tmp, 6);
+	    } else {
+		print_to_buf(d+i, 1);
+	    }
+	}
+    } else {
+	print_to_buf(d, l);
+    }
     return true;
 }
 
 
 static void print_line(sd_journal *j) {
     print_reboot(j);
+
+    if (json) {
+        if (!first_line) {
+            print_to_buf(",", 1);
+        }
+        print_to_buf("\"", 1);
+        first_line = false;
+    }
+
     print_timestamp(j);
     print_to_buf(" ", 1);
     print_field(j, "_HOSTNAME");
@@ -167,6 +210,11 @@ static void print_line(sd_journal *j) {
     print_pid(j);
     print_to_buf(": ", 2);
     print_field(j, "MESSAGE");
+
+    if (json) {
+        print_to_buf("\"", 1);
+    }
+
     print_to_buf("\n", 1);
 }
 
@@ -184,6 +232,7 @@ _Noreturn static void usage(char *error) {
         "  -n <integer>\t\tprint the last number entries logged\n"
         "  -f <cursor>\t\tprint from this cursor\n"
         "  -t <cursor>\t\tprint to this cursor\n"
+        "  -j \t\t\tprint as json"
         "  -h \t\t\tthis help\n"
         "\n"
         "Passing no range option will dump all the available journal\n"
@@ -217,7 +266,7 @@ int main(int argc, char *argv[]) {
 
     progname = argv[0];
 
-    while ((c = (char)getopt (argc, argv, "b:e:d:n:f:t:h")) != -1) {
+    while ((c = (char)getopt (argc, argv, "b:e:d:n:f:t:jh")) != -1) {
         switch (c) {
             case 'b':
                 begin = arg_to_uint64(optarg);
@@ -239,6 +288,9 @@ int main(int argc, char *argv[]) {
             case 't':
                 endcursor = optarg;
                 break;
+            case 'j':
+                json = true;
+                break;
             case 'h':
                 usage(NULL);
             case '?':
@@ -285,6 +337,10 @@ int main(int argc, char *argv[]) {
         return 1;
     }
 
+    if (json) {
+        print_to_buf("{\"data\":[", 9);
+    }
+
     // if we want to print the last x entries, seek to cursor or end,
     // then x entries back, print the cursor and finally print the
     // entries until end or cursor
@@ -350,6 +406,10 @@ int main(int argc, char *argv[]) {
     print_cursor(j);
     sd_journal_close(j);
 
+    if (json) {
+        print_to_buf("],\"success\":1}", 14);
+    }
+
     // print remaining buffer
     fflush_unlocked(stdout);
 
-- 
2.30.2