[Libguestfs] [PATCH v2] fish: add journal-view command

Maros Zatko mzatko at redhat.com
Tue Mar 3 20:55:06 UTC 2015


Lets user view journald log from VM in a similar format as journalctl uses.

Makes virt-log use the same code as guestfish for journal-view.

Fixes RFE: journal reader in guestfish (RHBZ#988100)
---
 cat/Makefile.am      |   1 +
 cat/log.c            | 112 +-------------------------------
 fish/Makefile.am     |   1 +
 fish/fish.h          |   3 +
 fish/journal.c       | 178 +++++++++++++++++++++++++++++++++++++++++++++++++++
 generator/actions.ml |   9 +++
 6 files changed, 194 insertions(+), 110 deletions(-)
 create mode 100644 fish/journal.c

diff --git a/cat/Makefile.am b/cat/Makefile.am
index d0db6fa..d472100 100644
--- a/cat/Makefile.am
+++ b/cat/Makefile.am
@@ -38,6 +38,7 @@ bin_PROGRAMS = virt-cat virt-filesystems virt-log virt-ls
 SHARED_SOURCE_FILES = \
 	../fish/domain.c \
 	../fish/inspect.c \
+	../fish/journal.c \
 	../fish/keys.c \
 	../fish/options.h \
 	../fish/options.c \
diff --git a/cat/log.c b/cat/log.c
index 616baed..cd51c50 100644
--- a/cat/log.c
+++ b/cat/log.c
@@ -273,128 +273,20 @@ do_log (void)
   return 0;
 }
 
-/* Find the value of the named field from the list of attributes.  If
- * not found, returns NULL (not an error).  If found, returns a
- * pointer to the field, and the length of the field.  NOTE: The field
- * is NOT \0-terminated, so you have to print it using "%.*s".
- *
- * There may be multiple fields with the same name.  In this case, the
- * function returns the first entry.
- */
-static const char *
-get_journal_field (const struct guestfs_xattr_list *xattrs, const char *name,
-                   size_t *len_rtn)
-{
-  uint32_t i;
-
-  for (i = 0; i < xattrs->len; ++i) {
-    if (STREQ (name, xattrs->val[i].attrname)) {
-      *len_rtn = xattrs->val[i].attrval_len;
-      return xattrs->val[i].attrval;
-    }
-  }
-
-  return NULL;                  /* not found */
-}
-
-static const char *const log_level_table[] = {
-  [LOG_EMERG] = "emerg",
-  [LOG_ALERT] = "alert",
-  [LOG_CRIT] = "crit",
-  [LOG_ERR] = "err",
-  [LOG_WARNING] = "warning",
-  [LOG_NOTICE] = "notice",
-  [LOG_INFO] = "info",
-  [LOG_DEBUG] = "debug"
-};
-
 static int
 do_log_journal (void)
 {
   int r;
-  unsigned errors = 0;
-
   if (guestfs_journal_open (g, JOURNAL_DIR) == -1)
     return -1;
 
-  while ((r = guestfs_journal_next (g)) > 0) {
-    CLEANUP_FREE_XATTR_LIST struct guestfs_xattr_list *xattrs = NULL;
-    const char *priority_str, *identifier, *comm, *pid, *message;
-    size_t priority_len, identifier_len, comm_len, pid_len, message_len;
-    int priority = LOG_INFO;
-    int64_t ts;
-
-    /* The question is what fields to display.  We should probably
-     * make this configurable, but for now use the "short" format from
-     * journalctl.  (XXX)
-     */
-
-    xattrs = guestfs_journal_get (g);
-    if (xattrs == NULL)
-      return -1;
-
-    ts = guestfs_journal_get_realtime_usec (g); /* error checked below */
-
-    priority_str = get_journal_field (xattrs, "PRIORITY", &priority_len);
-    //hostname = get_journal_field (xattrs, "_HOSTNAME", &hostname_len);
-    identifier = get_journal_field (xattrs, "SYSLOG_IDENTIFIER",
-                                    &identifier_len);
-    comm = get_journal_field (xattrs, "_COMM", &comm_len);
-    pid = get_journal_field (xattrs, "_PID", &pid_len);
-    message = get_journal_field (xattrs, "MESSAGE", &message_len);
-
-    /* Timestamp. */
-    if (ts >= 0) {
-      char buf[64];
-      time_t t = ts / 1000000;
-      struct tm tm;
-
-      if (strftime (buf, sizeof buf, "%b %d %H:%M:%S",
-                    localtime_r (&t, &tm)) <= 0) {
-        fprintf (stderr, _("%s: could not format journal entry timestamp\n"),
-                 guestfs_int_program_name);
-        errors++;
-        continue;
-      }
-      fputs (buf, stdout);
-    }
-
-    /* Hostname. */
-    /* We don't print this because it is assumed each line from the
-     * guest will have the same hostname.  (XXX)
-     */
-    //if (hostname)
-    //  printf (" %.*s", (int) hostname_len, hostname);
-
-    /* Identifier. */
-    if (identifier)
-      printf (" %.*s", (int) identifier_len, identifier);
-    else if (comm)
-      printf (" %.*s", (int) comm_len, comm);
-
-    /* PID */
-    if (pid)
-      printf ("[%.*s]", (int) pid_len, pid);
-
-    /* Log level. */
-    if (priority_str && *priority_str >= '0' && *priority_str <= '7')
-      priority = *priority_str - '0';
-
-    printf (" %s:", log_level_table[priority]);
-
-    /* Message. */
-    if (message)
-      printf (" %.*s", (int) message_len, message);
-
-    printf ("\n");
-  }
-  if (r == -1)                  /* error from guestfs_journal_next */
+  if (r = journal_view ("~3axv") == -1)
     return -1;
 
   if (guestfs_journal_close (g) == -1)
     return -1;
 
-  return errors > 0 ? -1 : 0;
+  return r;
 }
 
 static int
