This patch adds a new tool to extract information related to virtual machines
from the audit log files.

This tool is based on the proposal sent in the RFC:

https://www.redhat.com/archives/linux-audit/2011-November/msg00014.html
---
 audit.spec                 |    2 +
 configure.ac               |    2 +-
 tools/Makefile.am          |    2 +-
 tools/auvirt/Makefile.am   |   40 ++
 tools/auvirt/auvirt-list.c |  105 ++++
 tools/auvirt/auvirt-list.h |   31 +
 tools/auvirt/auvirt.8      |  121 ++++
 tools/auvirt/auvirt.c      | 1308 ++++++++++++++++++++++++++++++++++++++++++++
 8 files changed, 1609 insertions(+), 2 deletions(-)
 create mode 100644 tools/auvirt/Makefile.am
 create mode 100644 tools/auvirt/auvirt-list.c
 create mode 100644 tools/auvirt/auvirt-list.h
 create mode 100644 tools/auvirt/auvirt.8
 create mode 100644 tools/auvirt/auvirt.c

diff --git a/audit.spec b/audit.spec
index 383bed1..a9940b4 100644
--- a/audit.spec
+++ b/audit.spec
@@ -172,6 +172,7 @@ fi
 %attr(644,root,root) %{_mandir}/man8/autrace.8.gz
 %attr(644,root,root) %{_mandir}/man8/aulast.8.gz
 %attr(644,root,root) %{_mandir}/man8/aulastlog.8.gz
+%attr(644,root,root) %{_mandir}/man8/auvirt.8.gz
 %attr(644,root,root) %{_mandir}/man8/ausyscall.8.gz
 %attr(644,root,root) %{_mandir}/man7/audit.rules.7.gz
 %attr(644,root,root) %{_mandir}/man5/auditd.conf.5.gz
@@ -186,6 +187,7 @@ fi
 %attr(755,root,root) %{_bindir}/aulast
 %attr(755,root,root) %{_bindir}/aulastlog
 %attr(755,root,root) %{_bindir}/ausyscall
+%attr(755,root,root) %{_bindir}/auvirt
 %attr(755,root,root) /etc/rc.d/init.d/auditd
 %attr(750,root,root) %dir %{_var}/log/audit
 %attr(750,root,root) %dir /etc/audit
diff --git a/configure.ac b/configure.ac
index c1cf96f..b7f7fcd 100644
--- a/configure.ac
+++ b/configure.ac
@@ -255,7 +255,7 @@ AC_SUBST(libev_LIBS)
 AC_SUBST(LIBPRELUDE_CFLAGS)
 AC_SUBST(LIBPRELUDE_LDFLAGS)
 
-AC_OUTPUT(Makefile lib/Makefile lib/test/Makefile auparse/Makefile 
auparse/test/Makefile src/Makefile src/mt/Makefile src/libev/Makefile 
src/test/Makefile swig/Makefile docs/Makefile init.d/Makefile audisp/Makefile 
audisp/plugins/Makefile audisp/plugins/builtins/Makefile 
audisp/plugins/prelude/Makefile audisp/plugins/remote/Makefile 
audisp/plugins/zos-remote/Makefile bindings/Makefile bindings/python/Makefile 
tools/Makefile tools/aulast/Makefile tools/aulastlog/Makefile 
tools/ausyscall/Makefile)
+AC_OUTPUT(Makefile lib/Makefile lib/test/Makefile auparse/Makefile 
auparse/test/Makefile src/Makefile src/mt/Makefile src/libev/Makefile 
src/test/Makefile swig/Makefile docs/Makefile init.d/Makefile audisp/Makefile 
audisp/plugins/Makefile audisp/plugins/builtins/Makefile 
audisp/plugins/prelude/Makefile audisp/plugins/remote/Makefile 
audisp/plugins/zos-remote/Makefile bindings/Makefile bindings/python/Makefile 
tools/Makefile tools/aulast/Makefile tools/aulastlog/Makefile 
tools/ausyscall/Makefile tools/auvirt/Makefile)
 
 echo .
 echo "
diff --git a/tools/Makefile.am b/tools/Makefile.am
index 3b7acfe..15ba254 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -22,5 +22,5 @@
 
 CONFIG_CLEAN_FILES = *.loT *.rej *.orig
 
-SUBDIRS = aulast aulastlog ausyscall
+SUBDIRS = aulast aulastlog ausyscall auvirt
 
