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(©[i - 1], ©[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