On Thu, Mar 05, 2015 at 04:51:00PM +0100, Maros Zatko wrote:
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)
I think this patch could be in two parts, one where you move the
journal_view() code from cat/log.c into fish/journal.c, and a second
part where you add the extra command. More comments below.
cat/Makefile.am | 1 +
cat/log.c | 113 +-------------------------------
fish/Makefile.am | 1 +
fish/fish.h | 3 +
fish/journal.c | 178 +++++++++++++++++++++++++++++++++++++++++++++++++++
generator/actions.ml | 9 +++
6 files changed, 195 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..c72946b 100644
--- a/cat/log.c
+++ b/cat/log.c
@@ -36,6 +36,7 @@
#include "guestfs.h"
#include "options.h"
+#include "fish.h"
/* Currently open libguestfs handle. */
guestfs_h *g;
@@ -273,128 +274,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;
This returns NULL if the field doesn't exist, but ..
+}
+
+/* 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 lookup_field returns NULL, then get_journal_field will
segfault.
So I think lookup_field should print an error and call exit (EXIT_FAILURE)
if the field is not found.
+ 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 9f32cb5..4565f91 100644
--- a/generator/actions.ml
+++ b/generator/actions.ml
@@ -12714,6 +12714,15 @@ environment variable.
See also L</hexdump>." };
{ defaults with
+ name = "journal_view";
+ shortdesc = "view journald log";
+ longdesc = " journal-view
You need to document a couple of things here:
(1) That there is an optional extra parameter ('fields').
(2) At least a summary of how to specify the fields parameter and what
the fields characters mean.
+View journald log in format similar to journalctl.
+
+Use C<journal-open> first." };
+
+ { defaults with
name = "lcd";
shortdesc = "change working directory";
longdesc = " lcd directory
Rich.
--
Richard Jones, Virtualization Group, Red Hat
http://people.redhat.com/~rjones
Read my programming and virtualization blog:
http://rwmj.wordpress.com
Fedora Windows cross-compiler. Compile Windows programs, test, and
build Windows installers. Over 100 libraries supported.
http://fedoraproject.org/wiki/MinGW