diff --git a/fish/Makefile.am b/fish/Makefile.am
index c4b82ae..e4b4fcf 100644
--- a/fish/Makefile.am
+++ b/fish/Makefile.am
@@ -94,6 +94,7 @@ guestfish_SOURCES = \
 	glob.c \
 	help.c \
 	hexedit.c \
+	journal.c \
 	lcd.c \
 	man.c \
 	more.c \
diff --git a/fish/fish.h b/fish/fish.h
index df22e34..8ae6454 100644
--- a/fish/fish.h
+++ b/fish/fish.h
@@ -104,6 +104,9 @@ extern int rc_remote (int pid, const char *cmd, size_t argc, char *argv[],
 /* in tilde.c */
 extern char *try_tilde_expansion (char *path);
 
+/* in journal.c */
+extern int journal_view (const char *fields);
+
 /* This should just list all the built-in commands so they can
  * be added to the generated auto-completion code.
  */
diff --git a/fish/journal.c b/fish/journal.c
new file mode 100644
index 0000000..8472445
--- /dev/null
+++ b/fish/journal.c
@@ -0,0 +1,178 @@
+/* guestfish - guest filesystem shell
+ * Copyright (C) 2009-2015 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <libintl.h>
+#include <time.h>
+#include <syslog.h>
+
+#include "fish.h"
+
+/* Find the value of the named field from the list of attributes.  If
+ * not found, returns NULL (not an error).  If found, returns a
+ * pointer to the field, and the length of the field.  NOTE: The field
+ * is NOT \0-terminated, so you have to print it using "%.*s".
+ *
+ * There may be multiple fields with the same name.  In this case, the
+ * function returns the first entry.
+ */
+static const char *
+get_journal_field (const struct guestfs_xattr_list *xattrs, const char *name,
+                   size_t *len_rtn)
+{
+  uint32_t i;
+
+  for (i = 0; i < xattrs->len; ++i) {
+    if (STREQ (name, xattrs->val[i].attrname)) {
+      *len_rtn = xattrs->val[i].attrval_len;
+      return xattrs->val[i].attrval;
+    }
+  }
+
+  return NULL;                  /* not found */
+}
+
+static const char *const log_level_table[] = {
+  [LOG_EMERG] = "emerg",
+  [LOG_ALERT] = "alert",
+  [LOG_CRIT] = "crit",
+  [LOG_ERR] = "err",
+  [LOG_WARNING] = "warning",
+  [LOG_NOTICE] = "notice",
+  [LOG_INFO] = "info",
+  [LOG_DEBUG] = "debug"
+};
+
+static const char *const log_fields[] = {
+  /* Trusted fields */
+  "a", "_PID",
+  "b", "_UID",
+  "c", "_GID",
+  "d", "_COMM",
+  "e", "_EXE",
+  "f", "_CMDLINE",
+  "g", "_CAP_EFFECTIVE",
+  "h", "_AUDIT_SESSION",
+  "i", "_AUDIT_LOGINUID",
+  "j", "_SYSTEMD_CGROUP",
+  "k", "_SYSTEMD_SESSION",
+  "l", "_SYSTEMD_UNIT",
+  "m", "_SYSTEMD_USER_UNIT",
+  "n", "_SYSTEMD_OWNER_UID",
+  "o", "_SYSTEMD_SLICE",
+  "p", "_SELINUX_CONTEXT",
+  "q", "_SOURCE_REALTIME_TIMESTAMP",
+  "r", "_BOOT_ID",
+  "s", "_MACHINE_ID",
+  "t", "_HOSTNAME",
+  "u", "_TRANSPORT",
+  /* User fields */
+  "v", "MESSAGE",
+  "w", "MESSAGE_ID",
+  "x", "PRIORITY",
+  "y", "CODE_FILE",
+  "z", "CODE_LINE",
+  "0", "CODE_FUNC",
+  "1", "ERRNO",
+  "2", "SYSLOG_FACILITY",
+  "3", "SYSLOG_IDENTIFIER",
+  "4", "SYSLOG_PID"
+/* "~" means timestamp */
+};
+
+static const char *
+lookup_field (char field)
+{
+  size_t i = 0;
+  for (i = 0; i < sizeof log_fields / sizeof *log_fields; i += 2) {
+    if (field == log_fields[i][0])
+      return log_fields[i+1];
+  }
+  return NULL;
+}
+
+/* Fetch and print journal fields in specified order
+ * default is '~3axv'
+ */
+int
+journal_view (const char *fields)
+{
+  int r;
+  int errors = 0;
+  while ((r = guestfs_journal_next(g)) > 0) {
+    CLEANUP_FREE_XATTR_LIST struct guestfs_xattr_list *xattrs = NULL;
+    int64_t ts;
+    int priority = LOG_INFO;
+
+    xattrs = guestfs_journal_get (g);
+    if (xattrs == NULL)
+      return -1;
+
+    size_t f_id = 0;
+    for (f_id = 0; f_id < strlen (fields); ++f_id) {
+      if (fields[f_id] == '~') {
+        ts = guestfs_journal_get_realtime_usec (g);
+        /* Timestamp. */
+        if (ts >= 0) {
+          char buf[64];
+          time_t t = ts / 1000000;
+          struct tm tm;
+
+          if (strftime (buf, sizeof buf, "%b %d %H:%M:%S",
+                        localtime_r (&t, &tm)) <= 0) {
+            fprintf (stderr, _("could not format journal entry timestamp\n"));
+            errors++;
+            continue;
+          }
+          fputs (buf, stdout);
+        }
+      }
+
+      const char *field_name, *field_val;
+      size_t field_len;
+      if ((field_name = lookup_field (fields[f_id]))) {
+        field_val = get_journal_field (xattrs, field_name, &field_len);
+        if (STREQ (field_name, "PRIORITY")) {
+          if (field_val && *field_val >= '0' && *field_val <= '7')
+            priority = *field_val - '0';
+          printf (" %s:", log_level_table[priority]);
+        } else if (field_val) {
+          printf (" %.*s", (int)field_len, field_val);
+        }
+      }
+    }
+    printf ("\n");
+  }
+
+  return errors > 0 ? -1 : 0;
+}
+
+int
+run_journal_view (const char *cmd, size_t argc, char *argv[])
+{
+  if (argc > 0)
+    return journal_view (argv[0]);
+  return journal_view ("~3axv");
+}
diff --git a/generator/actions.ml b/generator/actions.ml
index c995787..c31b9a8 100644
--- a/generator/actions.ml
+++ b/generator/actions.ml
@@ -12667,6 +12667,15 @@ environment variable.
 See also L</hexdump>." };
 
   { defaults with
+    name = "journal_view";
+    shortdesc = "view journald log";
+    longdesc = "  journal-view
+
+View journald log in format similar to journalctl.
+
+Use C<journal-open> first." };
+
+  { defaults with
     name = "lcd";
     shortdesc = "change working directory";
     longdesc = " lcd directory
-- 
1.9.3




More information about the Libguestfs mailing list