xref: /freebsd/contrib/openbsm/bin/auditreduce/auditreduce.c (revision 1e413cf93298b5b97441a21d9a50fdcd0ee9945e)
1 /*
2  * Copyright (c) 2004 Apple Computer, Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1.  Redistributions of source code must retain the above copyright
9  *     notice, this list of conditions and the following disclaimer.
10  * 2.  Redistributions in binary form must reproduce the above copyright
11  *     notice, this list of conditions and the following disclaimer in the
12  *     documentation and/or other materials provided with the distribution.
13  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14  *     its contributors may be used to endorse or promote products derived
15  *     from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR
21  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
25  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
26  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27  * POSSIBILITY OF SUCH DAMAGE.
28  *
29  * $P4: //depot/projects/trustedbsd/openbsm/bin/auditreduce/auditreduce.c#20 $
30  */
31 
32 /*
33  * Tool used to merge and select audit records from audit trail files
34  */
35 
36 /*
37  * XXX Currently we do not support merging of records from multiple
38  * XXX audit trail files
39  * XXX We assume that records are sorted chronologically - both wrt to
40  * XXX the records present within the file and between the files themselves
41  */
42 
43 #include <config/config.h>
44 #ifdef HAVE_FULL_QUEUE_H
45 #include <sys/queue.h>
46 #else
47 #include <compat/queue.h>
48 #endif
49 
50 #include <bsm/libbsm.h>
51 
52 #include <err.h>
53 #include <grp.h>
54 #include <pwd.h>
55 #include <stdio.h>
56 #include <stdlib.h>
57 #include <sysexits.h>
58 #include <string.h>
59 #include <time.h>
60 #include <unistd.h>
61 #include <regex.h>
62 #include <errno.h>
63 
64 #include "auditreduce.h"
65 
66 static TAILQ_HEAD(tailhead, re_entry) re_head =
67     TAILQ_HEAD_INITIALIZER(re_head);
68 
69 extern char		*optarg;
70 extern int		 optind, optopt, opterr,optreset;
71 
72 static au_mask_t	 maskp;		/* Class. */
73 static time_t		 p_atime;	/* Created after this time. */
74 static time_t		 p_btime;	/* Created before this time. */
75 static uint16_t		 p_evtype;	/* Event that we are searching for. */
76 static int		 p_auid;	/* Audit id. */
77 static int		 p_euid;	/* Effective user id. */
78 static int		 p_egid;	/* Effective group id. */
79 static int		 p_rgid;	/* Real group id. */
80 static int		 p_ruid;	/* Real user id. */
81 static int		 p_subid;	/* Subject id. */
82 
83 /*
84  * Following are the objects (-o option) that we can select upon.
85  */
86 static char	*p_fileobj = NULL;
87 static char	*p_msgqobj = NULL;
88 static char	*p_pidobj = NULL;
89 static char	*p_semobj = NULL;
90 static char	*p_shmobj = NULL;
91 static char	*p_sockobj = NULL;
92 
93 static uint32_t opttochk = 0;
94 
95 static void
96 parse_regexp(char *re_string)
97 {
98 	char *orig, *copy, re_error[64];
99 	struct re_entry *rep;
100 	int error, nstrs, i, len;
101 
102 	copy = strdup(re_string);
103 	orig = copy;
104 	len = strlen(copy);
105 	for (nstrs = 0, i = 0; i < len; i++) {
106 		if (copy[i] == ',' && i > 0) {
107 			if (copy[i - 1] == '\\')
108 				strcpy(&copy[i - 1], &copy[i]);
109 			else {
110 				nstrs++;
111 				copy[i] = '\0';
112 			}
113 		}
114 	}
115 	TAILQ_INIT(&re_head);
116 	for (i = 0; i < nstrs + 1; i++) {
117 		rep = calloc(1, sizeof(*rep));
118 		if (rep == NULL) {
119 			(void) fprintf(stderr, "calloc: %s\n",
120 			    strerror(errno));
121 			exit(1);
122 		}
123 		if (*copy == '~') {
124 			copy++;
125 			rep->re_negate = 1;
126 		}
127 		rep->re_pattern = strdup(copy);
128 		error = regcomp(&rep->re_regexp, rep->re_pattern,
129 		    REG_EXTENDED | REG_NOSUB);
130 		if (error != 0) {
131 			regerror(error, &rep->re_regexp, re_error, 64);
132 			(void) fprintf(stderr, "regcomp: %s\n", re_error);
133 			exit(1);
134 		}
135 		TAILQ_INSERT_TAIL(&re_head, rep, re_glue);
136 		len = strlen(copy);
137 		copy += len + 1;
138 	}
139 	free(orig);
140 }
141 
142 static void
143 usage(const char *msg)
144 {
145 	fprintf(stderr, "%s\n", msg);
146 	fprintf(stderr, "Usage: auditreduce [options] [file ...]\n");
147 	fprintf(stderr, "\tOptions are : \n");
148 	fprintf(stderr, "\t-A : all records\n");
149 	fprintf(stderr, "\t-a YYYYMMDD[HH[[MM[SS]]] : after date\n");
150 	fprintf(stderr, "\t-b YYYYMMDD[HH[[MM[SS]]] : before date\n");
151 	fprintf(stderr, "\t-c <flags> : matching class\n");
152 	fprintf(stderr, "\t-d YYYYMMDD : on date\n");
153 	fprintf(stderr, "\t-e <uid|name>  : effective user\n");
154 	fprintf(stderr, "\t-f <gid|group> : effective group\n");
155 	fprintf(stderr, "\t-g <gid|group> : real group\n");
156 	fprintf(stderr, "\t-j <pid> : subject id \n");
157 	fprintf(stderr, "\t-m <evno|evname> : matching event\n");
158 	fprintf(stderr, "\t-o objecttype=objectvalue\n");
159 	fprintf(stderr, "\t\t file=<pathname>\n");
160 	fprintf(stderr, "\t\t msgqid=<ID>\n");
161 	fprintf(stderr, "\t\t pid=<ID>\n");
162 	fprintf(stderr, "\t\t semid=<ID>\n");
163 	fprintf(stderr, "\t\t shmid=<ID>\n");
164 	fprintf(stderr, "\t-r <uid|name> : real user\n");
165 	fprintf(stderr, "\t-u <uid|name> : audit user\n");
166 	exit(EX_USAGE);
167 }
168 
169 /*
170  * Check if the given auid matches the selection criteria.
171  */
172 static int
173 select_auid(int au)
174 {
175 
176 	/* Check if we want to select on auid. */
177 	if (ISOPTSET(opttochk, OPT_u)) {
178 		if (au != p_auid)
179 			return (0);
180 	}
181 	return (1);
182 }
183 
184 /*
185  * Check if the given euid matches the selection criteria.
186  */
187 static int
188 select_euid(int euser)
189 {
190 
191 	/* Check if we want to select on euid. */
192 	if (ISOPTSET(opttochk, OPT_e)) {
193 		if (euser != p_euid)
194 			return (0);
195 	}
196 	return (1);
197 }
198 
199 /*
200  * Check if the given egid matches the selection criteria.
201  */
202 static int
203 select_egid(int egrp)
204 {
205 
206 	/* Check if we want to select on egid. */
207 	if (ISOPTSET(opttochk, OPT_f)) {
208 		if (egrp != p_egid)
209 			return (0);
210 	}
211 	return (1);
212 }
213 
214 /*
215  * Check if the given rgid matches the selection criteria.
216  */
217 static int
218 select_rgid(int grp)
219 {
220 
221 	/* Check if we want to select on rgid. */
222 	if (ISOPTSET(opttochk, OPT_g)) {
223 		if (grp != p_rgid)
224 			return (0);
225 	}
226 	return (1);
227 }
228 
229 /*
230  * Check if the given ruid matches the selection criteria.
231  */
232 static int
233 select_ruid(int user)
234 {
235 
236 	/* Check if we want to select on rgid. */
237 	if (ISOPTSET(opttochk, OPT_r)) {
238 		if (user != p_ruid)
239 			return (0);
240 	}
241 	return (1);
242 }
243 
244 /*
245  * Check if the given subject id (pid) matches the selection criteria.
246  */
247 static int
248 select_subid(int subid)
249 {
250 
251 	/* Check if we want to select on subject uid. */
252 	if (ISOPTSET(opttochk, OPT_j)) {
253 		if (subid != p_subid)
254 			return (0);
255 	}
256 	return (1);
257 }
258 
259 
260 /*
261  * Check if object's pid maches the given pid.
262  */
263 static int
264 select_pidobj(uint32_t pid)
265 {
266 
267 	if (ISOPTSET(opttochk, OPT_op)) {
268 		if (pid != strtol(p_pidobj, (char **)NULL, 10))
269 			return (0);
270 	}
271 	return (1);
272 }
273 
274 /*
275  * Check if the given ipc object with the given type matches the selection
276  * criteria.
277  */
278 static int
279 select_ipcobj(u_char type, uint32_t id, uint32_t *optchkd)
280 {
281 
282 	if (type == AT_IPC_MSG) {
283 		SETOPT((*optchkd), OPT_om);
284 		if (ISOPTSET(opttochk, OPT_om)) {
285 			if (id != strtol(p_msgqobj, (char **)NULL, 10))
286 				return (0);
287 		}
288 		return (1);
289 	} else if (type == AT_IPC_SEM) {
290 		SETOPT((*optchkd), OPT_ose);
291 		if (ISOPTSET(opttochk, OPT_ose)) {
292 			if (id != strtol(p_semobj, (char **)NULL, 10))
293 				return (0);
294 		}
295 		return (1);
296 	} else if (type == AT_IPC_SHM) {
297 		SETOPT((*optchkd), OPT_osh);
298 		if (ISOPTSET(opttochk, OPT_osh)) {
299 			if (id != strtol(p_shmobj, (char **)NULL, 10))
300 				return (0);
301 		}
302 		return (1);
303 	}
304 
305 	/* Unknown type -- filter if *any* ipc filtering is required. */
306 	if (ISOPTSET(opttochk, OPT_om) || ISOPTSET(opttochk, OPT_ose)
307 	    || ISOPTSET(opttochk, OPT_osh))
308 		return (0);
309 
310 	return (1);
311 }
312 
313 
314 /*
315  * Check if the file name matches selection criteria.
316  */
317 static int
318 select_filepath(char *path, uint32_t *optchkd)
319 {
320 	struct re_entry *rep;
321 	int match;
322 
323 	SETOPT((*optchkd), OPT_of);
324 	match = 1;
325 	if (ISOPTSET(opttochk, OPT_of)) {
326 		match = 0;
327 		TAILQ_FOREACH(rep, &re_head, re_glue) {
328 			if (regexec(&rep->re_regexp, path, 0, NULL,
329 			    0) != REG_NOMATCH)
330 				return (!rep->re_negate);
331 		}
332 	}
333 	return (match);
334 }
335 
336 /*
337  * Returns 1 if the following pass the selection rules:
338  *
339  * before-time,
340  * after time,
341  * date,
342  * class,
343  * event
344  */
345 static int
346 select_hdr32(tokenstr_t tok, uint32_t *optchkd)
347 {
348 
349 	SETOPT((*optchkd), (OPT_A | OPT_a | OPT_b | OPT_c | OPT_m));
350 
351 	/* The A option overrides a, b and d. */
352 	if (!ISOPTSET(opttochk, OPT_A)) {
353 		if (ISOPTSET(opttochk, OPT_a)) {
354 			if (difftime((time_t)tok.tt.hdr32.s, p_atime) < 0) {
355 				/* Record was created before p_atime. */
356 				return (0);
357 			}
358 		}
359 
360 		if (ISOPTSET(opttochk, OPT_b)) {
361 			if (difftime(p_btime, (time_t)tok.tt.hdr32.s) < 0) {
362 				/* Record was created after p_btime. */
363 				return (0);
364 			}
365 		}
366 	}
367 
368 	if (ISOPTSET(opttochk, OPT_c)) {
369 		/*
370 		 * Check if the classes represented by the event matches
371 		 * given class.
372 		 */
373 		if (au_preselect(tok.tt.hdr32.e_type, &maskp, AU_PRS_BOTH,
374 		    AU_PRS_USECACHE) != 1)
375 			return (0);
376 	}
377 
378 	/* Check if event matches. */
379 	if (ISOPTSET(opttochk, OPT_m)) {
380 		if (tok.tt.hdr32.e_type != p_evtype)
381 			return (0);
382 	}
383 
384 	return (1);
385 }
386 
387 static int
388 select_return32(tokenstr_t tok_ret32, tokenstr_t tok_hdr32, uint32_t *optchkd)
389 {
390 	int sorf;
391 
392 	SETOPT((*optchkd), (OPT_c));
393 	if (tok_ret32.tt.ret32.status == 0)
394 		sorf = AU_PRS_SUCCESS;
395 	else
396 		sorf = AU_PRS_FAILURE;
397 	if (ISOPTSET(opttochk, OPT_c)) {
398 		if (au_preselect(tok_hdr32.tt.hdr32.e_type, &maskp, sorf,
399 		    AU_PRS_USECACHE) != 1)
400 			return (0);
401 	}
402 	return (1);
403 }
404 
405 /*
406  * Return 1 if checks for the the following succeed
407  * auid,
408  * euid,
409  * egid,
410  * rgid,
411  * ruid,
412  * process id
413  */
414 static int
415 select_proc32(tokenstr_t tok, uint32_t *optchkd)
416 {
417 
418 	SETOPT((*optchkd), (OPT_u | OPT_e | OPT_f | OPT_g | OPT_r | OPT_op));
419 
420 	if (!select_auid(tok.tt.proc32.auid))
421 		return (0);
422 	if (!select_euid(tok.tt.proc32.euid))
423 		return (0);
424 	if (!select_egid(tok.tt.proc32.egid))
425 		return (0);
426 	if (!select_rgid(tok.tt.proc32.rgid))
427 		return (0);
428 	if (!select_ruid(tok.tt.proc32.ruid))
429 		return (0);
430 	if (!select_pidobj(tok.tt.proc32.pid))
431 		return (0);
432 	return (1);
433 }
434 
435 /*
436  * Return 1 if checks for the the following succeed
437  * auid,
438  * euid,
439  * egid,
440  * rgid,
441  * ruid,
442  * subject id
443  */
444 static int
445 select_subj32(tokenstr_t tok, uint32_t *optchkd)
446 {
447 
448 	SETOPT((*optchkd), (OPT_u | OPT_e | OPT_f | OPT_g | OPT_r | OPT_j));
449 
450 	if (!select_auid(tok.tt.subj32.auid))
451 		return (0);
452 	if (!select_euid(tok.tt.subj32.euid))
453 		return (0);
454 	if (!select_egid(tok.tt.subj32.egid))
455 		return (0);
456 	if (!select_rgid(tok.tt.subj32.rgid))
457 		return (0);
458 	if (!select_ruid(tok.tt.subj32.ruid))
459 		return (0);
460 	if (!select_subid(tok.tt.subj32.pid))
461 		return (0);
462 	return (1);
463 }
464 
465 /*
466  * Read each record from the audit trail.  Check if it is selected after
467  * passing through each of the options
468  */
469 static int
470 select_records(FILE *fp)
471 {
472 	tokenstr_t tok_hdr32_copy;
473 	u_char *buf;
474 	tokenstr_t tok;
475 	int reclen;
476 	int bytesread;
477 	int selected;
478 	uint32_t optchkd;
479 
480 	int err = 0;
481 	while ((reclen = au_read_rec(fp, &buf)) != -1) {
482 		optchkd = 0;
483 		bytesread = 0;
484 		selected = 1;
485 		while ((selected == 1) && (bytesread < reclen)) {
486 			if (-1 == au_fetch_tok(&tok, buf + bytesread,
487 			    reclen - bytesread)) {
488 				/* Is this an incomplete record? */
489 				err = 1;
490 				break;
491 			}
492 
493 			/*
494 			 * For each token type we have have different
495 			 * selection criteria.
496 			 */
497 			switch(tok.id) {
498 			case AU_HEADER_32_TOKEN:
499 					selected = select_hdr32(tok,
500 					    &optchkd);
501 					bcopy(&tok, &tok_hdr32_copy,
502 					    sizeof(tok));
503 					break;
504 
505 			case AU_PROCESS_32_TOKEN:
506 					selected = select_proc32(tok,
507 					    &optchkd);
508 					break;
509 
510 			case AU_SUBJECT_32_TOKEN:
511 					selected = select_subj32(tok,
512 					    &optchkd);
513 					break;
514 
515 			case AU_IPC_TOKEN:
516 					selected = select_ipcobj(
517 					    tok.tt.ipc.type, tok.tt.ipc.id,
518 					    &optchkd);
519 					break;
520 
521 			case AU_FILE_TOKEN:
522 					selected = select_filepath(
523 					    tok.tt.file.name, &optchkd);
524 					break;
525 
526 			case AU_PATH_TOKEN:
527 					selected = select_filepath(
528 					    tok.tt.path.path, &optchkd);
529 					break;
530 
531 			case AU_RETURN_32_TOKEN:
532 				selected = select_return32(tok,
533 				    tok_hdr32_copy, &optchkd);
534 				break;
535 
536 			/*
537 			 * The following tokens dont have any relevant
538 			 * attributes that we can select upon.
539 			 */
540 			case AU_TRAILER_TOKEN:
541 			case AU_ARG32_TOKEN:
542 			case AU_ATTR32_TOKEN:
543 			case AU_EXIT_TOKEN:
544 			case AU_NEWGROUPS_TOKEN:
545 			case AU_IN_ADDR_TOKEN:
546 			case AU_IP_TOKEN:
547 			case AU_IPCPERM_TOKEN:
548 			case AU_IPORT_TOKEN:
549 			case AU_OPAQUE_TOKEN:
550 			case AU_SEQ_TOKEN:
551 			case AU_TEXT_TOKEN:
552 			case AU_ARB_TOKEN:
553 			case AU_SOCK_TOKEN:
554 			default:
555 				break;
556 			}
557 			bytesread += tok.len;
558 		}
559 		if ((selected == 1) && (!err)) {
560 			/* Check if all the options were matched. */
561 			if (!(opttochk & ~optchkd)) {
562 				/* XXX Write this record to the output file. */
563 				/* default to stdout */
564 				fwrite(buf, 1, reclen, stdout);
565 			}
566 		}
567 		free(buf);
568 	}
569 	return (0);
570 }
571 
572 /*
573  * The -o option has the form object_type=object_value.  Identify the object
574  * components.
575  */
576 void
577 parse_object_type(char *name, char *val)
578 {
579 	if (val == NULL)
580 		return;
581 
582 	if (!strcmp(name, FILEOBJ)) {
583 		p_fileobj = val;
584 		parse_regexp(val);
585 		SETOPT(opttochk, OPT_of);
586 	} else if (!strcmp(name, MSGQIDOBJ)) {
587 		p_msgqobj = val;
588 		SETOPT(opttochk, OPT_om);
589 	} else if (!strcmp(name, PIDOBJ)) {
590 		p_pidobj = val;
591 		SETOPT(opttochk, OPT_op);
592 	} else if (!strcmp(name, SEMIDOBJ)) {
593 		p_semobj = val;
594 		SETOPT(opttochk, OPT_ose);
595 	} else if (!strcmp(name, SHMIDOBJ)) {
596 		p_shmobj = val;
597 		SETOPT(opttochk, OPT_osh);
598 	} else if (!strcmp(name, SOCKOBJ)) {
599 		p_sockobj = val;
600 		SETOPT(opttochk, OPT_oso);
601 	} else
602 		usage("unknown value for -o");
603 }
604 
605 int
606 main(int argc, char **argv)
607 {
608 	struct group *grp;
609 	struct passwd *pw;
610 	struct tm tm;
611 	au_event_t *n;
612 	FILE *fp;
613 	int i;
614 	char *objval, *converr;
615 	int ch;
616 	char timestr[128];
617 	char *fname;
618 
619 	converr = NULL;
620 
621 	while ((ch = getopt(argc, argv, "Aa:b:c:d:e:f:g:j:m:o:r:u:")) != -1) {
622 		switch(ch) {
623 		case 'A':
624 			SETOPT(opttochk, OPT_A);
625 			break;
626 
627 		case 'a':
628 			if (ISOPTSET(opttochk, OPT_a)) {
629 				usage("d is exclusive with a and b");
630 			}
631 			SETOPT(opttochk, OPT_a);
632 			bzero(&tm, sizeof(tm));
633 			strptime(optarg, "%Y%m%d%H%M%S", &tm);
634 			strftime(timestr, sizeof(timestr), "%Y%m%d%H%M%S",
635 			    &tm);
636 			/* fprintf(stderr, "Time converted = %s\n", timestr); */
637 			p_atime = mktime(&tm);
638 			break;
639 
640 		case 'b':
641 			if (ISOPTSET(opttochk, OPT_b)) {
642 				usage("d is exclusive with a and b");
643 			}
644 			SETOPT(opttochk, OPT_b);
645 			bzero(&tm, sizeof(tm));
646 			strptime(optarg, "%Y%m%d%H%M%S", &tm);
647 			strftime(timestr, sizeof(timestr), "%Y%m%d%H%M%S",
648 			    &tm);
649 			/* fprintf(stderr, "Time converted = %s\n", timestr); */
650 			p_btime = mktime(&tm);
651 			break;
652 
653 		case 'c':
654 			if (0 != getauditflagsbin(optarg, &maskp)) {
655 				/* Incorrect class */
656 				usage("Incorrect class");
657 			}
658 			SETOPT(opttochk, OPT_c);
659 			break;
660 
661 		case 'd':
662 			if (ISOPTSET(opttochk, OPT_b) || ISOPTSET(opttochk,
663 			    OPT_a))
664 				usage("'d' is exclusive with 'a' and 'b'");
665 			SETOPT(opttochk, OPT_d);
666 			bzero(&tm, sizeof(tm));
667 			strptime(optarg, "%Y%m%d", &tm);
668 			strftime(timestr, sizeof(timestr), "%Y%m%d", &tm);
669 			/* fprintf(stderr, "Time converted = %s\n", timestr); */
670 			p_atime = mktime(&tm);
671 			tm.tm_hour = 23;
672 			tm.tm_min = 59;
673 			tm.tm_sec = 59;
674 			strftime(timestr, sizeof(timestr), "%Y%m%d", &tm);
675 			/* fprintf(stderr, "Time converted = %s\n", timestr); */
676 			p_btime = mktime(&tm);
677 			break;
678 
679 		case 'e':
680 			p_euid = strtol(optarg, &converr, 10);
681 			if (*converr != '\0') {
682 				/* Try the actual name */
683 				if ((pw = getpwnam(optarg)) == NULL)
684 					break;
685 				p_euid = pw->pw_uid;
686 			}
687 			SETOPT(opttochk, OPT_e);
688 			break;
689 
690 		case 'f':
691 			p_egid = strtol(optarg, &converr, 10);
692 			if (*converr != '\0') {
693 				/* Try actual group name. */
694 				if ((grp = getgrnam(optarg)) == NULL)
695 					break;
696 				p_egid = grp->gr_gid;
697 			}
698 			SETOPT(opttochk, OPT_f);
699 			break;
700 
701 		case 'g':
702 			p_rgid = strtol(optarg, &converr, 10);
703 			if (*converr != '\0') {
704 				/* Try actual group name. */
705 				if ((grp = getgrnam(optarg)) == NULL)
706 					break;
707 				p_rgid = grp->gr_gid;
708 			}
709 			SETOPT(opttochk, OPT_g);
710 			break;
711 
712 		case 'j':
713 			p_subid = strtol(optarg, (char **)NULL, 10);
714 			SETOPT(opttochk, OPT_j);
715 			break;
716 
717 		case 'm':
718 			p_evtype = strtol(optarg, (char **)NULL, 10);
719 			if (p_evtype == 0) {
720 				/* Could be the string representation. */
721 				n = getauevnonam(optarg);
722 				if (n == NULL)
723 					usage("Incorrect event name");
724 				p_evtype = *n;
725 			}
726 			SETOPT(opttochk, OPT_m);
727 			break;
728 
729 		case 'o':
730 			objval = strchr(optarg, '=');
731 			if (objval != NULL) {
732 				*objval = '\0';
733 				objval += 1;
734 				parse_object_type(optarg, objval);
735 			}
736 			break;
737 
738 		case 'r':
739 			p_ruid = strtol(optarg, &converr, 10);
740 			if (*converr != '\0') {
741 				if ((pw = getpwnam(optarg)) == NULL)
742 					break;
743 				p_ruid = pw->pw_uid;
744 			}
745 			SETOPT(opttochk, OPT_r);
746 			break;
747 
748 		case 'u':
749 			p_auid = strtol(optarg, &converr, 10);
750 			if (*converr != '\0') {
751 				if ((pw = getpwnam(optarg)) == NULL)
752 					break;
753 				p_auid = pw->pw_uid;
754 			}
755 			SETOPT(opttochk, OPT_u);
756 			break;
757 
758 		case '?':
759 		default:
760 			usage("Unknown option");
761 		}
762 	}
763 	argv += optind;
764 	argc -= optind;
765 
766 	if (argc == 0) {
767 		if (select_records(stdin) == -1)
768 			errx(EXIT_FAILURE,
769 			    "Couldn't select records from stdin");
770 		exit(EXIT_SUCCESS);
771 	}
772 
773 	/*
774 	 * XXX: We should actually be merging records here.
775 	 */
776 	for (i = 0; i < argc; i++) {
777 		fname = argv[i];
778 		fp = fopen(fname, "r");
779 		if (fp == NULL)
780 			errx(EXIT_FAILURE, "Couldn't open %s", fname);
781 		if (select_records(fp) == -1) {
782 			errx(EXIT_FAILURE, "Couldn't select records %s",
783 			    fname);
784 		}
785 		fclose(fp);
786 	}
787 	exit(EXIT_SUCCESS);
788 }
789