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