diff --git a/tools/auvirt/Makefile.am b/tools/auvirt/Makefile.am
new file mode 100644
index 0000000..0e9ec5b
--- /dev/null
+++ b/tools/auvirt/Makefile.am
@@ -0,0 +1,40 @@
+#
+# Makefile.am --
+# Copyright (c) 2011 IBM Corp.
+# All Rights Reserved.
+#
+# This software may be freely redistributed and/or modified under the
+# terms of the GNU General Public License as published by the Free
+# Software Foundation; either version 2, 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; see the file COPYING. If not, write to the
+# Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+# Authors:
+#   Marcelo Henrique Cerri <[email protected]>
+#
+ 
+CONFIG_CLEAN_FILES = *.loT *.rej *.orig
+AUTOMAKE_OPTIONS = no-dependencies
+EXTRA_DIST = $(man_MANS)
+INCLUDES = -I${top_srcdir} \
+                  -I${top_srcdir}/lib \
+                  -I${top_srcdir}/auparse \
+                  -I${top_srcdir}/src
+LIBS = -L${top_builddir}/auparse \
+          -lauparse
+AM_CFLAGS = -D_GNU_SOURCE
+bin_PROGRAMS = auvirt
+noinst_HEADERS = auvirt-list.h
+man_MANS = auvirt.8
+
+auvirt_SOURCES = auvirt.c \
+                                auvirt-list.c \
+                                ${top_srcdir}/src/ausearch-time.c
diff --git a/tools/auvirt/auvirt-list.c b/tools/auvirt/auvirt-list.c
new file mode 100644
index 0000000..7502188
--- /dev/null
+++ b/tools/auvirt/auvirt-list.c
@@ -0,0 +1,105 @@
+#include "auvirt-list.h"
+#include <stdlib.h>
+
+list_t *list_init(list_t *list, list_free_data_fn *free_data_fn)
+{
+       if (list == NULL)
+               return NULL;
+       list->head = list->tail = NULL;
+       list->free_data_fn = free_data_fn;
+       return list;
+}
+
+list_t *list_new(list_free_data_fn *free_data_fn)
+{
+       return list_init(malloc(sizeof(list_t)), free_data_fn);
+}
+
+void list_free_node(list_node_t *node, list_free_data_fn *free_data_fn)
+{
+       if (node) {
+               if (free_data_fn)
+                       free_data_fn(node->data);
+               free(node);
+       }
+}
+
+void list_free_(list_t *list, list_free_data_fn *free_data_fn)
+{
+       if (list != NULL) {
+               list_node_t *it = list->head;
+               while (it && it->next) {
+                       it = it->next;
+                       list_free_node(it->prev, free_data_fn);
+               }
+               list_free_node(it, free_data_fn);
+               free(list);
+       }
+}
+
+void list_free(list_t *list)
+{
+       if (list)
+               list_free_(list, list->free_data_fn);
+}
+
+list_node_t *list_insert_after(list_t *list, list_node_t *it,
+               void *data)
+{
+       list_node_t *node = NULL;
+       if (list == NULL)
+               return NULL;
+
+       /* allocate node */
+       node = malloc(sizeof(list_node_t));
+       if (node == NULL)
+               return NULL;
+       node->data = data;
+
+       /* insert the new node after it */
+       node->prev = it;
+       if (it) {
+               node->next = it->next;
+               it->next = node;
+       }
+       else
+               node->next = list->head;
+       if (node->next)
+               node->next->prev = node;
+
+       /* update list's head and tail */
+       if (it == list->tail)
+               list->tail = node;
+       if (it == NULL)
+               list->head = node;
+
+       return node;
+}
+
+list_node_t *list_append(list_t *list, void *data)
+{
+       return list_insert_after(list, list->tail, data);
+}
+
+int list_remove_(list_t *list, list_node_t *it,
+               list_free_data_fn *free_data_fn)
+{
+       if (list == NULL || it == NULL)
+               return 1;
+       if (list->head == it)
+               list->head = it->next;
+       if (list->tail == it)
+               list->tail = it->prev;
+       if (it->next)
+               it->next->prev = it->prev;
+       if (it->prev)
+               it->prev->next = it->next;
+       list_free_node(it, free_data_fn);
+       return 0;
+}
+
+int list_remove(list_t *list, list_node_t *it)
+{
+       return list_remove_(list, it, list->free_data_fn);
+}
+
diff --git a/tools/auvirt/auvirt-list.h b/tools/auvirt/auvirt-list.h
new file mode 100644
index 0000000..fb58746
--- /dev/null
+++ b/tools/auvirt/auvirt-list.h
@@ -0,0 +1,31 @@
+
+#ifndef AUVIRT_LIST_HEADER
+#define AUVIRT_LIST_HEADER
+
+typedef void (list_free_data_fn)(void *data);
+
+typedef struct list_node_t {
+       void *data;
+       struct list_node_t *prev;
+       struct list_node_t *next;
+} list_node_t;
+
+typedef struct list_t {
+       list_node_t *head;
+       list_node_t *tail;
+       list_free_data_fn *free_data_fn;
+} list_t;
+
+list_t *list_init(list_t *list, list_free_data_fn *free_data_fn);
+list_t *list_new(list_free_data_fn *free_data_fn);
+void list_free_(list_t *list, list_free_data_fn *free_data_fn);
+void list_free(list_t *list);
+list_node_t *list_insert_after(list_t *list, list_node_t *it,
+               void *data);
+list_node_t *list_append(list_t *list, void *data);
+int list_remove_(list_t *list, list_node_t *it,
+               list_free_data_fn *free_data_fn);
+int list_remove(list_t *list, list_node_t *it);
+
+#endif
+
diff --git a/tools/auvirt/auvirt.8 b/tools/auvirt/auvirt.8
new file mode 100644
index 0000000..86266f7
--- /dev/null
+++ b/tools/auvirt/auvirt.8
@@ -0,0 +1,121 @@
+.TH AUVIRT 8 "Dec 2011" "IBM Corp" "System Administration Utilities"
+.SH NAME
+auvirt - a program that shows data related to virtual machines
+
+.SH SYNOPSIS
+.B auvirt
+[ \fIOPTIONS\fP ]
+
+.SH DESCRIPTION
+\fBauvirt\fP shows a list of guest sessions found in the audit logs. If a guest
+is specified, only the events related to that guest is considered. To specify a
+guest, both UUID or VM name can be given.
+
+For each guest session the tool prints a record with the domain name, the user
+that started the guest, the time when the guest was started and the time when
+the guest was stoped.
+
+If the option "--all-events" is given a more detailed output is shown. In this
+mode other records are shown for guest's stops, resource
+assignments, host shutdowns and AVC and anomaly events. The first field
+indicates the event type and can have the following values: start, stop,
+res, avc, anom and down (for host shutdowns).
+
+Resource assignments have the additional fields: resource type, reason and
+resource. And AVC records have the following additional fields: operation,
+result, command and target.
+
+By default, auvirt reads records from the system audit log file. But
+\fB--stdin\fP and \fB--file\fP options can be specified to change this
+behavior.
+
+.SH OPTIONS
+.TP
+\fB--all-events\fP
+Show records for all virtualization related events.
+.TP
+\fB--debug\fP
+Print debug messages to standard output.
+.TP
+\fB-f\fP, \fB--file\fP \fIfile\fP
+Read records from the given \fIfile\fP instead from the system audit log file.
+.TP
+\fB-h\fP, \fB--help\fP
+Print help message and exit.
+.TP
+\fB--proof\fP
+Add after each event a line containing all the identifiers of the audit records
+used to calculate the event. Each identifier consists of unix time,
+milliseconds and serial number.
+.TP
+\fB--show-uuid\fP
+Add the guest's UUID to each record.
+.TP
+\fB--stdin\fP
+Read records from the standard input instead from the system audit log file.
+This option cannot be specified with \fB--file\fP.
+.TP
+\fB--summary\fP
+Print a summary with information about the events found. The summary contains
+the considered range of time, the number of guest starts and stops, the number
+of resource assignments, the number of AVC and anomaly events, the number of
+host shutdowns and the number of failed operations.
+.TP
+.BR \-te ,\  \-\-end \ [\fIend-date\fP]\ [\fIend-time\fP]
+Search for events with time stamps equal to or before the given end time. The
+format of end time depends on your locale. If the date is omitted,
+.B today
+is assumed. If the time is omitted,
+.B now
+is assumed. Use 24 hour clock time rather than AM or PM to specify time.
+An example date using the en_US.utf8 locale is 09/03/2009. An example of time
+is 18:00:00. The date format accepted is influenced by the LC_TIME
+environmental variable.
+
+You may also use the word: \fBnow\fP, \fBrecent\fP, \fBtoday\fP,
+\fByesterday\fP, \fBthis\-week\fP, \fBweek\-ago\fP, \fBthis\-month\fP,
+\fBthis\-year\fP. \fBToday\fP means starting now. \fBRecent\fP is 10 minutes
+ago. \fBYesterday\fP is 1 second after midnight the previous day.
+\fBThis\-week\fP means starting 1 second after midnight on day 0 of the week
+determined by your locale (see \fBlocaltime\fP). \fBThis\-month\fP means 1
+second after midnight on day 1 of the month. \fBThis\-year\fP means the 1
+second after midnight on the first day of the first month.
+.TP
+.BR \-ts ,\  \-\-start \ [\fIstart-date\fP]\ [\fIstart-time\fP]
+Search for events with time stamps equal to or after the given end time. The
+format of end time depends on your locale. If the date is omitted,
+.B today
+is assumed. If the time is omitted,
+.B midnight
+is assumed. Use 24 hour clock time rather than AM or PM to specify time. An
+example date using the en_US.utf8 locale is 09/03/2009. An example of time is
+18:00:00. The date format accepted is influenced by the LC_TIME environmental
+variable.
+
+You may also use the word: \fBnow\fP, \fBrecent\fP, \fBtoday\fP,
+\fByesterday\fP, \fBthis\-week\fP, \fBthis\-month\fP, \fBthis\-year\fP.
+\fBToday\fP means starting at 1 second after midnight. \fBRecent\fP is 10
+minutes ago. \fBYesterday\fP is 1 second after midnight the previous day.
+\fBThis\-week\fP means starting 1 second after midnight on day 0 of the week
+determined by your locale (see \fBlocaltime\fP). \fBThis\-month\fP means 1
+second after midnight on day 1 of the month. \fBThis\-year\fP means the 1
+second after midnight on the first day of the first month.
+.TP
+\fB-u\fP, \fB--uuid\fP \ \fIUUID\fP
+Only show events related to the guest with the given UUID.
+.TP
+\fB-v\fP, \fB--vm\fP \ \fIname\fP
+Only show events related to the guest with the given name.
+
+.SH EXAMPLES
+To see all the records in this month for a guest
+
+\fBauvirt --start this-month --vm GuestVmName --all-events\fP
+
+.SH SEE ALSO
+.BR aulast (8),
+.BR ausearch (8),
+.BR aureport (8).
+
+.SH AUTHOR
+Marcelo Cerri
diff --git a/tools/auvirt/auvirt.c b/tools/auvirt/auvirt.c
new file mode 100644
index 0000000..c04780a
--- /dev/null
+++ b/tools/auvirt/auvirt.c
@@ -0,0 +1,1308 @@
+/*
+ * auvirt.c - A tool to extract data related to virtualization.
+ * Copyright (c) 2011 IBM Corp.
+ * All Rights Reserved.
+ *
+ * This software may be freely redistributed and/or modified under the
+ * terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2, 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; see the file COPYING. If not, write to the
+ * Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Authors:
+ *   Marcelo Henrique Cerri <[email protected]>
+ */
+
+#include "config.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <locale.h>
+#include <string.h>
+#include <regex.h>
+#include <time.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include "auparse.h"
+#include "libaudit.h"
+#include "ausearch-time.h"
+#include "auvirt-list.h"
+
+/* Command line parameters */
+static int help_flag = 0;
+static int stdin_flag = 0;
+static int summary_flag = 0;
+static int all_events_flag = 0;
+static int uuid_flag = 0;
+static int proof_flag = 0;
+static const char *vm = NULL;
+static const char *uuid = NULL;
+static const char *file = NULL;
+static int debug = 0;
+/*
+ * The start time and end time given in the command line is stored respectively
+ * in the variables start_time and end_time that are declared/defined in the
+ * files ausearch-time.h and ausearch-time.c. These files are reused from the
+ * ausearch tool source code:
+ *
+ *     time_t start_time = 0;
+ *     time_t end_time = 0;
+ */
+
+/* List of events */
+enum event_type {
+       ET_NONE = 0, ET_START, ET_STOP, ET_MACHINE_ID, ET_AVC, ET_RES, ET_ANOM,
+       ET_DOWN
+};
+struct record_id {
+       time_t time;
+       unsigned int milli;
+       unsigned long serial;
+};
+struct event {
+       enum event_type type;
+       time_t start;
+       time_t end;
+       uid_t uid;
+       char *uuid;
+       char *name;
+       int success;
+       pid_t pid;
+       /* Fields specific for resource events: */
+       char *reason;
+       char *res_type;
+       char *res;
+       /* Fields specific for machine id events: */
+       char *seclevel;
+       /* Fields specific for avc events: */
+       char *target;
+       char *comm;
+       char *seresult;
+       char *seperms;
+       /* Fields to print proof information: */
+       struct record_id proof[4];
+};
+list_t *events = NULL;
+
+
+/* Auxiliary functions to allocate and to free events. */
+struct event *event_alloc()
+{
+       struct event *event = malloc(sizeof(struct event));
+       if (event) {
+               /* The new event is initialized with values that represents
+                * unset values: -1 for uid and pid and 0 (or NULL) for numbers
+                * and pointers. For example, event->end = 0 represents an
+                * unfinished event.
+                */
+               memset(event, 0, sizeof(struct event));
+               event->uid = -1;
+               event->pid = -1;
+       }
+       return event;
+}
+
+void event_free(struct event *event)
+{
+       if (event) {
+               free(event->uuid);
+               free(event->name);
+               free(event->reason);
+               free(event->res_type);
+               free(event->res);
+               free(event->seclevel);
+               free(event->target);
+               free(event->comm);
+               free(event->seresult);
+               free(event->seperms);
+               free(event);
+       }
+}
+
+inline char *copy_str(const char *str)
+{
+       return (str) ? strdup(str) : NULL;
+}
+
+void usage(FILE *output)
+{
+       fprintf(output, "usage: auvirt [--stdin] [--all-events] [--summary] "
+                       "[--start start-date [start-time]] "
+                       "[--end end-date [end-time]] [--file file-name] "
+                       "[--show--uuid] [--proof] "
+                       "[--uuid uuid] [--vm vm-name]\n");
+}
+
+/* Parse and check command line arguments */
+int parse_args(int argc, char **argv)
+{
+       /* Based on http://www.ietf.org/rfc/rfc4122.txt */
+       const char *uuid_pattern = "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-"
+               "[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$";
+       int i, rc = 0;
+       regex_t uuid_regex;
+
+       if (regcomp(&uuid_regex, uuid_pattern, REG_EXTENDED)) {
+               fprintf(stderr, "Failed to initialize program.\n");
+               return 1;
+       }
+
+       for (i = 1; i < argc; i++) {
+               const char *opt = argv[i];
+               if (opt[0] != '-') {
+                       fprintf(stderr, "Argument not expected: %s\n", opt);
+                       goto error;
+               } else if (strcmp("--vm", opt) == 0 ||
+                          strcmp("-v", opt) == 0) {
+                       if ((i + 1) >= argc || argv[i + 1][0] == '-') {
+                               fprintf(stderr, "\"%s\" option requires "
+                                               "an argument.\n", opt);
+                               goto error;
+                       }
+                       vm = argv[++i];
+               } else if (strcmp("--uuid", opt) == 0 ||
+                          strcmp("-u", opt) == 0) {
+                       if ((i + 1) >= argc || argv[i + 1][0] == '-') {
+                               fprintf(stderr, "\"%s\" option requires "
+                                               "an argument.\n", opt);
+                               goto error;
+                       }
+                       if (regexec(&uuid_regex, argv[i + 1], 0, NULL, 0)) {
+                               fprintf(stderr, "Invalid uuid: %s\n",
+                                               argv[i + 1]);
+                               goto error;
+                       }
+                       uuid = argv[++i];
+               } else if (strcmp("--all-events", opt) == 0 ||
+                          strcmp("-a", opt) == 0) {
+                       all_events_flag = 1;
+               } else if (strcmp("--summary", opt) == 0 ||
+                          strcmp("-s", opt) == 0) {
+                       summary_flag = 1;
+               } else if (strcmp("--file", opt) == 0 ||
+                          strcmp("-f", opt) == 0) {
+                       if ((i + 1) >= argc || argv[i + 1][0] == '-') {
+                               fprintf(stderr, "\"%s\" option requires "
+                                               "an argument.\n", opt);
+                               goto error;
+                       }
+                       file = argv[++i];
+               } else if (strcmp("--show-uuid", opt) == 0) {
+                       uuid_flag = 1;
+               } else if (strcmp("--stdin", opt) == 0) {
+                       stdin_flag = 1;
+               } else if (strcmp("--proof", opt) == 0) {
+                       proof_flag = 1;
+               } else if (strcmp("--help", opt) == 0 ||
+                          strcmp("-h", opt) == 0) {
+                       help_flag = 1;
+                       goto exit;
+               } else if (strcmp("--start", opt) == 0 ||
+                          strcmp("-ts", opt) == 0) {
+                       const char *date, *time = NULL;
+                       if ((i + 1) >= argc || argv[i + 1][0] == '-') {
+                               fprintf(stderr, "\"%s\" option requires at "
+                                               "least one argument.\n", opt);
+                               goto error;
+                       }
+                       date = argv[++i];
+                       if ((i + 1) < argc && argv[i + 1][0] != '-')
+                               time = argv[++i];
+                       /* This will set start_time */
+                       if(ausearch_time_start(date, time))
+                               goto error;
+               } else if (strcmp("--end", opt) == 0 ||
+                          strcmp("-te", opt) == 0) {
+                       const char *date, *time = NULL;
+                       if ((i + 1) >= argc || argv[i + 1][0] == '-') {
+                               fprintf(stderr, "\"%s\" option requires at "
+                                               "least one argument.\n", opt);
+                               goto error;
+                       }
+                       date = argv[++i];
+                       if ((i + 1) < argc && argv[i + 1][0] != '-')
+                               time = argv[++i];
+                       /* This will set end_time */
+                       if (ausearch_time_end(date, time))
+                               goto error;
+               } else if (strcmp("--debug", opt) == 0) {
+                       debug = 1;
+               } else {
+                       fprintf(stderr, "Unknown option \"%s\".\n", opt);
+                       goto error;
+               }
+       }
+
+       /* Validate conflicting options */
+       if (stdin_flag && file) {
+               fprintf(stderr, "\"--sdtin\" and \"--file\" options "
+                               "must not be specified together.\n");
+               goto error;
+       }
+
+       if (debug) {
+               fprintf(stderr, "help_flag='%i'\n", help_flag);
+               fprintf(stderr, "stdin_flag='%i'\n", stdin_flag);
+               fprintf(stderr, "all_events_flag='%i'\n", all_events_flag);
+               fprintf(stderr, "summary_flag='%i'\n", summary_flag);
+               fprintf(stderr, "uuid='%s'\n", uuid ? uuid : "(null)");
+               fprintf(stderr, "vm='%s'\n", vm ? vm : "(null)");
+               fprintf(stderr, "file='%s'\n", file ? file : "(null)");
+               fprintf(stderr, "start_time='%-.16s'\n", (start_time == 0L) ?
+                               "" : ctime(&start_time));
+               fprintf(stderr, "end_time='%-.16s'\n", (end_time == 0L) ?
+                               "" : ctime(&end_time));
+       }
+
+exit:
+       regfree(&uuid_regex);
+       return rc;
+error:
+       rc = 1;
+       goto exit;
+}
+
+/* Initialize an auparse_state_t with the correct log source. */
+auparse_state_t *init_auparse()
+{
+       auparse_state_t *au = NULL;
+       if (stdin_flag) {
+               au = auparse_init(AUSOURCE_FILE_POINTER, stdin);
+       } else if (file) {
+               au = auparse_init(AUSOURCE_FILE, file);
+       } else {
+               if (getuid()) {
+                       fprintf(stderr, "You probably need to be root for "
+                                       "this to work\n");
+               }
+               au = auparse_init(AUSOURCE_LOGS, NULL);
+       }
+       if (au == NULL) {
+               fprintf(stderr, "Error: %s\n", strerror(errno));
+       }
+       return au;
+}
+
+/* Create a criteria to search for the virtualization related records */
+int create_search_criteria(auparse_state_t *au)
+{
+       char *error = NULL;
+       char expr[1024];
+       snprintf(expr, sizeof(expr),
+               "(\\record_type >= %d && \\record_type <= %d)",
+               AUDIT_FIRST_VIRT_MSG, AUDIT_LAST_VIRT_MSG);
+       if (ausearch_add_expression(au, expr, &error, AUSEARCH_RULE_CLEAR)) {
+               fprintf(stderr, "Criteria error: %s\n", error);
+               free(error);
+               return 1;
+       }
+       if (uuid) {
+               if (ausearch_add_item(au, "uuid", "=", uuid,
+                                       AUSEARCH_RULE_AND)) {
+                       fprintf(stderr, "Criteria error: uuid\n");
+                       return 1;
+               }
+       }
+       if (vm) {
+               /*
+                * If a field has its value quoted in the audit log, for
+                * example:
+                *      vm="guest-name"
+                *
+                * auparse will consider the field value with quotes when
+                * matching a rule. For example, using the example above the
+                * following rule will not match:
+                *     ausearch_add_item(au, "vm", "=", "guest-name", how);
+                *
+                * But this rule will match:
+                *     ausearch_add_item(au, "vm", "=", "\"guest-name\"", how);
+                *
+                * TODO use a better approach for this problem...
+                */
+               snprintf(expr, sizeof(expr), "\"%s\"", vm);
+               if (ausearch_add_item(au, "vm", "=", expr,
+                                       AUSEARCH_RULE_AND)) {
+                       fprintf(stderr, "Criteria error: id\n");
+                       return 1;
+               }
+       }
+       if (all_events_flag || summary_flag) {
+               if (ausearch_add_item(au, "type", "=", "AVC",
+                                       AUSEARCH_RULE_OR)) {
+                       fprintf(stderr, "Criteria error: AVC\n");
+                       return 1;
+               }
+               if (ausearch_add_item(au, "type", "=", "SYSTEM_SHUTDOWN",
+                                       AUSEARCH_RULE_OR)) {
+                       fprintf(stderr, "Criteria error: shutdown\n");
+                       return 1;
+               }
+               snprintf(expr, sizeof(expr),
+                       "(\\record_type >= %d && \\record_type <= %d) ||"
+                       "(\\record_type >= %d && \\record_type <= %d)",
+                       AUDIT_FIRST_ANOM_MSG, AUDIT_LAST_ANOM_MSG,
+                       AUDIT_FIRST_KERN_ANOM_MSG, AUDIT_LAST_KERN_ANOM_MSG);
+               if (ausearch_add_expression(au, expr, &error,
+                                       AUSEARCH_RULE_OR)) {
+                       fprintf(stderr, "Criteria error: %s\n", error);
+                       free(error);
+                       return 1;
+               }
+       }
+       if (start_time) {
+               if (ausearch_add_timestamp_item(au, ">=", start_time, 0,
+                                       AUSEARCH_RULE_AND)) {
+                       fprintf(stderr, "Criteria error: start_time\n");
+                       return 1;
+               }
+       }
+       if (end_time) {
+               if (ausearch_add_timestamp_item(au, "<=", end_time, 0,
+                                       AUSEARCH_RULE_AND)) {
+                       fprintf(stderr, "Criteria error: end_time\n");
+                       return 1;
+               }
+       }
+       return 0;
+}
+
+/* Extract the most common fields from virtualization-related records. */
+int extract_virt_fields(auparse_state_t *au, const char **p_uuid,
+               uid_t *p_uid, time_t *p_time, const char **p_name,
+               int *p_suc)
+{
+       const char *field;
+       auparse_first_record(au);
+       /* Order matters */
+       if (p_uid) {
+               if (!auparse_find_field(au, field = "uid"))
+                       goto error;
+               *p_uid = auparse_get_field_int(au);
+       }
+       if (p_name) {
+               if (!auparse_find_field(au, field = "vm"))
+                       goto error;
+               *p_name = auparse_get_field_str(au);
+       }
+       if (p_uuid) {
+               if (!auparse_find_field(au, field = "uuid"))
+                       goto error;
+               *p_uuid = auparse_get_field_str(au);
+       }
+       if (p_suc) {
+               const char *res = auparse_find_field(au, field = "res");
+               if (res == NULL)
+                       goto error;
+               *p_suc = (strcmp("success", res) == 0) ? 1 : 0;
+       }
+       if (p_time) {
+               *p_time = auparse_get_time(au);
+       }
+       return 0;
+
+error:
+       if (debug) {
+               fprintf(stderr, "Failed to get field \"%s\" for record "
+                               "%ld.%03u:%lu\n", field ? field : "",
+                               auparse_get_time(au),
+                               auparse_get_milli(au),
+                               auparse_get_serial(au));
+       }
+       return 1;
+}
+
+/* Return label and categories from a security context. */
+const char *get_seclevel(const char *seclabel)
+{
+       /*
+        * system_u:system_r:svirt_t:s0:c107,c434
+        *                           \____ _____/
+        *                                '
+        *                           level + cat
+        */
+       int c = 0;
+       for (;seclabel && *seclabel; seclabel++) {
+               if (*seclabel == ':')
+                       c += 1;
+               if (c == 3)
+                       return seclabel + 1;
+       }
+       return NULL;
+}
+
+int add_proof(struct event *event, auparse_state_t *au)
+{
+       if (!proof_flag)
+               return 0;
+
+       size_t i, proof_len = sizeof(event->proof)/sizeof(event->proof[0]);
+       for (i = 0; i < proof_len; i++) {
+               if (event->proof[i].time == 0)
+                       break;
+       }
+       if (i == proof_len) {
+               if (debug)
+                       fprintf(stderr, "Failed to add proof.\n");
+               return 1;
+       }
+
+       event->proof[i].time = auparse_get_time(au);
+       event->proof[i].milli = auparse_get_milli(au);
+       event->proof[i].serial = auparse_get_serial(au);
+       return 0;
+}
+
+/*
+ * machine_id records are used to get the selinux context associated to a
+ * guest.
+ */
+int process_machine_id_event(auparse_state_t *au)
+{
+       uid_t uid;
+       time_t time;
+       const char *seclevel, *uuid, *name;
+       struct event *event;
+       int success;
+
+       seclevel = get_seclevel(auparse_find_field(au, "vm-ctx"));
+       if (seclevel == NULL) {
+               if (debug)
+                       fprintf(stderr, "security context not found for "
+                                       "MACHINE_ID event.\n");
+       }
+
+       if (extract_virt_fields(au, &uuid, &uid, &time, &name, &success))
+               return 0;
+
+       event = event_alloc();
+       if (event == NULL)
+               return 1;
+       event->type = ET_MACHINE_ID;
+       event->uuid = copy_str(uuid);
+       event->name = copy_str(name);
+       event->success = success;
+       event->seclevel = copy_str(seclevel);
+       event->uid = uid;
+       event->start = time;
+       add_proof(event, au);
+       if (list_append(events, event) == NULL) {
+               event_free(event);
+               return 1;
+       }
+       return 0;
+}
+
+int add_start_guest_event(auparse_state_t *au)
+{
+       struct event *start;
+       uid_t uid;
+       time_t time;
+       const char *uuid, *name;
+       int success;
+       list_node_t *it;
+
+       /* Just skip this record if it failed to get some of the fields */
+       if (extract_virt_fields(au, &uuid, &uid, &time, &name, &success))
+               return 0;
+
+       /* On failure, loop backwards to update all the resources associated to
+        * the last session of this guest. When a machine_id or a stop event is
+        * found the loop can be broken because a machine_id is created at the
+        * beginning of a session and a stop event indicates a previous
+        * session.
+        */
+       if (!success) {
+               for (it = events->tail; it; it = it->prev) {
+                       struct event *event = it->data;
+                       if (event->success && event->uuid &&
+                           strcmp(uuid, event->uuid) == 0) {
+                               if (event->type == ET_STOP ||
+                                   event->type == ET_MACHINE_ID) {
+                                       /* An old session found. */
+                                       break;
+                               } else if (event->type == ET_RES &&
+                                          event->end == 0) {
+                                       event->end = time;
+                                       add_proof(event, au);
+                               }
+                       }
+               }
+       }
+
+       start = event_alloc();
+       if (start == NULL)
+               return 1;
+       start->type = ET_START;
+       start->uuid = copy_str(uuid);
+       start->name = copy_str(name);
+       start->success = success;
+       start->uid = uid;
+       start->start = time;
+       auparse_first_record(au);
+       if (auparse_find_field(au, "vm-pid"))
+               start->pid = auparse_get_field_int(au);
+       add_proof(start, au);
+       if (list_append(events, start) == NULL) {
+               event_free(start);
+               return 1;
+       }
+       return 0;
+}
+
+int add_stop_guest_event(auparse_state_t *au)
+{
+       list_node_t *it;
+       struct event *stop, *start = NULL, *event = NULL;
+       uid_t uid;
+       time_t time;
+       const char *uuid, *name;
+       int success;
+
+       /* Just skip this record if it failed to get some of the fields */
+       if (extract_virt_fields(au, &uuid, &uid, &time, &name, &success))
+               return 0;
+
+       /* Loop backwards to find the last start event for the uuid and
+        * update all resource records related to that guest session.
+        */
+       for (it = events->tail; it; it = it->prev) {
+               event = it->data;
+               if (event->success && event->uuid &&
+                   strcmp(uuid, event->uuid) == 0) {
+                       if (event->type == ET_START) {
+                               /* If an old session is found it's no longer
+                                * necessary to update the resource records.
+                                */
+                               if (event->end || start)
+                                       break;
+                               /* This is the start event related to the
+                                * current session. */
+                               start = event;
+                       } else if (event->type == ET_STOP ||
+                                  event->type == ET_MACHINE_ID) {
+                               /* Old session found. */
+                               break;
+                       } else if (event->type == ET_RES && event->end == 0) {
+                               /* Update the resource assignments. */
+                               event->end = time;
+                               add_proof(event, au);
+                       }
+               }
+       }
+       if (start == NULL) {
+               if (debug) {
+                       fprintf(stderr, "Couldn't find the correlated start i"
+                                       "record to the stop event.\n");
+               }
+               return 0;
+       }
+
+       /* Create a new stop event */
+       stop = event_alloc();
+       if (stop == NULL)
+               return 1;
+       stop->type = ET_STOP;
+       stop->uuid = copy_str(uuid);
+       stop->name = copy_str(name);
+       stop->success = success;
+       stop->uid = uid;
+       stop->start = time;
+       auparse_first_record(au);
+       if (auparse_find_field(au, "vm-pid"))
+               stop->pid = auparse_get_field_int(au);
+       add_proof(stop, au);
+       if (list_append(events, stop) == NULL) {
+               event_free(stop);
+               return 1;
+       }
+
+       /* Update the correlated start event. */
+       if (success) {
+               start->end = time;
+               add_proof(start, au);
+       }
+       return 0;
+}
+
+int process_control_event(auparse_state_t *au)
+{
+       const char *op;
+
+       op = auparse_find_field(au, "op");
+       if (op == NULL) {
+               if (debug)
+                       fprintf(stderr, "Invalid op field.\n");
+               return 0;
+       }
+
+       if (strcmp("start", op) == 0) {
+               if (add_start_guest_event(au))
+                       return 1;
+       } else if (strcmp("stop", op) == 0) {
+               if (add_stop_guest_event(au))
+                       return 1;
+       } else if (debug) {
+               fprintf(stderr, "Unknown op: %s\n", op);
+       }
+       return 0;
+}
+
+inline int is_resource(const char *res)
+{
+       if (res == NULL ||
+           res[0] == '\0' ||
+           strcmp("0", res) == 0 ||
+           strcmp("?", res) == 0)
+               return 0;
+       return 1;
+}
+
+int add_resource(auparse_state_t *au, const char *uuid, uid_t uid, time_t time,
+               const char *name, int success, const char *reason,
+               const char *res_type, const char *res)
+{
+       if (!is_resource(res))
+               return 0;
+
+       struct event *event = event_alloc();
+       if (event == NULL)
+               return 1;
+       event->type = ET_RES;
+       event->uuid = copy_str(uuid);
+       event->name = copy_str(name);
+       event->success = success;
+       event->reason = copy_str(reason);
+       event->res_type = copy_str(res_type);
+       event->res = copy_str(res);
+       event->uid = uid;
+       event->start = time;
+       add_proof(event, au);
+       if (list_append(events, event) == NULL) {
+               event_free(event);
+               return 1;
+       }
+       return 0;
+}
+
+int update_resource(auparse_state_t *au, const char *uuid, uid_t uid,
+               time_t time, const char *name, int success, const char *reason,
+               const char *res_type, const char *res)
+{
+       if (!is_resource(res) || !success)
+               return 0;
+
+       list_node_t *it;
+       struct event *start = NULL;
+
+       /* Find the last start event for the uuid */
+       for (it = events->tail; it; it = it->prev) {
+               start = it->data;
+               if (start->type == ET_RES &&
+                   start->success &&
+                   start->uuid &&
+                   strcmp(uuid, start->uuid) == 0 &&
+                   strcmp(res_type, start->res_type) == 0 &&
+                   strcmp(res, start->res) == 0)
+                       break;
+       }
+       if (it == NULL) {
+               if (debug) {
+                       fprintf(stderr, "Couldn't find the correlated resource"
+                                       " record to update.\n");
+               }
+               return 0;
+       }
+
+       start->end = time;
+       add_proof(start, au);
+       return 0;
+}
+
+int process_resource_event(auparse_state_t *au)
+{
+       uid_t uid;
+       time_t time;
+       const char *res_type, *uuid, *name;
+       char field[64];
+       const char *reason;
+       int success;
+
+       /* Just skip this record if it failed to get some of the fields */
+       if (extract_virt_fields(au, &uuid, &uid, &time, &name, &success))
+               return 0;
+
+       /* Get the resource type */
+       auparse_first_record(au);
+       res_type = auparse_find_field(au, "resrc");
+       reason = auparse_find_field(au, "reason");
+       if (res_type == NULL) {
+               if (debug)
+                       fprintf(stderr, "Invalid resrc field.\n");
+               return 0;
+       }
+
+       /* Resource records with these types have old and new values. New
+        * values indicate resources assignments and are added to the event
+        * list. Old values are used to update the end time of a resource
+        * assignment.
+        */
+       int rc = 0;
+       if (strcmp("disk", res_type) == 0 ||
+           strcmp("vcpu", res_type) == 0 ||
+           strcmp("mem", res_type) == 0 ||
+           strcmp("net", res_type) == 0) {
+               const char *res;
+               /* Resource removed */
+               snprintf(field, sizeof(field), "old-%s", res_type);
+               res = auparse_find_field(au, field);
+               if (res == NULL && debug) {
+                       fprintf(stderr, "Failed to get %s field.\n", field);
+               } else {
+                       rc += update_resource(au, uuid, uid, time, name,
+                                       success, reason, res_type, res);
+               }
+
+               /* Resource added */
+               snprintf(field, sizeof(field), "new-%s", res_type);
+               res = auparse_find_field(au, field);
+               if (res == NULL && debug) {
+                       fprintf(stderr, "Failed to get %s field.\n", field);
+               } else {
+                       rc += add_resource(au, uuid, uid, time, name, success,
+                                       reason, res_type, res);
+               }
+       } else if (strcmp("cgroup", res_type) == 0) {
+               auparse_first_record(au);
+               const char *cgroup = auparse_find_field(au, "cgroup");
+               rc += add_resource(au, uuid, uid, time, name, success, reason,
+                               res_type, cgroup);
+       } else if (debug) {
+               fprintf(stderr, "Found an unknown resource: %s.\n",
+                               res_type);
+       }
+       return rc;
+}
+
+/* Search for the last machine_id record with the given seclevel */
+struct event *get_machine_id_by_seclevel(const char *seclevel)
+{
+       struct event *machine_id = NULL;
+       list_node_t *it;
+
+       for (it = events->tail; it; it = it->prev) {
+               struct event *event = it->data;
+               if (event->type == ET_MACHINE_ID &&
+                   event->seclevel != NULL &&
+                   strcmp(event->seclevel, seclevel) == 0) {
+                       machine_id = event;
+                       break;
+               }
+       }
+
+       return machine_id;
+}
+
+/* AVC records are correlated to guest through the selinux context. */
+int process_avc(auparse_state_t *au)
+{
+       const char *target, *seclevel;
+       struct event *machine_id, *avc;
+       uid_t uid;
+       time_t time;
+
+       seclevel = get_seclevel(auparse_find_field(au, "tcontext"));
+       if (seclevel == NULL) {
+               if (debug) {
+                       fprintf(stderr, "Security context not found for "
+                                       "AVC event.\n");
+               }
+               return 0;
+       }
+
+       if (extract_virt_fields(au, NULL, &uid, &time, NULL, NULL))
+               return 0;
+
+       machine_id = get_machine_id_by_seclevel(seclevel);
+       if (machine_id == NULL) {
+               if (debug) {
+                       fprintf(stderr, "Couldn't get the security level from "
+                                       "the AVC event.\n");
+               }
+               return 0;
+       }
+
+       avc = event_alloc();
+       if (avc == NULL)
+               return 1;
+       avc->type = ET_AVC;
+
+       /* Guest info */
+       avc->uuid = copy_str(machine_id->uuid);
+       avc->name = copy_str(machine_id->name);
+       memcpy(avc->proof, machine_id->proof, sizeof(avc->proof));
+
+       /* AVC info */
+       avc->start = time;
+       avc->uid = uid;
+       avc->seclevel = copy_str(seclevel);
+       auparse_first_record(au);
+       avc->seresult = copy_str(auparse_find_field(au, "seresult"));
+       avc->seperms = copy_str(auparse_find_field(au, "seperms"));
+       avc->comm = copy_str(auparse_find_field(au, "comm"));
+       avc->target = copy_str(auparse_find_field(au, "name"));
+       add_proof(avc, au);
+       if (list_append(events, avc) == NULL) {
+               event_free(avc);
+               return 1;
+       }
+       return 0;
+}
+
+/* This function tries to correlate an anomaly record to a guest using the qemu
+ * pid or the selinux context. */
+int process_anom(auparse_state_t *au)
+{
+       uid_t uid;
+       time_t time;
+       pid_t pid = -1;
+       list_node_t *it;
+       struct event *anom, *start = NULL;
+
+       /* An anomaly record is correlated to a guest by the process id */
+       if (auparse_find_field(au, "pid")) {
+               pid = auparse_get_field_int(au);
+       } else {
+               if (debug) {
+                       fprintf(stderr, "Found an anomaly record "
+                                       "without pid.\n");
+               }
+       }
+
+       /* Loop backwards to find a running guest with the same pid. */
+       if (pid >= 0) {
+               for (it = events->tail; it; it = it->next) {
+                       struct event *event = it->data;
+                       if (event->pid == pid && event->success) {
+                               if (event->type == ET_STOP) {
+                                       break;
+                               } else if (event->type == ET_START) {
+                                       if (event->end == 0)
+                                               start = event;
+                                       break;
+                               }
+                       }
+               }
+       }
+
+       /* Try to match using selinux context */
+       if (start == NULL) {
+               const char *seclevel;
+               struct event *machine_id;
+
+               seclevel = get_seclevel(auparse_find_field(au, "subj"));
+               if (seclevel == NULL) {
+                       if (debug) {
+                               auparse_first_record(au);
+                               const char *text = auparse_get_record_text(au);
+                               fprintf(stderr, "Security context not found "
+                                               "for anomaly event: %s\n",
+                                               text ? text : "");
+                       }
+                       return 0;
+               }
+               machine_id = get_machine_id_by_seclevel(seclevel);
+               if (machine_id == NULL) {
+                       if (debug) {
+                               fprintf(stderr, "Couldn't get the security "
+                                       "level from the anomaly event.\n");
+                       }
+                       return 0;
+               }
+
+               for (it = events->tail; it; it = it->next) {
+                       struct event *event = it->data;
+                       if (event->success &&
+                           strcmp(machine_id->uuid, event->uuid) == 0) {
+                               if (event->type == ET_STOP) {
+                                       break;
+                               } else if (event->type == ET_START) {
+                                       if (event->end == 0)
+                                               start = event;
+                                       break;
+                               }
+                       }
+               }
+       }
+
+       if (start == NULL) {
+               if (debug) {
+                       const char *text = auparse_get_record_text(au);
+                       fprintf(stderr, "Guest not found for "
+                                       "anomaly record: %s.\n",
+                                       text ? text : "");
+               }
+               return 0;
+       }
+
+       if (extract_virt_fields(au, NULL, &uid, &time, NULL, NULL))
+               return 0;
+
+       anom = event_alloc();
+       if (anom == NULL)
+               return 1;
+       anom->type = ET_ANOM;
+       anom->uuid = copy_str(start->uuid);
+       anom->name = copy_str(start->name);
+       anom->uid = uid;
+       anom->start = time;
+       anom->pid = pid;
+       memcpy(anom->proof, start->proof, sizeof(anom->proof));
+       add_proof(anom, au);
+       if (list_append(events, anom) == NULL) {
+               event_free(anom);
+               return 1;
+       }
+       return 0;
+}
+
+int process_shutdown(auparse_state_t *au)
+{
+       uid_t uid = -1;
+       time_t time = 0;
+       struct event *down;
+       list_node_t *it;
+       int success = 0;
+
+       if (extract_virt_fields(au, NULL, &uid, &time, NULL, &success))
+               return 0;
+
+       for (it = events->tail; it; it = it->prev) {
+               struct event *event = it->data;
+               if (event->success) {
+                       if (event->type == ET_START || event->type == ET_RES) {
+                               if (event->end == 0) {
+                                       event->end = time;
+                                       add_proof(event, au);
+                               }
+                       } else if (event->type == ET_DOWN) {
+                               break;
+                       }
+               }
+       }
+
+       down = event_alloc();
+       if (down == NULL)
+               return 1;
+       down->type = ET_DOWN;
+       down->uid = uid;
+       down->start = time;
+       down->success = success;
+       add_proof(down, au);
+       if (list_append(events, down) == NULL) {
+               event_free(down);
+               return 1;
+       }
+       return 0;
+}
+
+/* Convert record type to a string */
+const char *get_rec_type(struct event *e)
+{
+       static char buf[64];
+       if (e == NULL)
+               return "";
+
+       switch (e->type) {
+       case ET_START:
+               return "start";
+       case ET_STOP:
+               return "stop";
+       case ET_RES:
+               return "res";
+       case ET_AVC:
+               return "avc";
+       case ET_ANOM:
+               return "anom";
+       case ET_DOWN:
+               return "down";
+       }
+
+       snprintf(buf, sizeof(buf), "%d", e->type);
+       return buf;
+}
+
+/* Convert uid to a string */
+const char *get_username(struct event *e)
+{
+       static char s[256];
+       if (!e || e->uid < 0) {
+               s[0] = '?';
+               s[1] = '\0';
+       } else {
+               struct passwd *passwd = getpwuid(e->uid);
+               if (passwd == NULL || passwd->pw_name == NULL) {
+                       snprintf(s, sizeof(s), "%d", e->uid);
+               } else {
+                       snprintf(s, sizeof(s), "%s", passwd->pw_name);
+               }
+       }
+       return s;
+}
+
+/* Convert a time period to string */
+const char *get_time_period(struct event *event)
+{
+       size_t i = 0;
+       static char buf[128];
+
+       i += sprintf(buf + i, "%-16.16s", ctime(&event->start));
+       if (event->end) {
+               time_t secs = event->end - event->start;
+               int mins, hours, days;
+               i += sprintf(buf + i, " - %-7.5s", ctime(&event->end) + 11);
+               mins  = (secs / 60) % 60;
+               hours = (secs / 3600) % 24;
+               days  = secs / 86400;
+               if (days) {
+                       i += sprintf(buf + i, "(%d+%02d:%02d)", days, hours,
+                                       mins);
+               } else {
+                       i += sprintf(buf + i, "(%02d:%02d)", hours, mins);
+               }
+       } else {
+               if (!event->success &&
+                   event->type != ET_AVC &&
+                   event->type != ET_ANOM) {
+                       i += sprintf(buf + i, " - failed");
+               }
+       }
+       return buf;
+}
+
+void print_event(struct event *event)
+{
+       /* Auxiliary macro to convert NULL to "" */
+       #define N(str) ((str) ? str : "")
+
+       /* machine id records are used just to get information about
+        * the guests. */
+       if (event->type == ET_MACHINE_ID)
+               return;
+       /* If "--all-events" is not given, only the start event is shown. */
+       if (!all_events_flag && event->type != ET_START)
+               return;
+       /* The type of event is shown only when all records are shown */
+       if (all_events_flag)
+               printf("%-5.5s ", get_rec_type(event));
+
+       /* Print common fields */
+       printf("%-25.25s", N(event->name));
+       if (uuid_flag)
+               printf("\t%-36.36s", N(event->uuid));
+       printf("\t%-11.11s\t%-35.35s", get_username(event),
+                       get_time_period(event));
+
+       /* Print type specific fields */
+       if (event->type == ET_RES) {
+               printf("\t%-12.12s", N(event->res_type));
+               printf("\t%-10.10s", N(event->reason));
+               printf("\t%s", N(event->res));
+       } else if (event->type == ET_MACHINE_ID) {
+               printf("\t%s", N(event->seclevel));
+       } else if (event->type == ET_AVC) {
+               printf("\t%-12.12s", N(event->seperms));
+               printf("\t%-10.10s", N(event->seresult));
+               printf("\t%s\t%s", N(event->comm), N(event->target));
+       }
+       printf("\n");
+
+       /* Print proof */
+       if (proof_flag) {
+               int first = 1;
+               int i, len = sizeof(event->proof)/sizeof(event->proof[0]);
+               printf("    Proof:");
+               for (i = 0; i < len; i++) {
+                       if (event->proof[i].time) {
+                               printf("%s %ld.%03u:%lu",
+                                       (first) ? "" : ",",
+                                       event->proof[i].time,
+                                       event->proof[i].milli,
+                                       event->proof[i].serial);
+                               first = 0;
+                       }
+               }
+               printf("\n\n");
+       }
+}
+
+/* Print all events */
+void print_events()
+{
+       list_node_t *it;
+       for (it = events->head; it; it = it->next) {
+               struct event *event = it->data;
+               if (event)
+                       print_event(event);
+       }
+}
+
+/* Count and print summary */
+void print_summary()
+{
+       /* Summary numbers */
+       time_t start_time = 0, end_time = 0;
+       long start = 0, stop = 0, res = 0, avc = 0, anom = 0,
+            shutdown = 0, failure = 0;
+
+       /* Calculate summary */
+       list_node_t *it;
+       for (it = events->head; it; it = it->next) {
+               struct event *event = it->data;
+               if (event->success == 0 &&
+                   (event->type == ET_START ||
+                    event->type == ET_STOP  ||
+                    event->type == ET_RES)) {
+                       failure++;
+               } else {
+                       switch (event->type) {
+                       case ET_START:
+                               start++;
+                               break;
+                       case ET_STOP:
+                               stop++;
+                               break;
+                       case ET_RES:
+                               res++;
+                               break;
+                       case ET_AVC:
+                               avc++;
+                               break;
+                       case ET_ANOM:
+                               anom++;
+                               break;
+                       case ET_DOWN:
+                               shutdown++;
+                               break;
+                       }
+               }
+
+               /* Calculate time range */
+               if (event->start) {
+                       if (start_time == 0 || event->start < start_time) {
+                               start_time = event->start;
+                       }
+                       if (end_time == 0 || event->start > end_time) {
+                               end_time = event->start;
+                       }
+               }
+               if (event->end) {
+                       if (start_time == 0 || event->end < start_time) {
+                               start_time = event->end;
+                       }
+                       if (end_time == 0 || event->end > end_time) {
+                               end_time = event->end;
+                       }
+               }
+
+       }
+
+       /* Print summary */
+       printf("Range of time for report:       %-.16s - %-.16s\n",
+                       (start_time) ? ctime(&start_time) : "undef",
+                       (end_time) ? ctime(&end_time) : "undef");
+       printf("Number of guest starts:         %ld\n", start);
+       printf("Number of guest stops:          %ld\n", stop);
+       printf("Number of resource assignments: %ld\n", res);
+       printf("Number of related AVCs:         %ld\n", avc);
+       printf("Number of related anomalies:    %ld\n", anom);
+       printf("Number of host shutdowns:       %ld\n", shutdown);
+       printf("Number of failed operations:    %ld\n", failure);
+}
+
+int main(int argc, char **argv)
+{
+       int rc = 0;
+       auparse_state_t *au = NULL;
+
+       setlocale(LC_ALL, "");
+       if (parse_args(argc, argv))
+               goto error;
+       if (help_flag) {
+               usage(stdout);
+               goto exit;
+       }
+
+       /* Initialize event list*/
+       events = list_new((list_free_data_fn*) event_free);
+       if (events == NULL)
+               goto unexpected_error;
+
+       /* Initialize auparse */
+       au = init_auparse();
+       if (au == NULL)
+               goto error;
+       if (create_search_criteria(au))
+               goto error;
+
+       while (ausearch_next_event(au) > 0) {
+               const char *op;
+               int err = 0;
+
+               switch(auparse_get_type(au)) {
+               case AUDIT_VIRT_MACHINE_ID:
+                       err = process_machine_id_event(au);
+                       break;
+               case AUDIT_VIRT_CONTROL:
+                       err = process_control_event(au);
+                       break;
+               case AUDIT_VIRT_RESOURCE:
+                       err = process_resource_event(au);
+                       break;
+               case AUDIT_AVC:
+                       err = process_avc(au);
+                       break;
+               case AUDIT_FIRST_ANOM_MSG ... AUDIT_LAST_ANOM_MSG:
+               case AUDIT_FIRST_KERN_ANOM_MSG ... AUDIT_LAST_KERN_ANOM_MSG:
+                       err = process_anom(au);
+                       break;
+               case AUDIT_SYSTEM_SHUTDOWN:
+                       err = process_shutdown(au);
+                       break;
+               }
+               if (err) {
+                       goto unexpected_error;
+               }
+               auparse_next_event(au);
+       }
+
+       /* Show results */
+       if (summary_flag) {
+               print_summary();
+       } else {
+               print_events();
+       }
+
+       /* success */
+       goto exit;
+
+unexpected_error:
+       fprintf(stderr, "Unexpected error\n");
+error:
+       rc = 1;
+exit:
+       if (au)
+               auparse_destroy(au);
+       list_free(events);
+       if (debug)
+               fprintf(stdout, "Exit code: %d\n", rc);
+       return rc;
+}
+
-- 
1.7.1

--
Linux-audit mailing list
[email protected]
https://www.redhat.com/mailman/listinfo/linux-audit

Reply via email to