kill tsol ("Trusted Solaris") aka TX ("Trusted Extensions")
[unleashed.git] / usr / src / cmd / auditreduce / option.c
blobb5cfe042bf3ea3ecc21e6a9ed7b592ac062dae4c
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
19 * CDDL HEADER END
22 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
27 * Command line option processing for auditreduce.
28 * The entry point is process_options(), which is called by main().
29 * Process_options() is the only function visible outside this module.
32 #include <locale.h>
33 #include <sys/zone.h> /* for max zonename length */
34 #include "auditr.h"
37 * Object entry.
38 * Maps object strings specified on the command line to a flag
39 * used when searching by object type.
42 struct obj_ent {
43 char *obj_str; /* string specified on the command line */
44 int obj_flag; /* flag used when searching */
47 typedef struct obj_ent obj_ent_t;
50 * Supports searches by object type.
52 static obj_ent_t obj_tbl[] = {
53 { "file", OBJ_PATH },
54 { "filegroup", OBJ_FGROUP },
55 { "fileowner", OBJ_FOWNER },
56 { "fmri", OBJ_FMRI },
57 { "lp", OBJ_LP },
58 { "msgqid", OBJ_MSG },
59 { "msgqgroup", OBJ_MSGGROUP },
60 { "msgqowner", OBJ_MSGOWNER },
61 { "path", OBJ_PATH },
62 { "pid", OBJ_PROC },
63 { "procgroup", OBJ_PGROUP },
64 { "procowner", OBJ_POWNER },
65 { "semid", OBJ_SEM },
66 { "semgroup", OBJ_SEMGROUP },
67 { "semowner", OBJ_SEMOWNER },
68 { "shmid", OBJ_SHM },
69 { "shmgroup", OBJ_SHMGROUP },
70 { "shmowner", OBJ_SHMOWNER },
71 { "sock", OBJ_SOCK },
72 { "user", OBJ_USER } };
74 extern int derive_date(char *, struct tm *);
75 extern int parse_time(char *, int);
76 extern char *re_comp2(char *);
77 extern time_t tm_to_secs(struct tm *);
79 static int a_isnum(char *, int);
80 static int check_file(audit_fcb_t *, int);
81 static int gather_dir(char *);
82 static audit_pcb_t *get_next_pcb(char *);
83 static obj_ent_t *obj_lkup(char *);
84 static int proc_class(char *);
85 static int proc_date(char *, int);
86 static int proc_file(char *, int);
87 static int process_fileopt(int, char *argv[], int);
88 static int proc_group(char *, gid_t *);
89 static int proc_id(char *, int);
90 static int proc_object(char *);
91 static void proc_pcb(audit_pcb_t *, char *, int);
92 static int proc_subject(char *);
93 static int proc_sid(char *);
94 static int proc_type(char *);
95 static int proc_user(char *, uid_t *);
96 static int proc_zonename(char *);
97 static int proc_fmri(char *);
100 * .func process_options - process command line options.
101 * .desc Process the user's command line options. These are of two types:
102 * single letter flags that are denoted by '-', and filenames. Some
103 * of the flags have arguments. Getopt() is used to get the flags.
104 * When this is done it calls process_fileopt() to handle any filenames
105 * that were there.
106 * .call ret = process_options(argc, argv).
107 * .arg argc - the original value.
108 * .arg argv - the original value.
109 * .ret 0 - no errors detected.
110 * .ret -1 - command line error detected (message already printed).
113 process_options(int argc, char **argv)
115 int opt;
116 int error = FALSE;
117 int error_combo = FALSE;
118 extern int optind; /* in getopt() */
119 extern char *optarg; /* in getopt() - holds arg to flag */
121 static char *options = "ACD:M:NQR:S:VO:"
122 "a:b:c:d:e:g:j:m:o:r:s:t:u:z:";
124 error_str = gettext("general error");
126 zonename = NULL;
128 * Big switch to process the flags.
129 * Start_over: is for handling the '-' for standard input. Getopt()
130 * doesn't recognize it.
132 start_over:
133 while ((opt = getopt(argc, argv, options)) != EOF) {
134 switch (opt) {
135 case 'A': /* all records from the files */
136 f_all = TRUE;
137 break;
138 case 'C': /* process only completed files */
139 f_complete = TRUE;
140 break;
141 case 'D': /* delete the files when done */
142 /* force 'A' 'C' 'O' to be active */
143 f_all = f_complete = TRUE;
144 f_outfile = optarg;
145 f_delete = TRUE;
146 break;
147 case 'M': /* only files from a certain machine */
148 f_machine = optarg;
149 break;
150 case 'N': /* new object selection mode */
151 new_mode = TRUE;
152 break;
153 case 'Q': /* no file error reporting */
154 f_quiet = TRUE;
155 break;
156 case 'R': /* from specified root */
157 f_root = optarg;
158 break;
159 case 'S': /* from specified server */
160 f_server = optarg;
161 break;
162 case 'V': /* list all files as they are opened */
163 f_verbose = TRUE;
164 break;
165 case 'O': /* write to outfile */
166 f_outfile = optarg;
167 break;
168 case 'a': /* after 'date' */
169 case 'b': /* before 'date' */
170 case 'd': /* from 'day' */
171 if (proc_date(optarg, opt))
172 error = TRUE;
173 break;
174 case 'j': /* subject */
175 if (proc_subject(optarg))
176 error = TRUE;
177 break;
178 case 'm': /* message 'type' */
179 if (proc_type(optarg))
180 error = TRUE;
181 break;
182 case 'o': /* object type */
183 if (proc_object(optarg))
184 error = TRUE;
185 break;
186 case 'c': /* message class */
187 if (proc_class(optarg))
188 error = TRUE;
189 break;
190 case 'u': /* form audit user */
191 case 'e': /* form effective user */
192 case 'r': /* form real user */
193 case 'f': /* form effective group */
194 case 'g': /* form real group */
195 if (proc_id(optarg, opt))
196 error = TRUE;
197 break;
198 case 's': /* session ID */
199 if (proc_sid(optarg))
200 error = TRUE;
201 break;
202 case 'z': /* zone name */
203 if (proc_zonename(optarg))
204 error = TRUE;
205 break;
206 case 't': /* termial ID reserved for later */
207 default:
208 return (-1);
210 if (error) {
211 (void) fprintf(stderr,
212 gettext("%s command line error - %s.\n"),
213 ar, error_str);
214 return (-1);
217 /* catch '-' option for stdin processing - getopt() won't see it */
218 if (optind < argc) {
219 if (argv[optind][0] == '-' && argv[optind][1] == '\0') {
220 optind++;
221 f_stdin = TRUE;
222 goto start_over;
226 * Give a default value for 'b' option if not specified.
228 if (m_before == 0)
229 m_before = MAXLONG; /* forever */
231 * Validate combinations of options.
232 * The following are done:
233 * 1. Can't have 'M' or 'S' or 'R' with filenames.
234 * 2. Can't have an after ('a') time after a before ('b') time.
235 * 3. Delete ('D') must have 'C' and 'A' and 'O' with it.
236 * 4. Input from stdin ('-') can't have filenames too.
238 if ((f_machine || f_server || f_root) && (argc != optind)) {
239 error_str = gettext(
240 "no filenames allowed with 'M' or 'S' or 'R' options");
241 error_combo = TRUE;
243 if (m_after >= m_before) {
244 error_str =
245 gettext("'a' parameter must be before 'b' parameter");
246 error_combo = TRUE;
248 if (f_delete &&
249 (!f_complete || !f_all || !f_outfile)) {
250 error_str = gettext(
251 "'C', 'A', and 'O' must be specified with 'D'");
252 error_combo = TRUE;
254 if (f_stdin && (argc != optind)) {
255 error_str = gettext("no filenames allowed with '-' option");
256 error_combo = TRUE;
259 * If error with option combos then print message and exit.
260 * If there was an error with just an option then exit.
262 if (error_combo) {
263 (void) fprintf(stderr,
264 gettext("%s command line error - %s.\n"), ar, error_str);
265 return (-1);
267 if (f_root == NULL)
268 f_root = "/etc/security/audit";
270 * Now handle any filenames included in the command line.
272 return (process_fileopt(argc, argv, optind));
276 proc_subject(char *optarg)
278 if (flags & M_SUBJECT) {
279 error_str = gettext("'j' option specified multiple times");
280 return (-1);
282 flags |= M_SUBJECT;
283 subj_id = atol(optarg);
284 return (0);
288 proc_sid(char *optarg)
290 if (flags & M_SID) {
291 error_str = gettext("'s' option specified multiple times");
292 return (-1);
294 flags |= M_SID;
295 m_sid = (au_asid_t)atol(optarg);
296 return (0);
300 proc_object(char *optarg)
302 char *obj_str;
303 char *obj_val;
304 char *obj_arg;
305 int err;
307 obj_ent_t *oep;
308 struct hostent *he;
310 if (flags & M_OBJECT) {
311 error_str = gettext("'o' option specified multiple times");
312 return (-1);
314 flags |= M_OBJECT;
315 if ((obj_arg = strdup(optarg)) == (char *)0)
316 return (-1);
317 if ((obj_str = strtok(optarg, "=")) == (char *)0 ||
318 (oep = obj_lkup(obj_str)) == (obj_ent_t *)0 ||
319 (obj_val = strtok((char *)0, "=")) == (char *)0) {
320 (void) sprintf(errbuf, gettext("invalid object arg (%s)"),
321 obj_arg);
322 error_str = errbuf;
323 return (-1);
326 obj_flag = oep->obj_flag;
328 switch (obj_flag) {
329 case OBJ_PATH:
330 if ((error_str = re_comp2(obj_val)) != (char *)NULL) {
331 return (-1);
333 return (0);
334 case OBJ_SOCK:
335 if (!a_isnum(obj_val, TRUE)) {
336 obj_id = atol(obj_val);
337 socket_flag = SOCKFLG_PORT;
338 return (0);
340 if (*obj_val == '0') {
341 (void) sscanf(obj_val, "%x", (uint_t *)&obj_id);
342 socket_flag = SOCKFLG_PORT;
343 return (0);
346 he = getipnodebyname((const void *)obj_val, AF_INET6, 0, &err);
347 if (he == 0) {
348 he = getipnodebyname((const void *)obj_val, AF_INET,
349 0, &err);
350 if (he == 0) {
351 (void) sprintf(errbuf,
352 gettext("invalid machine name (%s)"),
353 obj_val);
354 error_str = errbuf;
355 return (-1);
359 if (he->h_addrtype == AF_INET6) {
360 /* LINTED */
361 if (IN6_IS_ADDR_V4MAPPED(
362 (in6_addr_t *)he->h_addr_list[0])) {
363 /* address is IPv4 (32 bits) */
364 (void) memcpy(&obj_id,
365 he->h_addr_list[0] + 12, 4);
366 ip_type = AU_IPv4;
367 } else {
368 (void) memcpy(ip_ipv6, he->h_addr_list[0], 16);
369 ip_type = AU_IPv6;
371 } else {
372 /* address is IPv4 (32 bits) */
373 (void) memcpy(&obj_id, he->h_addr_list[0], 4);
374 ip_type = AU_IPv4;
377 freehostent(he);
378 socket_flag = SOCKFLG_MACHINE;
379 return (0);
380 case OBJ_MSG:
381 case OBJ_SEM:
382 case OBJ_SHM:
383 case OBJ_PROC:
384 obj_id = atol(obj_val);
385 return (0);
386 case OBJ_FGROUP:
387 case OBJ_MSGGROUP:
388 case OBJ_SEMGROUP:
389 case OBJ_SHMGROUP:
390 case OBJ_PGROUP:
391 return (proc_group(obj_val, &obj_group));
392 case OBJ_FOWNER:
393 case OBJ_MSGOWNER:
394 case OBJ_SEMOWNER:
395 case OBJ_SHMOWNER:
396 case OBJ_POWNER:
397 return (proc_user(obj_val, &obj_owner));
398 case OBJ_FMRI:
399 return (proc_fmri(obj_val));
400 case OBJ_USER:
401 return (proc_user(obj_val, &obj_user));
402 case OBJ_LP: /* lp objects have not yet been defined */
403 default: /* impossible */
404 (void) sprintf(errbuf, gettext("invalid object type (%s)"),
405 obj_str);
406 error_str = errbuf;
407 return (-1);
408 } /* switch */
409 /*NOTREACHED*/
413 obj_ent_t *
414 obj_lkup(char *obj_str)
416 int i;
418 for (i = 0; i < sizeof (obj_tbl) / sizeof (obj_ent_t); i++)
419 if (strcmp(obj_str, obj_tbl[i].obj_str) == 0)
420 return (&obj_tbl[i]);
422 /* not in table */
423 return (NULL);
428 * .func proc_type - process record type.
429 * .desc Process a record type. It is either as a number or a mnemonic.
430 * .call ret = proc_type(optstr).
431 * .arg optstr - ptr to name or number.
432 * .ret 0 - no errors detected.
433 * .ret -1 - error detected (error_str contains description).
436 proc_type(char *optstr)
438 struct au_event_ent *aep;
441 * Either a number or a name.
444 if (flags & M_TYPE) {
445 error_str = gettext("'m' option specified multiple times");
446 return (-1);
448 flags |= M_TYPE;
449 m_type = 0;
450 if (a_isnum(optstr, TRUE)) {
451 if ((aep = getauevnam(optstr)) != NULL)
452 m_type = aep->ae_number;
453 } else {
454 if ((aep = getauevnum((au_event_t)atoi(optstr))) !=
455 (struct au_event_ent *)NULL)
456 m_type = aep->ae_number;
458 if ((m_type == 0)) {
459 (void) sprintf(errbuf, gettext("invalid event (%s)"), optstr);
460 error_str = errbuf;
461 return (-1);
463 return (0);
468 * .func a_isnum - is it a number?
469 * .desc Determine if a string is a number or a name.
470 * A number may have a leading '+' or '-', but then must be
471 * all digits.
472 * .call ret = a_isnum(str).
473 * .arg str - ptr to the string.
474 * .arg leading - TRUE if leading '+-' allowed.
475 * .ret 0 - is a number.
476 * .ret 1 - is not a number.
479 a_isnum(char *str, int leading)
481 char *strs;
483 if ((leading == TRUE) && (*str == '-' || *str == '+'))
484 strs = str + 1;
485 else
486 strs = str;
488 if (strlen(strs) == strspn(strs, "0123456789"))
489 return (0);
490 else
491 return (1);
496 * .func proc_id - process user/group id's/
497 * .desc Process either a user number/name or group number/name.
498 * For names check to see if the name is active in the system
499 * to derive the number. If it is not active then fail. For a number
500 * also check to see if it is active, but only print a warning if it
501 * is not. An administrator may be looking at activity of a 'phantom'
502 * user.
503 * .call ret = proc_id(optstr, opt).
504 * .arg optstr - ptr to name or number.
505 * .arg opt - 'u' - audit user, 'e' - effective user, 'r' - real user,
506 * 'g' - group, 'f' - effective group.
507 * .ret 0 - no errors detected.
508 * .ret -1 - error detected (error_str contains description).
511 proc_id(char *optstr, int opt)
513 switch (opt) {
514 case 'e': /* effective user id */
515 if (flags & M_USERE) {
516 error_str = gettext(
517 "'e' option specified multiple times");
518 return (-1);
520 flags |= M_USERE;
521 return (proc_user(optstr, &m_usere));
522 case 'f': /* effective group id */
523 if (flags & M_GROUPE) {
524 error_str = gettext(
525 "'f' option specified multiple times");
526 return (-1);
528 flags |= M_GROUPE;
529 return (proc_group(optstr, &m_groupe));
530 case 'r': /* real user id */
531 if (flags & M_USERR) {
532 error_str = gettext(
533 "'r' option specified multiple times");
534 return (-1);
536 flags |= M_USERR;
537 return (proc_user(optstr, &m_userr));
538 case 'u': /* audit user id */
539 if (flags & M_USERA) {
540 error_str = gettext(
541 "'u' option specified multiple times");
542 return (-1);
544 flags |= M_USERA;
545 return (proc_user(optstr, &m_usera));
546 case 'g': /* real group id */
547 if (flags & M_GROUPR) {
548 error_str = gettext(
549 "'g' option specified multiple times");
550 return (-1);
552 flags |= M_GROUPR;
553 return (proc_group(optstr, &m_groupr));
554 default: /* impossible */
555 (void) sprintf(errbuf, gettext("'%c' unknown option"), opt);
556 error_str = errbuf;
557 return (-1);
559 /*NOTREACHED*/
564 proc_group(char *optstr, gid_t *gid)
566 struct group *grp;
568 if ((grp = getgrnam(optstr)) == NULL) {
569 if (!a_isnum(optstr, TRUE)) {
570 *gid = (gid_t)atoi(optstr);
571 return (0);
573 (void) sprintf(errbuf, gettext("group name invalid (%s)"),
574 optstr);
575 error_str = errbuf;
576 return (-1);
578 *gid = grp->gr_gid;
579 return (0);
584 proc_user(char *optstr, uid_t *uid)
586 struct passwd *usr;
588 if ((usr = getpwnam(optstr)) == NULL) {
589 if (!a_isnum(optstr, TRUE)) {
590 *uid = (uid_t)atoi(optstr);
591 return (0);
593 (void) sprintf(errbuf, gettext("user name invalid (%s)"),
594 optstr);
595 error_str = errbuf;
596 return (-1);
598 *uid = usr->pw_uid;
599 return (0);
604 * .func proc_date - process date argument.
605 * .desc Handle a date/time argument. See if the user has erred in combining
606 * the types of date arguments. Then parse the string and check for
607 * validity of each part.
608 * .call ret = proc_date(optstr, opt).
609 * .arg optstr - ptr to date/time string.
610 * .arg opt - 'd' for day, 'a' for after, or 'b' for before.
611 * .ret 0 - no errors detected.
612 * .ret -1 - errors detected (error_str knows what it is).
615 proc_date(char *optstr, int opt)
617 static int m_day = FALSE;
619 if (opt == 'd') {
620 if (m_day == TRUE) {
621 error_str = gettext(
622 "'d' option may not be used with 'a' or 'b'");
623 return (-1);
625 m_day = TRUE;
627 if ((opt == 'd') && (m_before || m_after)) {
628 error_str = gettext(
629 "'d' option may not be used with 'a' or 'b'");
630 return (-1);
632 if ((opt == 'a' || opt == 'b') && m_day) {
633 error_str = gettext(
634 "'a' or 'b' option may not be used with 'd'");
635 return (-1);
637 if ((opt == 'a') && (m_after != 0)) {
638 error_str = gettext("'a' option specified multiple times");
639 return (-1);
641 if ((opt == 'b') && (m_before != 0)) {
642 error_str = gettext("'b' option specified multiple times");
643 return (-1);
645 if (parse_time(optstr, opt))
646 return (-1);
647 return (0);
652 * .func proc_class - process message class argument.
653 * .desc Process class type and see if it is for real.
654 * .call ret = proc_class(optstr).
655 * .arg optstr - ptr to class.
656 * .ret 0 - class has class.
657 * .ret -1 - class in no good.
660 proc_class(char *optstr)
662 if (flags & M_CLASS) {
663 error_str = gettext("'c' option specified multiple times");
664 return (-1);
666 flags |= M_CLASS;
668 if (getauditflagsbin(optstr, &mask) != 0) {
669 (void) sprintf(errbuf, gettext("unknown class (%s)"), optstr);
670 error_str = errbuf;
671 return (-1);
674 if (mask.am_success != mask.am_failure) {
675 flags |= M_SORF;
678 return (0);
683 * .func process_fileopt - process command line file options.
684 * .desc Process the command line file options and gather the specified files
685 * together in file groups based upon file name suffix. The user can
686 * specify files explicitly on the command line or via a directory.
687 * This is called after the command line flags are processed (as
688 * denoted by '-').
689 * .call ret = process_fileopt(argc, argv, optindex).
690 * .arg argc - current value of argc.
691 * .arg argv - current value of argv.
692 * .arg optindex- current index into argv (as setup by getopt()).
693 * .ret 0 - no errors detected.
694 * .ret -1 - error detected (message already printed).
697 process_fileopt(int argc, char **argv, int optindex)
699 int f_mode = FM_ALLDIR;
700 char f_dr[MAXNAMLEN+1];
701 char *f_dir = f_dr;
702 char *fname;
703 static char *std = "standard input";
704 audit_fcb_t *fcb;
705 DIR * dirp;
706 struct dirent *dp;
707 audit_pcb_t *pcb;
710 * Take input from stdin, not any files.
711 * Use a single fcb to do this.
713 if (f_stdin) {
714 fcb = (audit_fcb_t *)a_calloc(1, sizeof (*fcb) + strlen(std));
715 (void) strcpy(fcb->fcb_file, std);
716 fcb->fcb_suffix = fcb->fcb_name = fcb->fcb_file;
717 fcb->fcb_next = NULL;
718 fcb->fcb_start = 0;
719 fcb->fcb_end = MAXLONG; /* forever */
720 if ((pcb = get_next_pcb((char *)NULL)) == (audit_pcb_t *)NULL)
721 return (-1);
722 pcb->pcb_suffix = fcb->fcb_file;
723 pcb->pcb_dfirst = pcb->pcb_first = fcb; /* one-item list */
724 pcb->pcb_dlast = pcb->pcb_last = fcb;
725 pcb->pcb_cur = fcb;
728 * No files specified on the command line.
729 * Process a directory of files or subdirectories.
731 else if (argc == optindex) {
733 * A specific server directory was requested.
735 if (f_server) {
736 if (strchr(f_server, '/')) { /* given full path */
737 f_dir = f_server;
738 f_mode = FM_ALLFILE; /* all files here */
739 } else { /* directory off audit root */
740 f_dir[0] = '\0';
741 (void) strcat(f_dir, f_root);
742 (void) strcat(f_dir, "/");
743 (void) strcat(f_dir, f_server);
744 f_mode = FM_ALLFILE;
748 * Gather all of the files in the directory 'f_dir'.
750 if (f_mode == FM_ALLFILE) {
751 if (gather_dir(f_dir)) { /* get those files together */
752 return (-1);
754 } else {
756 * Gather all of the files in all of the
757 * directories in 'f_root'.
759 if ((dirp = opendir(f_root)) == NULL) {
760 (void) sprintf(errbuf, gettext(
761 "%s can't open directory %s"), ar, f_root);
762 perror(errbuf);
763 return (-1);
765 /* read the directory and process all of the subs */
766 for (dp = readdir(dirp);
767 dp != NULL; dp = readdir(dirp)) {
768 if (dp->d_name[0] == '.')
769 continue;
770 f_dir[0] = '\0';
771 (void) strcat(f_dir, f_root);
772 (void) strcat(f_dir, "/");
773 (void) strcat(f_dir, dp->d_name);
774 if (gather_dir(f_dir)) /* process a sub */
775 return (-1);
777 (void) closedir(dirp);
779 } else {
781 * User specified filenames on the comm and line.
783 f_cmdline = TRUE;
784 for (; optindex < argc; optindex++) {
785 fname = argv[optindex]; /* get a filename */
786 if (proc_file(fname, FALSE))
787 return (-1);
790 return (0);
795 * .func gather_dir - gather a directory's files together.
796 * .desc Process all of the files in a specific directory. The files may
797 * be checked for adherence to the file name form at.
798 * If the directory can't be opened that is ok - just print
799 * a message and continue.
800 * .call ret = gather_dir(dir).
801 * .arg dir - ptr to full pathname of directory.
802 * .ret 0 - no errors detected.
803 * .ret -1 - error detected (message already printed).
806 gather_dir(char *dir)
808 char dname[MAXNAMLEN+1];
809 char fname[MAXNAMLEN+1];
810 DIR * dirp;
811 struct dirent *dp;
813 (void) snprintf(dname, sizeof (dname), "%s/files", dir);
815 if ((dirp = opendir(dname)) == NULL) {
816 if (errno != ENOTDIR) {
817 (void) sprintf(errbuf,
818 gettext("%s can't open directory - %s"), ar, dname);
819 perror(errbuf);
821 return (0);
823 for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
824 if (dp->d_name[0] == '.') /* can't see hidden files */
825 continue;
826 fname[0] = '\0';
827 (void) strcat(fname, dname); /* create pathname of file */
828 (void) strcat(fname, "/");
829 (void) strcat(fname, dp->d_name);
830 if (proc_file(fname, TRUE))
831 return (-1);
833 (void) closedir(dirp);
834 return (0);
839 * .func proc_file - process a single candidate file.
840 * .desc Check out a file to see if it should be used in the merge.
841 * This includes checking the name (mode is TRUE) against the
842 * file format, checking access rights to the file, and thence
843 * getting and fcb and installing the fcb into the correct pcb.
844 * If the file fails then the fcb is not installed into a pcb
845 * and the file dissapears from view.
846 * .call proc_file(fname, mode).
847 * .arg fname - ptr to full pathna me of file.
848 * .arg mode - TRUE if checking adherence to file name format.
849 * .ret 0 - no fatal errors detected.
850 * .ret -1 - fatal error detected - quit altogether
851 * (message already printed).
854 proc_file(char *fname, int mode)
856 int reject = FALSE;
857 size_t len;
858 struct stat stat_buf;
859 audit_fcb_t *fcb, *fcbp, *fcbprev;
860 audit_pcb_t *pcb;
863 * See if it is a weird file like a directory or
864 * character special (around here?).
866 if (stat(fname, &stat_buf)) {
867 return (0);
869 if (!S_ISREG(stat_buf.st_mode))
870 return (0);
872 * Allocate a new fcb to hold fcb and full filename.
874 len = sizeof (audit_fcb_t) + strlen(fname);
875 fcb = (audit_fcb_t *)a_calloc(1, len);
876 (void) strcpy(fcb->fcb_file, fname);
877 if (check_file(fcb, mode)) { /* check file name */
878 if (!f_quiet) {
879 (void) fprintf(stderr, "%s %s:\n %s.\n", ar,
880 error_str, fname);
882 reject = TRUE;
883 } else {
885 * Check against file criteria.
886 * Check finish-time here, and start-time later on
887 * while processing.
888 * This is because the start time on a file can be after
889 * the first record(s).
891 if (f_complete && (fcb->fcb_flags & FF_NOTTERM) && !f_cmdline)
892 reject = TRUE;
893 if (!f_all && (fcb->fcb_end < m_after))
894 reject = TRUE;
895 if (f_machine) {
896 if (strlen(fcb->fcb_suffix) != strlen(f_machine) ||
897 (strcmp(fcb->fcb_suffix, f_machine) != 0)) {
898 reject = TRUE;
902 if (reject == FALSE) {
903 filenum++; /* count of total files to be processed */
904 fcb->fcb_next = NULL;
905 if ((pcb = get_next_pcb(fcb->fcb_suffix)) == NULL) {
906 return (-1);
908 /* Place FCB into the PCB in order - oldest first. */
909 fcbp = pcb->pcb_first;
910 fcbprev = NULL;
911 while (fcbp != NULL) {
912 if (fcb->fcb_start < fcbp->fcb_start) {
913 if (fcbprev)
914 fcbprev->fcb_next = fcb;
915 else
916 pcb->pcb_dfirst = pcb->pcb_first = fcb;
917 fcb->fcb_next = fcbp;
918 break;
920 fcbprev = fcbp;
921 fcbp = fcbp->fcb_next;
923 /* younger than all || empty list */
924 if (!fcb->fcb_next) {
925 if (pcb->pcb_first == NULL)
926 pcb->pcb_dfirst = pcb->pcb_first = fcb;
927 pcb->pcb_dlast = pcb->pcb_last = fcb;
928 if (fcbprev)
929 fcbprev->fcb_next = fcb;
931 } else {
932 free((char *)fcb); /* rejected */
934 return (0);
939 * .func check_file - check filename and setup fcb.
940 * .desc Check adherence to the file format (do_check is TRUE) and setup
941 * the fcb with useful information.
942 * filename format: yyyymmddhhmmss.yyyymmddhhmmss.suffix
943 * yyyymmddhhmmss.not_terminated.suffix
944 * If do_check is FALSE then still see if the filename does confirm
945 * to the format. If it does then extract useful information from
946 * it (start time and end time). But if it doesn't then don't print
947 * any error messages.
948 * .call ret = check_file(fcb, do_check).
949 * .arg fcb - ptr to fcb that holds the file.
950 * .arg do_check - if TRUE do check adherence to file format.
951 * .ret 0 - no errors detected.
952 * .ret -1 - file failed somehow (error_str tells why).
955 check_file(audit_fcb_t *fcb, int do_check)
957 int ret;
958 char *namep, *slp;
959 char errb[256]; /* build error message */
960 struct tm tme;
962 errb[0] = '\0';
963 /* get just the filename */
964 for (slp = namep = fcb->fcb_file; *namep; namep++) {
965 if (*namep == '/')
966 slp = namep + 1; /* slp -> the filename itself */
968 if (do_check == FALSE) {
969 fcb->fcb_end = MAXLONG; /* forever */
970 fcb->fcb_suffix = NULL;
971 fcb->fcb_name = slp;
972 ret = 0;
973 } else {
974 ret = -1;
976 if ((int)strlen(slp) < 31) {
977 (void) sprintf(errbuf, gettext("filename too short (%d)"),
978 strlen(slp));
979 error_str = errbuf;
980 return (ret);
983 * Get working copy of filename.
985 namep = (char *)a_calloc(1, strlen(slp) + 1);
986 (void) strcpy(namep, slp);
987 if (namep[14] != '.' || namep[29] != '.') {
988 (void) sprintf(errbuf,
989 gettext("invalid filename format (%c or %c)"), namep[14],
990 namep[29]);
991 error_str = errbuf;
992 free(namep);
993 return (ret);
995 namep[14] = '\0'; /* mark off start time */
996 namep[29] = '\0'; /* mark off finish time */
997 if (derive_date(namep, &tme)) {
998 (void) strcat(errb, gettext("starting time-stamp invalid - "));
999 (void) strcat(errb, error_str);
1000 (void) strcpy(errbuf, errb);
1001 error_str = errbuf;
1002 free(namep);
1003 return (ret);
1006 * Keep start time from filename. Use it to order files in
1007 * the file list. Later we will update this when we read
1008 * the first record from the file.
1010 fcb->fcb_start = tm_to_secs(&tme);
1012 if (strcmp(&namep[15], "not_terminated") == 0) {
1013 fcb->fcb_end = MAXLONG; /* forever */
1015 * Only treat a 'not_terminated' file as such if
1016 * it is not on the command line.
1018 if (do_check == TRUE)
1019 fcb->fcb_flags |= FF_NOTTERM;
1020 } else if (derive_date(&namep[15], &tme)) {
1021 (void) strcat(errb, gettext("ending time-stamp invalid - "));
1022 (void) strcat(errb, error_str);
1023 (void) strcpy(errbuf, errb);
1024 error_str = errbuf;
1025 free(namep);
1026 return (ret);
1027 } else {
1028 fcb->fcb_end = tm_to_secs(&tme);
1030 fcb->fcb_name = slp;
1031 fcb->fcb_suffix = &slp[30];
1032 free(namep);
1033 return (0);
1038 * .func get_next_pcb - get a pcb to use.
1039 * .desc The pcb's in the array audit_pcbs are used to hold single file
1040 * groups in the form of a linked list. Each pcb holds files that
1041 * are tied together by a common suffix in the file name. Here we
1042 * get either 1. the existing pcb holding a specified sufix or
1043 * 2. a new pcb if we can't find an existing one.
1044 * .call pcb = get_next_pcb(suffix).
1045 * .arg suffix - ptr to suffix we are seeking.
1046 * .ret pcb - ptr to pcb that hold s the sought suffix.
1047 * .ret NULL- serious failure in memory allocation. Quit processing.
1049 audit_pcb_t *
1050 get_next_pcb(char *suffix)
1052 int i = 0;
1053 int zerosize;
1054 unsigned int size;
1055 audit_pcb_t *pcb;
1057 /* Search through (maybe) entire array. */
1058 while (i < pcbsize) {
1059 pcb = &audit_pcbs[i++];
1060 if (pcb->pcb_first == NULL) {
1061 proc_pcb(pcb, suffix, i);
1062 return (pcb); /* came to an unused one */
1064 if (suffix) {
1065 if (strcmp(pcb->pcb_suffix, suffix) == 0)
1066 return (pcb); /* matched one with suffix */
1070 * Uh-oh, the entire array is used and we haven't gotten one yet.
1071 * Allocate a bigger array.
1073 pcbsize += PCB_INC;
1074 size = pcbsize * sizeof (audit_pcb_t);
1075 zerosize = size - ((pcbsize - PCB_INC) * sizeof (audit_pcb_t));
1076 if ((audit_pcbs = (audit_pcb_t *)realloc((char *)audit_pcbs, size)) ==
1077 NULL) {
1078 (void) sprintf(errbuf,
1079 gettext("%s memory reallocation failed (%d bytes)"), ar,
1080 size);
1081 perror(errbuf);
1082 audit_stats(); /* give user statistics on usage */
1083 return (NULL); /* really bad thing to have happen */
1086 * Don't know if realloc clears the new memory like calloc would.
1088 (void) memset((void *) & audit_pcbs[pcbsize-PCB_INC], 0,
1089 (size_t)zerosize);
1090 pcb = &audit_pcbs[pcbsize-PCB_INC]; /* allocate the first new one */
1091 proc_pcb(pcb, suffix, pcbsize - PCB_INC);
1092 return (pcb);
1097 * .func proc_pcb - process pcb.
1098 * .desc Common pcb processing for above routine.
1099 * .call proc_pcb(pcb, suffix, i).
1100 * .arg pcb - ptr to pcb.
1101 * .arg suffix - prt to suffix tha t ties this group together.
1102 * .arg i - index into audit_pcbs[ ].
1103 * .ret void.
1105 void
1106 proc_pcb(audit_pcb_t *pcb, char *suffix, int i)
1108 if (suffix)
1109 pcb->pcb_suffix = suffix;
1110 pcbnum++; /* one more pcb in use */
1111 pcb->pcb_size = AUDITBUFSIZE;
1112 pcb->pcb_rec = (char *)a_calloc(1, AUDITBUFSIZE);
1113 pcb->pcb_time = -1;
1114 pcb->pcb_flags |= PF_USEFILE; /* note this one controls files */
1115 pcb->pcb_procno = i; /* save index into audit_pcbs [] for id */
1119 * proc_zonename - pick up zone name.
1121 * all non-empty and not-too-long strings are valid since any name
1122 * may be valid.
1124 * ret 0: non-empty string
1125 * ret -1: empty string or string is too long.
1127 static int
1128 proc_zonename(char *optstr)
1130 size_t length = strlen(optstr);
1131 if ((length < 1) || (length > ZONENAME_MAX)) {
1132 (void) sprintf(errbuf,
1133 gettext("invalid zone name: %s"), optstr);
1134 error_str = errbuf;
1135 return (-1);
1137 zonename = strdup(optstr);
1138 flags |= M_ZONENAME;
1139 return (0);
1143 * proc_frmi - set up frmi for pattern matching.
1144 * Logic ripped off of scf_walk_fmri()
1145 * Thanks to the smf team.
1147 * ret 0: OK
1148 * ret -1: error
1150 static int
1151 proc_fmri(char *optstr)
1153 if (strpbrk(optstr, "*?[") != NULL) {
1154 /* have a pattern to glob for */
1156 fmri.sp_type = PATTERN_GLOB;
1157 if (optstr[0] == '*' ||
1158 (strlen(optstr) >= 4 && optstr[3] == ':')) {
1159 fmri.sp_arg = strdup(optstr);
1160 } else if ((fmri.sp_arg = malloc(strlen(optstr) + 6)) != NULL) {
1161 (void) snprintf(fmri.sp_arg, strlen(optstr) + 6,
1162 "svc:/%s", optstr);
1164 } else {
1165 fmri.sp_type = PATTERN_PARTIAL;
1166 fmri.sp_arg = strdup(optstr);
1168 if (fmri.sp_arg == NULL)
1169 return (-1);
1171 return (0);