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