Imported Upstream version 2.4.3
[platform/upstream/audit.git] / tools / auvirt / auvirt.c
1 /*
2  * auvirt.c - A tool to extract data related to virtualization.
3  * Copyright (c) 2011 IBM Corp.
4  * All Rights Reserved.
5  *
6  * This software may be freely redistributed and/or modified under the
7  * terms of the GNU General Public License as published by the Free
8  * Software Foundation; either version 2, or (at your option) any
9  * later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; see the file COPYING. If not, write to the
18  * Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19  *
20  * Authors:
21  *   Marcelo Henrique Cerri <mhcerri@br.ibm.com>
22  */
23
24 #include "config.h"
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <errno.h>
28 #include <ctype.h>
29 #include <locale.h>
30 #include <string.h>
31 #include <regex.h>
32 #include <time.h>
33 #include <sys/types.h>
34 #include <pwd.h>
35 #include <unistd.h>
36 #include "auparse.h"
37 #include "libaudit.h"
38 #include "ausearch-time.h"
39 #include "auvirt-list.h"
40
41 /* Command line parameters */
42 static int help_flag = 0;
43 static int stdin_flag = 0;
44 static int summary_flag = 0;
45 static int all_events_flag = 0;
46 static int uuid_flag = 0;
47 static int proof_flag = 0;
48 static const char *vm = NULL;
49 static const char *uuid = NULL;
50 static const char *file = NULL;
51 static int debug = 0;
52 /*
53  * The start time and end time given in the command line is stored respectively
54  * in the variables start_time and end_time that are declared/defined in the
55  * files ausearch-time.h and ausearch-time.c. These files are reused from the
56  * ausearch tool source code:
57  *
58  *      time_t start_time = 0;
59  *      time_t end_time = 0;
60  */
61
62 /* List of events */
63 enum event_type {
64         ET_NONE = 0, ET_START, ET_STOP, ET_MACHINE_ID, ET_AVC, ET_RES, ET_ANOM,
65         ET_DOWN
66 };
67 struct record_id {
68         time_t time;
69         unsigned int milli;
70         unsigned long serial;
71 };
72 struct event {
73         enum event_type type;
74         time_t start;
75         time_t end;
76         uid_t uid;
77         char *uuid;
78         char *name;
79         int success;
80         pid_t pid;
81         /* Fields specific for resource events: */
82         char *reason;
83         char *res_type;
84         char *res;
85         /* Fields specific for cgroup resources */
86         char *cgroup_class;
87         char *cgroup_detail;
88         char *cgroup_acl;
89         /* Fields specific for machine id events: */
90         char *seclevel;
91         /* Fields specific for avc events: */
92         char *avc_result;
93         char *avc_operation;
94         char *target;
95         char *comm;
96         char *context;
97         /* Fields to print proof information: */
98         struct record_id proof[4];
99 };
100 list_t *events = NULL;
101
102
103 /* Auxiliary functions to allocate and to free events. */
104 struct event *event_alloc(void)
105 {
106         struct event *event = malloc(sizeof(struct event));
107         if (event) {
108                 /* The new event is initialized with values that represents
109                  * unset values: -1 for uid and pid and 0 (or NULL) for numbers
110                  * and pointers. For example, event->end = 0 represents an
111                  * unfinished event.
112                  */
113                 memset(event, 0, sizeof(struct event));
114                 event->uid = -1;
115                 event->pid = -1;
116         }
117         return event;
118 }
119
120 void event_free(struct event *event)
121 {
122         if (event) {
123                 free(event->uuid);
124                 free(event->name);
125                 free(event->reason);
126                 free(event->res_type);
127                 free(event->res);
128                 free(event->avc_result);
129                 free(event->avc_operation);
130                 free(event->seclevel);
131                 free(event->target);
132                 free(event->comm);
133                 free(event->cgroup_class);
134                 free(event->cgroup_detail);
135                 free(event->cgroup_acl);
136                 free(event->context);
137                 free(event);
138         }
139 }
140
141 inline char *copy_str(const char *str)
142 {
143         return (str) ? strdup(str) : NULL;
144 }
145
146 void usage(FILE *output)
147 {
148         fprintf(output, "usage: auvirt [--stdin] [--all-events] [--summary] "
149                         "[--start start-date [start-time]] "
150                         "[--end end-date [end-time]] [--file file-name] "
151                         "[--show-uuid] [--proof] "
152                         "[--uuid uuid] [--vm vm-name]\n");
153 }
154
155 /* Parse and check command line arguments */
156 int parse_args(int argc, char **argv)
157 {
158         /* Based on http://www.ietf.org/rfc/rfc4122.txt */
159         const char *uuid_pattern = "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-"
160                 "[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$";
161         int i, rc = 0;
162         regex_t uuid_regex;
163
164         if (regcomp(&uuid_regex, uuid_pattern, REG_EXTENDED)) {
165                 fprintf(stderr, "Failed to initialize program.\n");
166                 return 1;
167         }
168
169         for (i = 1; i < argc; i++) {
170                 const char *opt = argv[i];
171                 if (opt[0] != '-') {
172                         fprintf(stderr, "Argument not expected: %s\n", opt);
173                         goto error;
174                 } else if (strcmp("--vm", opt) == 0 ||
175                            strcmp("-v", opt) == 0) {
176                         if ((i + 1) >= argc || argv[i + 1][0] == '-') {
177                                 fprintf(stderr, "\"%s\" option requires "
178                                                 "an argument.\n", opt);
179                                 goto error;
180                         }
181                         vm = argv[++i];
182                 } else if (strcmp("--uuid", opt) == 0 ||
183                            strcmp("-u", opt) == 0) {
184                         if ((i + 1) >= argc || argv[i + 1][0] == '-') {
185                                 fprintf(stderr, "\"%s\" option requires "
186                                                 "an argument.\n", opt);
187                                 goto error;
188                         }
189                         if (regexec(&uuid_regex, argv[i + 1], 0, NULL, 0)) {
190                                 fprintf(stderr, "Invalid uuid: %s\n",
191                                                 argv[i + 1]);
192                                 goto error;
193                         }
194                         uuid = argv[++i];
195                 } else if (strcmp("--all-events", opt) == 0 ||
196                            strcmp("-a", opt) == 0) {
197                         all_events_flag = 1;
198                 } else if (strcmp("--summary", opt) == 0 ||
199                            strcmp("-s", opt) == 0) {
200                         summary_flag = 1;
201                 } else if (strcmp("--file", opt) == 0 ||
202                            strcmp("-f", opt) == 0) {
203                         if ((i + 1) >= argc || argv[i + 1][0] == '-') {
204                                 fprintf(stderr, "\"%s\" option requires "
205                                                 "an argument.\n", opt);
206                                 goto error;
207                         }
208                         file = argv[++i];
209                 } else if (strcmp("--show-uuid", opt) == 0) {
210                         uuid_flag = 1;
211                 } else if (strcmp("--stdin", opt) == 0) {
212                         stdin_flag = 1;
213                 } else if (strcmp("--proof", opt) == 0) {
214                         proof_flag = 1;
215                 } else if (strcmp("--help", opt) == 0 ||
216                            strcmp("-h", opt) == 0) {
217                         help_flag = 1;
218                         goto exit;
219                 } else if (strcmp("--start", opt) == 0 ||
220                            strcmp("-ts", opt) == 0) {
221                         const char *date, *time = NULL;
222                         if ((i + 1) >= argc || argv[i + 1][0] == '-') {
223                                 fprintf(stderr, "\"%s\" option requires at "
224                                                 "least one argument.\n", opt);
225                                 goto error;
226                         }
227                         date = argv[++i];
228                         if ((i + 1) < argc && argv[i + 1][0] != '-')
229                                 time = argv[++i];
230                         /* This will set start_time */
231                         if(ausearch_time_start(date, time))
232                                 goto error;
233                 } else if (strcmp("--end", opt) == 0 ||
234                            strcmp("-te", opt) == 0) {
235                         const char *date, *time = NULL;
236                         if ((i + 1) >= argc || argv[i + 1][0] == '-') {
237                                 fprintf(stderr, "\"%s\" option requires at "
238                                                 "least one argument.\n", opt);
239                                 goto error;
240                         }
241                         date = argv[++i];
242                         if ((i + 1) < argc && argv[i + 1][0] != '-')
243                                 time = argv[++i];
244                         /* This will set end_time */
245                         if (ausearch_time_end(date, time))
246                                 goto error;
247                 } else if (strcmp("--debug", opt) == 0) {
248                         debug = 1;
249                 } else {
250                         fprintf(stderr, "Unknown option \"%s\".\n", opt);
251                         goto error;
252                 }
253         }
254
255         /* Validate conflicting options */
256         if (stdin_flag && file) {
257                 fprintf(stderr, "\"--sdtin\" and \"--file\" options "
258                                 "must not be specified together.\n");
259                 goto error;
260         }
261
262         if (debug) {
263                 fprintf(stderr, "help_flag='%i'\n", help_flag);
264                 fprintf(stderr, "stdin_flag='%i'\n", stdin_flag);
265                 fprintf(stderr, "all_events_flag='%i'\n", all_events_flag);
266                 fprintf(stderr, "summary_flag='%i'\n", summary_flag);
267                 fprintf(stderr, "uuid='%s'\n", uuid ? uuid : "(null)");
268                 fprintf(stderr, "vm='%s'\n", vm ? vm : "(null)");
269                 fprintf(stderr, "file='%s'\n", file ? file : "(null)");
270                 fprintf(stderr, "start_time='%-.16s'\n", (start_time == 0L) ?
271                                 "" : ctime(&start_time));
272                 fprintf(stderr, "end_time='%-.16s'\n", (end_time == 0L) ?
273                                 "" : ctime(&end_time));
274         }
275
276 exit:
277         regfree(&uuid_regex);
278         return rc;
279 error:
280         rc = 1;
281         goto exit;
282 }
283
284 /* Initialize an auparse_state_t with the correct log source. */
285 auparse_state_t *init_auparse(void)
286 {
287         auparse_state_t *au = NULL;
288         if (stdin_flag) {
289                 au = auparse_init(AUSOURCE_FILE_POINTER, stdin);
290         } else if (file) {
291                 au = auparse_init(AUSOURCE_FILE, file);
292         } else {
293                 if (getuid()) {
294                         fprintf(stderr, "You probably need to be root for "
295                                         "this to work\n");
296                 }
297                 au = auparse_init(AUSOURCE_LOGS, NULL);
298         }
299         if (au == NULL) {
300                 fprintf(stderr, "Error: %s\n", strerror(errno));
301         }
302         return au;
303 }
304
305 /* Create a criteria to search for the virtualization related records */
306 int create_search_criteria(auparse_state_t *au)
307 {
308         char *error = NULL;
309         char expr[1024];
310         snprintf(expr, sizeof(expr),
311                 "(\\record_type >= %d && \\record_type <= %d)",
312                 AUDIT_FIRST_VIRT_MSG, AUDIT_LAST_VIRT_MSG);
313         if (ausearch_add_expression(au, expr, &error, AUSEARCH_RULE_CLEAR)) {
314                 fprintf(stderr, "Criteria error: %s\n", error);
315                 free(error);
316                 return 1;
317         }
318         if (uuid) {
319                 if (ausearch_add_item(au, "uuid", "=", uuid,
320                                         AUSEARCH_RULE_AND)) {
321                         fprintf(stderr, "Criteria error: uuid\n");
322                         return 1;
323                 }
324         }
325         if (vm) {
326                 if (ausearch_add_interpreted_item(au, "vm", "=", vm,
327                                         AUSEARCH_RULE_AND)) {
328                         fprintf(stderr, "Criteria error: id\n");
329                         return 1;
330                 }
331         }
332         if (all_events_flag || summary_flag) {
333                 if (ausearch_add_item(au, "type", "=", "AVC",
334                                         AUSEARCH_RULE_OR)) {
335                         fprintf(stderr, "Criteria error: AVC\n");
336                         return 1;
337                 }
338                 if (ausearch_add_item(au, "type", "=", "SYSTEM_SHUTDOWN",
339                                         AUSEARCH_RULE_OR)) {
340                         fprintf(stderr, "Criteria error: shutdown\n");
341                         return 1;
342                 }
343                 snprintf(expr, sizeof(expr),
344                         "(\\record_type >= %d && \\record_type <= %d) ||"
345                         "(\\record_type >= %d && \\record_type <= %d)",
346                         AUDIT_FIRST_ANOM_MSG, AUDIT_LAST_ANOM_MSG,
347                         AUDIT_FIRST_KERN_ANOM_MSG, AUDIT_LAST_KERN_ANOM_MSG);
348                 if (ausearch_add_expression(au, expr, &error,
349                                         AUSEARCH_RULE_OR)) {
350                         fprintf(stderr, "Criteria error: %s\n", error);
351                         free(error);
352                         return 1;
353                 }
354         }
355         if (start_time) {
356                 if (ausearch_add_timestamp_item(au, ">=", start_time, 0,
357                                         AUSEARCH_RULE_AND)) {
358                         fprintf(stderr, "Criteria error: start_time\n");
359                         return 1;
360                 }
361         }
362         if (end_time) {
363                 if (ausearch_add_timestamp_item(au, "<=", end_time, 0,
364                                         AUSEARCH_RULE_AND)) {
365                         fprintf(stderr, "Criteria error: end_time\n");
366                         return 1;
367                 }
368         }
369         return 0;
370 }
371
372 /* Extract the most common fields from virtualization-related records. */
373 int extract_virt_fields(auparse_state_t *au, const char **p_uuid,
374                 uid_t *p_uid, time_t *p_time, const char **p_name,
375                 int *p_suc)
376 {
377         const char *field;
378         auparse_first_record(au);
379         /* Order matters */
380         if (p_uid) {
381                 if (!auparse_find_field(au, field = "uid"))
382                         goto error;
383                 *p_uid = auparse_get_field_int(au);
384         }
385         if (p_name) {
386                 if (!auparse_find_field(au, field = "vm"))
387                         goto error;
388                 *p_name = auparse_interpret_field(au);
389         }
390         if (p_uuid) {
391                 if (!auparse_find_field(au, field = "uuid"))
392                         goto error;
393                 *p_uuid = auparse_get_field_str(au);
394         }
395         if (p_suc) {
396                 const char *res = auparse_find_field(au, field = "res");
397                 if (res == NULL)
398                         goto error;
399                 *p_suc = (strcmp("success", res) == 0) ? 1 : 0;
400         }
401         if (p_time) {
402                 *p_time = auparse_get_time(au);
403         }
404         return 0;
405
406 error:
407         if (debug) {
408                 fprintf(stderr, "Failed to get field \"%s\" for record "
409                                 "%ld.%03u:%lu\n", field ? field : "",
410                                 auparse_get_time(au),
411                                 auparse_get_milli(au),
412                                 auparse_get_serial(au));
413         }
414         return 1;
415 }
416
417 /* Return label and categories from a security context. */
418 const char *get_seclevel(const char *seclabel)
419 {
420         /*
421          * system_u:system_r:svirt_t:s0:c107,c434
422          *                           \____ _____/
423          *                                '
424          *                           level + cat
425          */
426         int c = 0;
427         for (;seclabel && *seclabel; seclabel++) {
428                 if (*seclabel == ':')
429                         c += 1;
430                 if (c == 3)
431                         return seclabel + 1;
432         }
433         return NULL;
434 }
435
436 int add_proof(struct event *event, auparse_state_t *au)
437 {
438         if (!proof_flag)
439                 return 0;
440
441         size_t i, proof_len = sizeof(event->proof)/sizeof(event->proof[0]);
442         for (i = 0; i < proof_len; i++) {
443                 if (event->proof[i].time == 0)
444                         break;
445         }
446         if (i == proof_len) {
447                 if (debug)
448                         fprintf(stderr, "Failed to add proof.\n");
449                 return 1;
450         }
451
452         event->proof[i].time = auparse_get_time(au);
453         event->proof[i].milli = auparse_get_milli(au);
454         event->proof[i].serial = auparse_get_serial(au);
455         return 0;
456 }
457
458 /*
459  * machine_id records are used to get the selinux context associated to a
460  * guest.
461  */
462 int process_machine_id_event(auparse_state_t *au)
463 {
464         uid_t uid;
465         time_t time;
466         const char *seclevel, *uuid, *name;
467         struct event *event;
468         int success;
469
470         seclevel = get_seclevel(auparse_find_field(au, "vm-ctx"));
471         if (seclevel == NULL) {
472                 if (debug)
473                         fprintf(stderr, "Security context not found for "
474                                         "MACHINE_ID event.\n");
475         }
476
477         if (extract_virt_fields(au, &uuid, &uid, &time, &name, &success))
478                 return 0;
479
480         event = event_alloc();
481         if (event == NULL)
482                 return 1;
483         event->type = ET_MACHINE_ID;
484         event->uuid = copy_str(uuid);
485         event->name = copy_str(name);
486         event->success = success;
487         event->seclevel = copy_str(seclevel);
488         event->uid = uid;
489         event->start = time;
490         add_proof(event, au);
491         if (list_append(events, event) == NULL) {
492                 event_free(event);
493                 return 1;
494         }
495         return 0;
496 }
497
498 int add_start_guest_event(auparse_state_t *au)
499 {
500         struct event *start;
501         uid_t uid;
502         time_t time;
503         const char *uuid, *name;
504         int success;
505         list_node_t *it;
506
507         /* Just skip this record if it failed to get some of the fields */
508         if (extract_virt_fields(au, &uuid, &uid, &time, &name, &success))
509                 return 0;
510
511         /* On failure, loop backwards to update all the resources associated to
512          * the last session of this guest. When a machine_id or a stop event is
513          * found the loop can be broken because a machine_id is created at the
514          * beginning of a session and a stop event indicates a previous
515          * session.
516          */
517         if (!success) {
518                 for (it = events->tail; it; it = it->prev) {
519                         struct event *event = it->data;
520                         if (event->success && event->uuid &&
521                             strcmp(uuid, event->uuid) == 0) {
522                                 if (event->type == ET_STOP ||
523                                     event->type == ET_MACHINE_ID) {
524                                         /* An old session found. */
525                                         break;
526                                 } else if (event->type == ET_RES &&
527                                            event->end == 0) {
528                                         event->end = time;
529                                         add_proof(event, au);
530                                 }
531                         }
532                 }
533         }
534
535         start = event_alloc();
536         if (start == NULL)
537                 return 1;
538         start->type = ET_START;
539         start->uuid = copy_str(uuid);
540         start->name = copy_str(name);
541         start->success = success;
542         start->uid = uid;
543         start->start = time;
544         auparse_first_record(au);
545         if (auparse_find_field(au, "vm-pid"))
546                 start->pid = auparse_get_field_int(au);
547         add_proof(start, au);
548         if (list_append(events, start) == NULL) {
549                 event_free(start);
550                 return 1;
551         }
552         return 0;
553 }
554
555 int add_stop_guest_event(auparse_state_t *au)
556 {
557         list_node_t *it;
558         struct event *stop, *start = NULL, *event = NULL;
559         uid_t uid;
560         time_t time;
561         const char *uuid, *name;
562         int success;
563
564         /* Just skip this record if it failed to get some of the fields */
565         if (extract_virt_fields(au, &uuid, &uid, &time, &name, &success))
566                 return 0;
567
568         /* Loop backwards to find the last start event for the uuid and
569          * update all resource records related to that guest session.
570          */
571         for (it = events->tail; it; it = it->prev) {
572                 event = it->data;
573                 if (event->success && event->uuid &&
574                     strcmp(uuid, event->uuid) == 0) {
575                         if (event->type == ET_START) {
576                                 /* If an old session is found it's no longer
577                                  * necessary to update the resource records.
578                                  */
579                                 if (event->end || start)
580                                         break;
581                                 /* This is the start event related to the
582                                  * current session. */
583                                 start = event;
584                         } else if (event->type == ET_STOP ||
585                                    event->type == ET_MACHINE_ID) {
586                                 /* Old session found. */
587                                 break;
588                         } else if (event->type == ET_RES && event->end == 0) {
589                                 /* Update the resource assignments. */
590                                 event->end = time;
591                                 add_proof(event, au);
592                         }
593                 }
594         }
595         if (start == NULL) {
596                 if (debug) {
597                         fprintf(stderr, "Couldn't find the correlated start "
598                                         "record to the stop event.\n");
599                 }
600                 return 0;
601         }
602
603         /* Create a new stop event */
604         stop = event_alloc();
605         if (stop == NULL)
606                 return 1;
607         stop->type = ET_STOP;
608         stop->uuid = copy_str(uuid);
609         stop->name = copy_str(name);
610         stop->success = success;
611         stop->uid = uid;
612         stop->start = time;
613         auparse_first_record(au);
614         if (auparse_find_field(au, "vm-pid"))
615                 stop->pid = auparse_get_field_int(au);
616         add_proof(stop, au);
617         if (list_append(events, stop) == NULL) {
618                 event_free(stop);
619                 return 1;
620         }
621
622         /* Update the correlated start event. */
623         if (success) {
624                 start->end = time;
625                 add_proof(start, au);
626         }
627         return 0;
628 }
629
630 int process_control_event(auparse_state_t *au)
631 {
632         const char *op;
633
634         op = auparse_find_field(au, "op");
635         if (op == NULL) {
636                 if (debug)
637                         fprintf(stderr, "Invalid op field.\n");
638                 return 0;
639         }
640
641         if (strcmp("start", op) == 0) {
642                 if (add_start_guest_event(au))
643                         return 1;
644         } else if (strcmp("stop", op) == 0) {
645                 if (add_stop_guest_event(au))
646                         return 1;
647         } else if (debug) {
648                 fprintf(stderr, "Unknown op: %s\n", op);
649         }
650         return 0;
651 }
652
653 inline int is_resource(const char *res)
654 {
655         if (res == NULL ||
656             res[0] == '\0' ||
657             strcmp("0", res) == 0 ||
658             strcmp("?", res) == 0)
659                 return 0;
660         return 1;
661 }
662
663 int add_resource(auparse_state_t *au, const char *uuid, uid_t uid, time_t time,
664                 const char *name, int success, const char *reason,
665                 const char *res_type, const char *res)
666 {
667         if (!is_resource(res))
668                 return 0;
669
670         struct event *event = event_alloc();
671         if (event == NULL)
672                 return 1;
673         event->type = ET_RES;
674         event->uuid = copy_str(uuid);
675         event->name = copy_str(name);
676         event->success = success;
677         event->reason = copy_str(reason);
678         event->res_type = copy_str(res_type);
679         event->res = copy_str(res);
680         event->uid = uid;
681         event->start = time;
682         add_proof(event, au);
683
684         /* Get cgroup specific fields. */
685         if (strcmp("cgroup", res_type) == 0) {
686                 event->cgroup_class = copy_str(auparse_find_field(au, "class"));
687                 if (event->cgroup_class) {
688                         const char *detail = NULL;
689                         if (strcmp("path", event->cgroup_class) == 0) {
690                                 if (auparse_find_field(au, "path"))
691                                         detail = auparse_interpret_field(au);
692                         } else if (strcmp("major", event->cgroup_class) == 0) {
693                                 detail = auparse_find_field(au, "category");
694                         }
695                         event->cgroup_detail = copy_str(detail);
696                 }
697                 event->cgroup_acl = copy_str(auparse_find_field(au, "acl"));
698         }
699
700         if (list_append(events, event) == NULL) {
701                 event_free(event);
702                 return 1;
703         }
704         return 0;
705 }
706
707 int update_resource(auparse_state_t *au, const char *uuid, uid_t uid,
708                 time_t time, const char *name, int success, const char *reason,
709                 const char *res_type, const char *res)
710 {
711         if (!is_resource(res) || !success)
712                 return 0;
713
714         list_node_t *it;
715         struct event *start = NULL;
716
717         /* Find the last start event for the uuid */
718         for (it = events->tail; it; it = it->prev) {
719                 start = it->data;
720                 if (start->type == ET_RES &&
721                     start->success &&
722                     start->uuid &&
723                     strcmp(uuid, start->uuid) == 0 &&
724                     strcmp(res_type, start->res_type) == 0 &&
725                     strcmp(res, start->res) == 0)
726                         break;
727         }
728         if (it == NULL) {
729                 if (debug) {
730                         fprintf(stderr, "Couldn't find the correlated resource"
731                                         " record to update for %s.\n", res_type);
732                 }
733                 return 0;
734         }
735
736         start->end = time;
737         add_proof(start, au);
738         return 0;
739 }
740
741 int process_resource_event(auparse_state_t *au)
742 {
743         uid_t uid;
744         time_t time;
745         const char *res_type, *uuid, *name;
746         char field[64];
747         const char *reason;
748         int success;
749
750         /* Just skip this record if it failed to get some of the fields */
751         if (extract_virt_fields(au, &uuid, &uid, &time, &name, &success))
752                 return 0;
753
754         /* Get the resource type */
755         auparse_first_record(au);
756         res_type = auparse_find_field(au, "resrc");
757         reason = auparse_find_field(au, "reason");
758         if (res_type == NULL) {
759                 if (debug)
760                         fprintf(stderr, "Invalid resrc field.\n");
761                 return 0;
762         }
763
764         /* Resource records with these types have old and new values. New
765          * values indicate resources assignments and are added to the event
766          * list. Old values are used to update the end time of a resource
767          * assignment.
768          */
769         int rc = 0;
770         if (strcmp("disk", res_type) == 0 ||
771             strcmp("vcpu", res_type) == 0 ||
772             strcmp("mem", res_type) == 0 ||
773             strcmp("rng", res_type) == 0 ||
774             strcmp("net", res_type) == 0) {
775                 const char *res = NULL;
776                 /* Resource removed */
777                 snprintf(field, sizeof(field), "old-%s", res_type);
778                 if(auparse_find_field(au, field))
779                         res = auparse_interpret_field(au);
780                 if (res == NULL && debug) {
781                         fprintf(stderr, "Failed to get %s field.\n", field);
782                 } else {
783                         rc += update_resource(au, uuid, uid, time, name,
784                                         success, reason, res_type, res);
785                 }
786
787                 /* Resource added */
788                 res = NULL;
789                 snprintf(field, sizeof(field), "new-%s", res_type);
790                 if (auparse_find_field(au, field))
791                         res = auparse_interpret_field(au);
792                 if (res == NULL && debug) {
793                         fprintf(stderr, "Failed to get %s field.\n", field);
794                 } else {
795                         rc += add_resource(au, uuid, uid, time, name, success,
796                                         reason, res_type, res);
797                 }
798         } else if (strcmp("cgroup", res_type) == 0) {
799                 auparse_first_record(au);
800                 const char *cgroup = NULL;
801                 if (auparse_find_field(au, "cgroup"))
802                         cgroup = auparse_interpret_field(au);
803                 rc += add_resource(au, uuid, uid, time, name, success, reason,
804                                 res_type, cgroup);
805         } else if (debug) {
806                 fprintf(stderr, "Found an unknown resource: %s.\n",
807                                 res_type);
808         }
809         return rc;
810 }
811
812 /* Search for the last machine_id record with the given seclevel */
813 struct event *get_machine_id_by_seclevel(const char *seclevel)
814 {
815         struct event *machine_id = NULL;
816         list_node_t *it;
817
818         for (it = events->tail; it; it = it->prev) {
819                 struct event *event = it->data;
820                 if (event->type == ET_MACHINE_ID &&
821                     event->seclevel != NULL &&
822                     strcmp(event->seclevel, seclevel) == 0) {
823                         machine_id = event;
824                         break;
825                 }
826         }
827
828         return machine_id;
829 }
830
831 int process_avc_selinux_context(auparse_state_t *au, const char *context)
832 {
833         const char *seclevel;
834         struct event *machine_id, *avc;
835         uid_t uid;
836         time_t time;
837
838         seclevel = get_seclevel(auparse_find_field(au, context));
839         if (seclevel == NULL) {
840                 if (debug) {
841                         fprintf(stderr, "Security context not found "
842                                         "for AVC event.\n");
843                 }
844                 return 0;
845         }
846
847         if (extract_virt_fields(au, NULL, &uid, &time, NULL, NULL))
848                 return 0;
849
850         machine_id = get_machine_id_by_seclevel(seclevel);
851         if (machine_id == NULL) {
852                 if (debug) {
853                         fprintf(stderr, "Couldn't get the security "
854                                         "level from the AVC event.\n");
855                 }
856                 return 0;
857         }
858
859         avc = event_alloc();
860         if (avc == NULL)
861                 return 1;
862         avc->type = ET_AVC;
863
864         /* Guest info */
865         avc->uuid = copy_str(machine_id->uuid);
866         avc->name = copy_str(machine_id->name);
867         memcpy(avc->proof, machine_id->proof, sizeof(avc->proof));
868
869         /* AVC info */
870         avc->start = time;
871         avc->uid = uid;
872         avc->seclevel = copy_str(seclevel);
873         auparse_first_record(au);
874         avc->avc_result = copy_str(auparse_find_field(au, "seresult"));
875         avc->avc_operation = copy_str(auparse_find_field(au, "seperms"));
876         if (auparse_find_field(au, "comm"))
877                 avc->comm = copy_str(auparse_interpret_field(au));
878         if (auparse_find_field(au, "name"))
879                 avc->target = copy_str(auparse_interpret_field(au));
880
881         /* get the context related to the permission that was denied. */
882         if (avc->avc_operation) {
883                 const char *ctx = NULL;
884                 if (strcmp("relabelfrom", avc->avc_operation) == 0) {
885                         ctx = auparse_find_field(au, "scontext");
886                 } else if (strcmp("relabelto", avc->avc_operation) == 0) {
887                         ctx = auparse_find_field(au, "tcontext");
888                 }
889                 avc->context = copy_str(ctx);
890         }
891
892         add_proof(avc, au);
893         if (list_append(events, avc) == NULL) {
894                 event_free(avc);
895                 return 1;
896         }
897         return 0;
898 }
899
900 /* AVC records are correlated to guest through the selinux context. */
901 int process_avc_selinux(auparse_state_t *au)
902 {
903         const char **context;
904         const char *contexts[] = { "tcontext", "scontext", NULL };
905
906         for (context = contexts; context && *context; context++) {
907                 if (process_avc_selinux_context(au, *context))
908                         return 1;
909         }
910         return 0;
911 }
912
913 #ifdef WITH_APPARMOR
914 int process_avc_apparmor_source(auparse_state_t *au)
915 {
916         uid_t uid = -1;
917         time_t time = 0;
918         struct event *avc;
919         const char *target;
920
921         /* Get the target object. */
922         if (auparse_find_field(au, "name") == NULL) {
923                 if (debug) {
924                         auparse_first_record(au);
925                         fprintf(stderr, "Couldn't get the resource name from "
926                                         "the AVC record: %s\n",
927                                         auparse_get_record_text(au));
928                 }
929                 return 0;
930         }
931         target = auparse_interpret_field(au);
932
933         /* Loop backwards to find a guest session with the target object
934          * assigned to. */
935         struct list_node_t *it;
936         struct event *res = NULL;
937         for (it = events->tail; it; it = it->prev) {
938                 struct event *event = it->data;
939                 if (event->success) {
940                         if (event->type == ET_DOWN) {
941                                 /* It's just possible to find a matching guest
942                                  * session in the current host session.
943                                  */
944                                 break;
945                         } else if (event->type == ET_RES &&
946                                    event->end == 0 &&
947                                    event->res != NULL &&
948                                    strcmp(target, event->res) == 0) {
949                                 res = event;
950                                 break;
951                         }
952                 }
953         }
954
955         /* Check if a resource event was found. */
956         if (res == NULL) {
957                 if (debug) {
958                         fprintf(stderr, "Target object not found for AVC "
959                                         "event.\n");
960                 }
961                 return 0;
962         }
963
964         if (extract_virt_fields(au, NULL, &uid, &time, NULL, NULL))
965                 return 0;
966
967         avc = event_alloc();
968         if (avc == NULL)
969                 return 1;
970         avc->type = ET_AVC;
971
972         /* Guest info */
973         avc->uuid = copy_str(res->uuid);
974         avc->name = copy_str(res->name);
975         memcpy(avc->proof, res->proof, sizeof(avc->proof));
976
977         /* AVC info */
978         avc->start = time;
979         avc->uid = uid;
980         auparse_first_record(au);
981         if (auparse_find_field(au, "apparmor")) {
982                 int i;
983                 avc->avc_result = copy_str(auparse_interpret_field(au));
984                 for (i = 0; avc->avc_result && avc->avc_result[i]; i++) {
985                         avc->avc_result[i] = tolower(avc->avc_result[i]);
986                 }
987         }
988         if (auparse_find_field(au, "operation"))
989                 avc->avc_operation = copy_str(auparse_interpret_field(au));
990         avc->target = copy_str(target);
991         if (auparse_find_field(au, "comm"))
992                 avc->comm = copy_str(auparse_interpret_field(au));
993
994         add_proof(avc, au);
995         if (list_append(events, avc) == NULL) {
996                 event_free(avc);
997                 return 1;
998         }
999         return 0;
1000 }
1001
1002 int process_avc_apparmor_target(auparse_state_t *au)
1003 {
1004         uid_t uid;
1005         time_t time;
1006         const char *profile;
1007         struct event *avc;
1008
1009         /* Get profile associated with the AVC record */
1010         if (auparse_find_field(au, "profile") == NULL) {
1011                 if (debug) {
1012                         auparse_first_record(au);
1013                         fprintf(stderr, "AppArmor profile not found for AVC "
1014                                         "record: %s\n",
1015                                         auparse_get_record_text(au));
1016                 }
1017                 return 0;
1018         }
1019         profile = auparse_interpret_field(au);
1020
1021         /* Break path to get just the basename */
1022         const char *basename = profile + strlen(profile);
1023         while (basename != profile && *basename != '/')
1024                 basename--;
1025         if (*basename == '/')
1026                 basename++;
1027
1028         /* Check if it is an apparmor profile generated by libvirt and get the
1029          * guest UUID from it */
1030         const char *prefix = "libvirt-";
1031         if (strncmp(prefix, basename, strlen(prefix)) != 0) {
1032                 if (debug) {
1033                         fprintf(stderr, "Found a profile which is not "
1034                                         "generated by libvirt: %s\n", profile);
1035                 }
1036                 return 0;
1037         }
1038
1039         /* Try to find a valid guest session */
1040         const char *uuid = basename + strlen(prefix);
1041         struct list_node_t *it;
1042         struct event *machine_id = NULL;
1043         for (it = events->tail; it; it = it->prev) {
1044                 struct event *event = it->data;
1045                 if (event->success) {
1046                         if (event->uuid != NULL &&
1047                             strcmp(event->uuid, uuid) == 0) {
1048                                 /* machine_id is used here instead of the start
1049                                  * event because it is generated before any
1050                                  * other event when a guest is started. So,
1051                                  * it's possible to correlate AVC events that
1052                                  * occurs during a guest start.
1053                                  */
1054                                 if (event->type == ET_MACHINE_ID) {
1055                                         machine_id = event;
1056                                         break;
1057                                 } else if (event->type == ET_STOP) {
1058                                         break;
1059                                 }
1060                         } else if (event->type == ET_DOWN) {
1061                                 break;
1062                         }
1063                 }
1064         }
1065         if (machine_id == NULL) {
1066                 if (debug) {
1067                         fprintf(stderr, "Found an AVC record for an unknown "
1068                                         "guest.\n");
1069                 }
1070                 return 0;
1071         }
1072
1073         if (extract_virt_fields(au, NULL, &uid, &time, NULL, NULL))
1074                 return 0;
1075
1076         avc = event_alloc();
1077         if (avc == NULL)
1078                 return 1;
1079         avc->type = ET_AVC;
1080
1081         /* Guest info */
1082         avc->uuid = copy_str(machine_id->uuid);
1083         avc->name = copy_str(machine_id->name);
1084         memcpy(avc->proof, machine_id->proof, sizeof(avc->proof));
1085
1086         /* AVC info */
1087         avc->start = time;
1088         avc->uid = uid;
1089         auparse_first_record(au);
1090         if (auparse_find_field(au, "apparmor")) {
1091                 int i;
1092                 avc->avc_result = copy_str(auparse_interpret_field(au));
1093                 for (i = 0; avc->avc_result && avc->avc_result[i]; i++) {
1094                         avc->avc_result[i] = tolower(avc->avc_result[i]);
1095                 }
1096         }
1097         if (auparse_find_field(au, "operation"))
1098                 avc->avc_operation = copy_str(auparse_interpret_field(au));
1099         if (auparse_find_field(au, "name"))
1100                 avc->target = copy_str(auparse_interpret_field(au));
1101         if (auparse_find_field(au, "comm"))
1102                 avc->comm = copy_str(auparse_interpret_field(au));
1103
1104         add_proof(avc, au);
1105         if (list_append(events, avc) == NULL) {
1106                 event_free(avc);
1107                 return 1;
1108         }
1109         return 0;
1110 }
1111
1112 /* AVC records are correlated to guest through the apparmor path name. */
1113 int process_avc_apparmor(auparse_state_t *au)
1114 {
1115         if (process_avc_apparmor_target(au))
1116                 return 1;
1117         auparse_first_record(au);
1118         return process_avc_apparmor_source(au);
1119 }
1120 #endif
1121
1122 int process_avc(auparse_state_t *au)
1123 {
1124         /* Check if it is a SELinux AVC record */
1125         if (auparse_find_field(au, "tcontext")) {
1126                 auparse_first_record(au);
1127                 return process_avc_selinux(au);
1128         }
1129
1130 #ifdef WITH_APPARMOR
1131         /* Check if it is an AppArmor AVC record */
1132         auparse_first_record(au);
1133         if (auparse_find_field(au, "apparmor")) {
1134                 auparse_first_record(au);
1135                 return process_avc_apparmor(au);
1136         }
1137 #endif
1138         return 0;
1139 }
1140
1141 /* This function tries to correlate an anomaly record to a guest using the qemu
1142  * pid or the selinux context. */
1143 int process_anom(auparse_state_t *au)
1144 {
1145         uid_t uid;
1146         time_t time;
1147         pid_t pid = -1;
1148         list_node_t *it;
1149         struct event *anom, *start = NULL;
1150
1151         /* An anomaly record is correlated to a guest by the process id */
1152         if (auparse_find_field(au, "pid")) {
1153                 pid = auparse_get_field_int(au);
1154         } else {
1155                 if (debug) {
1156                         fprintf(stderr, "Found an anomaly record "
1157                                         "without pid.\n");
1158                 }
1159         }
1160
1161         /* Loop backwards to find a running guest with the same pid. */
1162         if (pid >= 0) {
1163                 for (it = events->tail; it; it = it->next) {
1164                         struct event *event = it->data;
1165                         if (event->pid == pid && event->success) {
1166                                 if (event->type == ET_STOP) {
1167                                         break;
1168                                 } else if (event->type == ET_START) {
1169                                         if (event->end == 0)
1170                                                 start = event;
1171                                         break;
1172                                 }
1173                         }
1174                 }
1175         }
1176
1177         /* Try to match using selinux context */
1178         if (start == NULL) {
1179                 const char *seclevel;
1180                 struct event *machine_id;
1181
1182                 seclevel = get_seclevel(auparse_find_field(au, "subj"));
1183                 if (seclevel == NULL) {
1184                         if (debug) {
1185                                 auparse_first_record(au);
1186                                 const char *text = auparse_get_record_text(au);
1187                                 fprintf(stderr, "Security context not found "
1188                                                 "for anomaly event: %s\n",
1189                                                 text ? text : "");
1190                         }
1191                         return 0;
1192                 }
1193                 machine_id = get_machine_id_by_seclevel(seclevel);
1194                 if (machine_id == NULL) {
1195                         if (debug) {
1196                                 fprintf(stderr, "Couldn't get the security "
1197                                         "level from the anomaly event.\n");
1198                         }
1199                         return 0;
1200                 }
1201
1202                 for (it = events->tail; it; it = it->next) {
1203                         struct event *event = it->data;
1204                         if (event->success && machine_id->uuid && event->uuid &&
1205                             strcmp(machine_id->uuid, event->uuid) == 0) {
1206                                 if (event->type == ET_STOP) {
1207                                         break;
1208                                 } else if (event->type == ET_START) {
1209                                         if (event->end == 0)
1210                                                 start = event;
1211                                         break;
1212                                 }
1213                         }
1214                 }
1215         }
1216
1217         if (start == NULL) {
1218                 if (debug) {
1219                         const char *text = auparse_get_record_text(au);
1220                         fprintf(stderr, "Guest not found for "
1221                                         "anomaly record: %s.\n",
1222                                         text ? text : "");
1223                 }
1224                 return 0;
1225         }
1226
1227         if (extract_virt_fields(au, NULL, &uid, &time, NULL, NULL))
1228                 return 0;
1229
1230         anom = event_alloc();
1231         if (anom == NULL)
1232                 return 1;
1233         anom->type = ET_ANOM;
1234         anom->uuid = copy_str(start->uuid);
1235         anom->name = copy_str(start->name);
1236         anom->uid = uid;
1237         anom->start = time;
1238         anom->pid = pid;
1239         memcpy(anom->proof, start->proof, sizeof(anom->proof));
1240         add_proof(anom, au);
1241         if (list_append(events, anom) == NULL) {
1242                 event_free(anom);
1243                 return 1;
1244         }
1245         return 0;
1246 }
1247
1248 int process_shutdown(auparse_state_t *au)
1249 {
1250         uid_t uid = -1;
1251         time_t time = 0;
1252         struct event *down;
1253         list_node_t *it;
1254         int success = 0;
1255
1256         if (extract_virt_fields(au, NULL, &uid, &time, NULL, &success))
1257                 return 0;
1258
1259         for (it = events->tail; it; it = it->prev) {
1260                 struct event *event = it->data;
1261                 if (event->success) {
1262                         if (event->type == ET_START || event->type == ET_RES) {
1263                                 if (event->end == 0) {
1264                                         event->end = time;
1265                                         add_proof(event, au);
1266                                 }
1267                         } else if (event->type == ET_DOWN) {
1268                                 break;
1269                         }
1270                 }
1271         }
1272
1273         down = event_alloc();
1274         if (down == NULL)
1275                 return 1;
1276         down->type = ET_DOWN;
1277         down->uid = uid;
1278         down->start = time;
1279         down->success = success;
1280         add_proof(down, au);
1281         if (list_append(events, down) == NULL) {
1282                 event_free(down);
1283                 return 1;
1284         }
1285         return 0;
1286 }
1287
1288 /* Convert record type to a string */
1289 const char *get_rec_type(struct event *e)
1290 {
1291         static char buf[64];
1292         if (e == NULL)
1293                 return "";
1294
1295         switch (e->type) {
1296         case ET_START:
1297                 return "start";
1298         case ET_STOP:
1299                 return "stop";
1300         case ET_RES:
1301                 return "res";
1302         case ET_AVC:
1303                 return "avc";
1304         case ET_ANOM:
1305                 return "anom";
1306         case ET_DOWN:
1307                 return "down";
1308         }
1309
1310         snprintf(buf, sizeof(buf), "%d", e->type);
1311         return buf;
1312 }
1313
1314 /* Convert uid to a string */
1315 const char *get_username(struct event *e)
1316 {
1317         static char s[256];
1318         if (!e || (int)e->uid == -1) {
1319                 s[0] = '?';
1320                 s[1] = '\0';
1321         } else {
1322                 struct passwd *passwd = getpwuid(e->uid);
1323                 if (passwd == NULL || passwd->pw_name == NULL) {
1324                         snprintf(s, sizeof(s), "%d", e->uid);
1325                 } else {
1326                         snprintf(s, sizeof(s), "%s", passwd->pw_name);
1327                 }
1328         }
1329         return s;
1330 }
1331
1332 /* Convert a time period to string */
1333 const char *get_time_period(struct event *event)
1334 {
1335         size_t i = 0;
1336         static char buf[128];
1337
1338         i += sprintf(buf + i, "%-16.16s", ctime(&event->start));
1339         if (event->end) {
1340                 time_t secs = event->end - event->start;
1341                 int mins, hours, days;
1342                 i += sprintf(buf + i, " - %-7.5s", ctime(&event->end) + 11);
1343                 mins  = (secs / 60) % 60;
1344                 hours = (secs / 3600) % 24;
1345                 days  = secs / 86400;
1346                 if (days) {
1347                         i += sprintf(buf + i, "(%d+%02d:%02d)", days, hours,
1348                                         mins);
1349                 } else {
1350                         i += sprintf(buf + i, "(%02d:%02d)", hours, mins);
1351                 }
1352         } else {
1353                 if (!event->success &&
1354                     event->type != ET_AVC &&
1355                     event->type != ET_ANOM) {
1356                         i += sprintf(buf + i, " - failed");
1357                 }
1358         }
1359         return buf;
1360 }
1361
1362 void print_event(struct event *event)
1363 {
1364         /* Auxiliary macro to convert NULL to "" */
1365         #define N(str) ((str) ? str : "")
1366
1367         /* machine id records are used just to get information about
1368          * the guests. */
1369         if (event->type == ET_MACHINE_ID)
1370                 return;
1371         /* If "--all-events" is not given, only the start event is shown. */
1372         if (!all_events_flag && event->type != ET_START)
1373                 return;
1374         /* The type of event is shown only when all records are shown */
1375         if (all_events_flag)
1376                 printf("%-5.5s ", get_rec_type(event));
1377
1378         /* Print common fields */
1379         printf("%-25.25s", N(event->name));
1380         if (uuid_flag)
1381                 printf("\t%-36.36s", N(event->uuid));
1382         printf("\t%-11.11s\t%-35.35s", get_username(event),
1383                         get_time_period(event));
1384
1385         /* Print type specific fields */
1386         if (event->type == ET_RES) {
1387                 printf("\t%-12.12s", N(event->res_type));
1388                 printf("\t%-10.10s", N(event->reason));
1389                 if (strcmp("cgroup", event->res_type) != 0) {
1390                         printf("\t%s", N(event->res));
1391                 } else {
1392                         printf("\t%s\t%s\t%s", N(event->cgroup_class),
1393                                         N(event->cgroup_acl),
1394                                         N(event->cgroup_detail));
1395                 }
1396         } else if (event->type == ET_MACHINE_ID) {
1397                 printf("\t%s", N(event->seclevel));
1398         } else if (event->type == ET_AVC) {
1399                 printf("\t%-12.12s", N(event->avc_operation));
1400                 printf("\t%-10.10s", N(event->avc_result));
1401                 printf("\t%s\t%s\t%s", N(event->comm), N(event->target),
1402                                 N(event->context));
1403         }
1404         printf("\n");
1405
1406         /* Print proof */
1407         if (proof_flag) {
1408                 int first = 1;
1409                 int i, len = sizeof(event->proof)/sizeof(event->proof[0]);
1410                 printf("    Proof:");
1411                 for (i = 0; i < len; i++) {
1412                         if (event->proof[i].time) {
1413                                 printf("%s %ld.%03u:%lu",
1414                                         (first) ? "" : ",",
1415                                         event->proof[i].time,
1416                                         event->proof[i].milli,
1417                                         event->proof[i].serial);
1418                                 first = 0;
1419                         }
1420                 }
1421                 printf("\n\n");
1422         }
1423 }
1424
1425 /* Print all events */
1426 void print_events(void)
1427 {
1428         list_node_t *it;
1429         for (it = events->head; it; it = it->next) {
1430                 struct event *event = it->data;
1431                 if (event)
1432                         print_event(event);
1433         }
1434 }
1435
1436 /* Count and print summary */
1437 void print_summary(void)
1438 {
1439         /* Summary numbers */
1440         time_t start_time = 0, end_time = 0;
1441         long start = 0, stop = 0, res = 0, avc = 0, anom = 0,
1442              shutdown = 0, failure = 0;
1443         char start_buf[32], end_buf[32];
1444
1445         /* Calculate summary */
1446         list_node_t *it;
1447         for (it = events->head; it; it = it->next) {
1448                 struct event *event = it->data;
1449                 if (event->success == 0 &&
1450                     (event->type == ET_START ||
1451                      event->type == ET_STOP  ||
1452                      event->type == ET_RES)) {
1453                         failure++;
1454                 } else {
1455                         switch (event->type) {
1456                         case ET_START:
1457                                 start++;
1458                                 break;
1459                         case ET_STOP:
1460                                 stop++;
1461                                 break;
1462                         case ET_RES:
1463                                 res++;
1464                                 break;
1465                         case ET_AVC:
1466                                 avc++;
1467                                 break;
1468                         case ET_ANOM:
1469                                 anom++;
1470                                 break;
1471                         case ET_DOWN:
1472                                 shutdown++;
1473                                 break;
1474                         }
1475                 }
1476
1477                 /* Calculate time range */
1478                 if (event->start) {
1479                         if (start_time == 0 || event->start < start_time) {
1480                                 start_time = event->start;
1481                         }
1482                         if (end_time == 0 || event->start > end_time) {
1483                                 end_time = event->start;
1484                         }
1485                 }
1486                 if (event->end) {
1487                         if (start_time == 0 || event->end < start_time) {
1488                                 start_time = event->end;
1489                         }
1490                         if (end_time == 0 || event->end > end_time) {
1491                                 end_time = event->end;
1492                         }
1493                 }
1494
1495         }
1496
1497         if (start_time)
1498                 ctime_r(&start_time, start_buf);
1499         else
1500                 strcpy(start_buf, "undef");
1501         if (end_time)
1502                 ctime_r(&end_time, end_buf);
1503         else
1504                 strcpy(end_buf, "undef");
1505
1506         /* Print summary */
1507         printf("Range of time for report:       %-.16s - %-.16s\n",
1508                         start_buf, end_buf);
1509         printf("Number of guest starts:         %ld\n", start);
1510         printf("Number of guest stops:          %ld\n", stop);
1511         printf("Number of resource assignments: %ld\n", res);
1512         printf("Number of related AVCs:         %ld\n", avc);
1513         printf("Number of related anomalies:    %ld\n", anom);
1514         printf("Number of host shutdowns:       %ld\n", shutdown);
1515         printf("Number of failed operations:    %ld\n", failure);
1516 }
1517
1518 int main(int argc, char **argv)
1519 {
1520         int rc = 0;
1521         auparse_state_t *au = NULL;
1522
1523         setlocale(LC_ALL, "");
1524         if (parse_args(argc, argv))
1525                 goto error;
1526         if (help_flag) {
1527                 usage(stdout);
1528                 goto exit;
1529         }
1530
1531         /* Initialize event list*/
1532         events = list_new((list_free_data_fn*) event_free);
1533         if (events == NULL)
1534                 goto unexpected_error;
1535
1536         /* Initialize auparse */
1537         au = init_auparse();
1538         if (au == NULL)
1539                 goto error;
1540         if (create_search_criteria(au))
1541                 goto error;
1542
1543         while (ausearch_next_event(au) > 0) {
1544                 int err = 0;
1545
1546                 switch(auparse_get_type(au)) {
1547                 case AUDIT_VIRT_MACHINE_ID:
1548                         err = process_machine_id_event(au);
1549                         break;
1550                 case AUDIT_VIRT_CONTROL:
1551                         err = process_control_event(au);
1552                         break;
1553                 case AUDIT_VIRT_RESOURCE:
1554                         err = process_resource_event(au);
1555                         break;
1556                 case AUDIT_AVC:
1557                         err = process_avc(au);
1558                         break;
1559                 case AUDIT_FIRST_ANOM_MSG ... AUDIT_LAST_ANOM_MSG:
1560                 case AUDIT_FIRST_KERN_ANOM_MSG ... AUDIT_LAST_KERN_ANOM_MSG:
1561                         err = process_anom(au);
1562                         break;
1563                 case AUDIT_SYSTEM_SHUTDOWN:
1564                         err = process_shutdown(au);
1565                         break;
1566                 }
1567                 if (err) {
1568                         goto unexpected_error;
1569                 }
1570                 auparse_next_event(au);
1571         }
1572
1573         /* Show results */
1574         if (summary_flag) {
1575                 print_summary();
1576         } else {
1577                 print_events();
1578         }
1579
1580         /* success */
1581         goto exit;
1582
1583 unexpected_error:
1584         fprintf(stderr, "Unexpected error\n");
1585 error:
1586         rc = 1;
1587 exit:
1588         if (au)
1589                 auparse_destroy(au);
1590         list_free(events);
1591         if (debug)
1592                 fprintf(stdout, "Exit code: %d\n", rc);
1593         return rc;
1594 }
1595