Home | History | Annotate | Download | only in auditreduce
      1 /*
      2  * CDDL HEADER START
      3  *
      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.
      7  *
      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.
     12  *
     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]
     18  *
     19  * CDDL HEADER END
     20  */
     21 /*
     22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 /*
     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.
     30  */
     31 
     32 #include <locale.h>
     33 #include <sys/zone.h>	/* for max zonename length */
     34 #include "auditr.h"
     35 
     36 /*
     37  * Object entry.
     38  * Maps object strings specified on the command line to a flag
     39  * used when searching by object type.
     40  */
     41 
     42 struct obj_ent {
     43 	char	*obj_str; /* string specified on the command line */
     44 	int	obj_flag; /* flag used when searching */
     45 };
     46 
     47 typedef struct obj_ent obj_ent_t;
     48 
     49 /*
     50  * Supports searches by object type.
     51  */
     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 
     73 extern int	derive_date(char *, struct tm *);
     74 extern int	parse_time(char *, int);
     75 extern char	*re_comp2(char *);
     76 extern time_t	tm_to_secs(struct tm *);
     77 
     78 static int	a_isnum(char *, int);
     79 static int	check_file(audit_fcb_t *, int);
     80 static int	gather_dir(char *);
     81 static audit_pcb_t *get_next_pcb(char *);
     82 static obj_ent_t *obj_lkup(char *);
     83 static int	proc_class(char *);
     84 static int	proc_date(char *, int);
     85 static int	proc_file(char *, int);
     86 static int	process_fileopt(int, char *argv[], int);
     87 static int	proc_group(char *, gid_t *);
     88 static int	proc_id(char *, int);
     89 static int	proc_object(char *);
     90 static void	proc_pcb(audit_pcb_t *, char *, int);
     91 static int	proc_label(char *);
     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 *);
     98 
     99 /*
    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).
    111  */
    112 int
    113 process_options(int argc, char **argv)
    114 {
    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 */
    120 
    121 	static char	*options = "ACD:M:NQR:S:VO:"
    122 	    "a:b:c:d:e:g:j:l:m:o:r:s:t:u:z:";
    123 
    124 	error_str = gettext("general error");
    125 
    126 	zonename = NULL;
    127 	/*
    128 	 * Big switch to process the flags.
    129 	 * Start_over: is for handling the '-' for standard input. Getopt()
    130 	 * doesn't recognize it.
    131 	 */
    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 'l':		/* TX label range */
    199 			if (!is_system_labeled()) {
    200 				(void) fprintf(stderr,
    201 				    gettext("%s option 'l' requires "
    202 				    "Trusted Extensions.\n"), ar);
    203 				return (-1);
    204 			}
    205 			if (proc_label(optarg))
    206 				error = TRUE;
    207 			break;
    208 		case 's':		/* session ID */
    209 			if (proc_sid(optarg))
    210 				error = TRUE;
    211 			break;
    212 		case 'z':		/* zone name */
    213 			if (proc_zonename(optarg))
    214 				error = TRUE;
    215 			break;
    216 		case 't':		/* termial ID reserved for later */
    217 		default:
    218 			return (-1);
    219 		}
    220 		if (error) {
    221 			(void) fprintf(stderr,
    222 			    gettext("%s command line error - %s.\n"),
    223 			    ar, error_str);
    224 			return (-1);
    225 		}
    226 	}
    227 	/* catch '-' option for stdin processing - getopt() won't see it */
    228 	if (optind < argc) {
    229 		if (argv[optind][0] == '-' && argv[optind][1] == '\0') {
    230 			optind++;
    231 			f_stdin = TRUE;
    232 			goto start_over;
    233 		}
    234 	}
    235 	/*
    236 	 * Give a default value for 'b' option if not specified.
    237 	 */
    238 	if (m_before == 0)
    239 		m_before = MAXLONG;	/* forever */
    240 	/*
    241 	 * Validate combinations of options.
    242 	 * The following are done:
    243 	 *	1. Can't have 'M' or 'S' or 'R' with filenames.
    244 	 *	2. Can't have an after ('a') time after a before ('b') time.
    245 	 *	3. Delete ('D') must have 'C' and 'A' and 'O' with it.
    246 	 *	4. Input from stdin ('-') can't have filenames too.
    247 	 */
    248 	if ((f_machine || f_server || f_root) && (argc != optind)) {
    249 		error_str = gettext(
    250 		    "no filenames allowed with 'M' or 'S' or 'R' options");
    251 		error_combo = TRUE;
    252 	}
    253 	if (m_after >= m_before) {
    254 		error_str =
    255 		    gettext("'a' parameter must be before 'b' parameter");
    256 		error_combo = TRUE;
    257 	}
    258 	if (f_delete &&
    259 	    (!f_complete || !f_all || !f_outfile)) {
    260 		error_str = gettext(
    261 		    "'C', 'A', and 'O' must be specified with 'D'");
    262 		error_combo = TRUE;
    263 	}
    264 	if (f_stdin && (argc != optind)) {
    265 		error_str = gettext("no filenames allowed with '-' option");
    266 		error_combo = TRUE;
    267 	}
    268 	/*
    269 	 * If error with option combos then print message and exit.
    270 	 * If there was an error with just an option then exit.
    271 	 */
    272 	if (error_combo) {
    273 		(void) fprintf(stderr,
    274 		    gettext("%s command line error - %s.\n"), ar, error_str);
    275 		return (-1);
    276 	}
    277 	if (f_root == NULL)
    278 		f_root = "/etc/security/audit";
    279 	/*
    280 	 * Now handle any filenames included in the command line.
    281 	 */
    282 	return (process_fileopt(argc, argv, optind));
    283 }
    284 
    285 int
    286 proc_subject(char *optarg)
    287 {
    288 	if (flags & M_SUBJECT) {
    289 		error_str = gettext("'j' option specified multiple times");
    290 		return (-1);
    291 	}
    292 	flags |= M_SUBJECT;
    293 	subj_id = atol(optarg);
    294 	return (0);
    295 }
    296 
    297 int
    298 proc_sid(char *optarg)
    299 {
    300 	if (flags & M_SID) {
    301 		error_str = gettext("'s' option specified multiple times");
    302 		return (-1);
    303 	}
    304 	flags |= M_SID;
    305 	m_sid = (au_asid_t)atol(optarg);
    306 	return (0);
    307 }
    308 
    309 int
    310 proc_object(char *optarg)
    311 {
    312 	char	*obj_str;
    313 	char	*obj_val;
    314 	char	*obj_arg;
    315 	int	err;
    316 
    317 	obj_ent_t *oep;
    318 	struct hostent *he;
    319 
    320 	if (flags & M_OBJECT) {
    321 		error_str = gettext("'o' option specified multiple times");
    322 		return (-1);
    323 	}
    324 	flags |= M_OBJECT;
    325 	if ((obj_arg = strdup(optarg)) == (char *)0)
    326 		return (-1);
    327 	if ((obj_str = strtok(optarg, "=")) == (char *)0 ||
    328 	    (oep = obj_lkup(obj_str)) == (obj_ent_t *)0 ||
    329 	    (obj_val = strtok((char *)0, "=")) == (char *)0) {
    330 		(void) sprintf(errbuf, gettext("invalid object arg (%s)"),
    331 		    obj_arg);
    332 		error_str = errbuf;
    333 		return (-1);
    334 	}
    335 
    336 	obj_flag = oep->obj_flag;
    337 
    338 	switch (obj_flag) {
    339 	case OBJ_PATH:
    340 		if ((error_str = re_comp2(obj_val)) != (char *)NULL) {
    341 			return (-1);
    342 		}
    343 		return (0);
    344 		/* NOTREACHED */
    345 	case OBJ_SOCK:
    346 		if (!a_isnum(obj_val, TRUE)) {
    347 			obj_id = atol(obj_val);
    348 			socket_flag = SOCKFLG_PORT;
    349 			return (0);
    350 		}
    351 		if (*obj_val == '0') {
    352 			(void) sscanf(obj_val, "%x", (uint_t *)&obj_id);
    353 			socket_flag = SOCKFLG_PORT;
    354 			return (0);
    355 		}
    356 
    357 		he = getipnodebyname((const void *)obj_val, AF_INET6, 0, &err);
    358 		if (he == 0) {
    359 			he = getipnodebyname((const void *)obj_val, AF_INET,
    360 			    0, &err);
    361 			if (he == 0) {
    362 				(void) sprintf(errbuf,
    363 				    gettext("invalid machine name (%s)"),
    364 				    obj_val);
    365 				error_str = errbuf;
    366 				return (-1);
    367 			}
    368 		}
    369 
    370 		if (he->h_addrtype == AF_INET6) {
    371 			/* LINTED */
    372 			if (IN6_IS_ADDR_V4MAPPED(
    373 			    (in6_addr_t *)he->h_addr_list[0])) {
    374 				/* address is IPv4 (32 bits) */
    375 				(void) memcpy(&obj_id,
    376 				    he->h_addr_list[0] + 12, 4);
    377 				ip_type = AU_IPv4;
    378 			} else {
    379 				(void) memcpy(ip_ipv6, he->h_addr_list[0], 16);
    380 				ip_type = AU_IPv6;
    381 			}
    382 		} else {
    383 			/* address is IPv4 (32 bits) */
    384 			(void) memcpy(&obj_id, he->h_addr_list[0], 4);
    385 			ip_type = AU_IPv4;
    386 		}
    387 
    388 		freehostent(he);
    389 		socket_flag = SOCKFLG_MACHINE;
    390 		return (0);
    391 		break;
    392 	case OBJ_MSG:
    393 	case OBJ_SEM:
    394 	case OBJ_SHM:
    395 	case OBJ_PROC:
    396 		obj_id = atol(obj_val);
    397 		return (0);
    398 		/* NOTREACHED */
    399 	case OBJ_FGROUP:
    400 	case OBJ_MSGGROUP:
    401 	case OBJ_SEMGROUP:
    402 	case OBJ_SHMGROUP:
    403 	case OBJ_PGROUP:
    404 		return (proc_group(obj_val, &obj_group));
    405 		/* NOTREACHED */
    406 	case OBJ_FOWNER:
    407 	case OBJ_MSGOWNER:
    408 	case OBJ_SEMOWNER:
    409 	case OBJ_SHMOWNER:
    410 	case OBJ_POWNER:
    411 		return (proc_user(obj_val, &obj_owner));
    412 		/* NOTREACHED */
    413 	case OBJ_FMRI:
    414 		return (proc_fmri(obj_val));
    415 		/* NOTREACHED */
    416 	case OBJ_LP: /* lp objects have not yet been defined */
    417 	default: /* impossible */
    418 		(void) sprintf(errbuf, gettext("invalid object type (%s)"),
    419 		    obj_str);
    420 		error_str = errbuf;
    421 		return (-1);
    422 		/* NOTREACHED */
    423 	} /* switch */
    424 	/*NOTREACHED*/
    425 }
    426 
    427 
    428 obj_ent_t *
    429 obj_lkup(char *obj_str)
    430 {
    431 	int	i;
    432 
    433 	for (i = 0; i < sizeof (obj_tbl) / sizeof (obj_ent_t); i++)
    434 		if (strcmp(obj_str, obj_tbl[i].obj_str) == 0)
    435 			return (&obj_tbl[i]);
    436 
    437 	/* not in table */
    438 	return (NULL);
    439 }
    440 
    441 
    442 /*
    443  * .func	proc_type - process record type.
    444  * .desc	Process a record type. It is either as a number or a mnemonic.
    445  * .call	ret = proc_type(optstr).
    446  * .arg	optstr	- ptr to name or number.
    447  * .ret	0	- no errors detected.
    448  * .ret	-1	- error detected (error_str contains description).
    449  */
    450 int
    451 proc_type(char *optstr)
    452 {
    453 	struct au_event_ent *aep;
    454 
    455 	/*
    456 	 * Either a number or a name.
    457 	 */
    458 
    459 	if (flags & M_TYPE) {
    460 		error_str = gettext("'m' option specified multiple times");
    461 		return (-1);
    462 	}
    463 	flags |= M_TYPE;
    464 	m_type = 0;
    465 	if (a_isnum(optstr, TRUE)) {
    466 		if ((aep = getauevnam(optstr)) != NULL)
    467 			m_type = aep->ae_number;
    468 	} else {
    469 		if ((aep = getauevnum((au_event_t)atoi(optstr))) !=
    470 		    (struct au_event_ent *)NULL)
    471 			m_type = aep->ae_number;
    472 	}
    473 	if ((m_type == 0)) {
    474 		(void) sprintf(errbuf, gettext("invalid event (%s)"), optstr);
    475 		error_str = errbuf;
    476 		return (-1);
    477 	}
    478 	return (0);
    479 }
    480 
    481 
    482 /*
    483  * .func	a_isnum - is it a number?
    484  * .desc	Determine if a string is a number or a name.
    485  *	A number may have a leading '+' or '-', but then must be
    486  *	all digits.
    487  * .call	ret = a_isnum(str).
    488  * .arg	str - ptr to the string.
    489  * .arg	leading	- TRUE if leading '+-' allowed.
    490  * .ret	0	- is a number.
    491  * .ret	1	- is not a number.
    492  */
    493 int
    494 a_isnum(char *str, int leading)
    495 {
    496 	char	*strs;
    497 
    498 	if ((leading == TRUE) && (*str == '-' || *str == '+'))
    499 		strs = str + 1;
    500 	else
    501 		strs = str;
    502 
    503 	if (strlen(strs) == strspn(strs, "0123456789"))
    504 		return (0);
    505 	else
    506 		return (1);
    507 }
    508 
    509 
    510 /*
    511  * .func	proc_id	- process user/group id's/
    512  * .desc	Process either a user number/name or group number/name.
    513  *	For names check to see if the name is active in the system
    514  *	to derive the number. If it is not active then fail. For a number
    515  *	also check to see if it is active, but only print a warning if it
    516  *	is not. An administrator may be looking at activity of a 'phantom'
    517  *	user.
    518  * .call	ret = proc_id(optstr, opt).
    519  * .arg	optstr	- ptr to name or number.
    520  * .arg	opt	- 'u' - audit user, 'e' - effective user, 'r' - real user,
    521  *		  'g' - group, 'f' - effective group.
    522  * .ret	0	- no errors detected.
    523  * .ret	-1	- error detected (error_str contains description).
    524  */
    525 int
    526 proc_id(char *optstr, int opt)
    527 {
    528 	switch (opt) {
    529 	case 'e': 		/* effective user id */
    530 		if (flags & M_USERE) {
    531 			error_str = gettext(
    532 			    "'e' option specified multiple times");
    533 			return (-1);
    534 		}
    535 		flags |= M_USERE;
    536 		return (proc_user(optstr, &m_usere));
    537 		/* NOTREACHED */
    538 	case 'f': 		/* effective group id */
    539 		if (flags & M_GROUPE) {
    540 			error_str = gettext(
    541 			    "'f' option specified multiple times");
    542 			return (-1);
    543 		}
    544 		flags |= M_GROUPE;
    545 		return (proc_group(optstr, &m_groupe));
    546 		/* NOTREACHED */
    547 	case 'r': 		/* real user id */
    548 		if (flags & M_USERR) {
    549 			error_str = gettext(
    550 			    "'r' option specified multiple times");
    551 			return (-1);
    552 		}
    553 		flags |= M_USERR;
    554 		return (proc_user(optstr, &m_userr));
    555 		/* NOTREACHED */
    556 	case 'u': 		/* audit user id */
    557 		if (flags & M_USERA) {
    558 			error_str = gettext(
    559 			    "'u' option specified multiple times");
    560 			return (-1);
    561 		}
    562 		flags |= M_USERA;
    563 		return (proc_user(optstr, &m_usera));
    564 		/* NOTREACHED */
    565 	case 'g': 		/* real group id */
    566 		if (flags & M_GROUPR) {
    567 			error_str = gettext(
    568 			    "'g' option specified multiple times");
    569 			return (-1);
    570 		}
    571 		flags |= M_GROUPR;
    572 		return (proc_group(optstr, &m_groupr));
    573 		/* NOTREACHED */
    574 	default: 		/* impossible */
    575 		(void) sprintf(errbuf, gettext("'%c' unknown option"), opt);
    576 		error_str = errbuf;
    577 		return (-1);
    578 		/* NOTREACHED */
    579 	}
    580 	/*NOTREACHED*/
    581 }
    582 
    583 
    584 int
    585 proc_group(char *optstr, gid_t *gid)
    586 {
    587 	struct group *grp;
    588 
    589 	if ((grp = getgrnam(optstr)) == NULL) {
    590 		if (!a_isnum(optstr, TRUE)) {
    591 			*gid = (gid_t)atoi(optstr);
    592 			return (0);
    593 		}
    594 		(void) sprintf(errbuf, gettext("group name invalid (%s)"),
    595 		    optstr);
    596 		error_str = errbuf;
    597 		return (-1);
    598 	}
    599 	*gid = grp->gr_gid;
    600 	return (0);
    601 }
    602 
    603 
    604 int
    605 proc_user(char *optstr, uid_t *uid)
    606 {
    607 	struct passwd *usr;
    608 
    609 	if ((usr = getpwnam(optstr)) == NULL) {
    610 		if (!a_isnum(optstr, TRUE)) {
    611 			*uid = (uid_t)atoi(optstr);
    612 			return (0);
    613 		}
    614 		(void) sprintf(errbuf, gettext("user name invalid (%s)"),
    615 		    optstr);
    616 		error_str = errbuf;
    617 		return (-1);
    618 	}
    619 	*uid = usr->pw_uid;
    620 	return (0);
    621 }
    622 
    623 
    624 /*
    625  * .func proc_date - process date argument.
    626  * .desc Handle a date/time argument. See if the user has erred in combining
    627  *	the types of date arguments. Then parse the string and check for
    628  *	validity of each part.
    629  * .call	ret = proc_date(optstr, opt).
    630  * .arg	optstr	- ptr to date/time string.
    631  * .arg	opt	- 'd' for day, 'a' for after, or 'b' for before.
    632  * .ret	0	- no errors detected.
    633  * .ret	-1	- errors detected (error_str knows what it is).
    634  */
    635 int
    636 proc_date(char *optstr, int opt)
    637 {
    638 	static int	m_day = FALSE;
    639 
    640 	if (opt == 'd') {
    641 		if (m_day == TRUE) {
    642 			error_str = gettext(
    643 			    "'d' option may not be used with 'a' or 'b'");
    644 			return (-1);
    645 		}
    646 		m_day = TRUE;
    647 	}
    648 	if ((opt == 'd') && (m_before || m_after)) {
    649 		error_str = gettext(
    650 		    "'d' option may not be used with 'a' or 'b'");
    651 		return (-1);
    652 	}
    653 	if ((opt == 'a' || opt == 'b') && m_day) {
    654 		error_str = gettext(
    655 		    "'a' or 'b' option may not be used with 'd'");
    656 		return (-1);
    657 	}
    658 	if ((opt == 'a') && (m_after != 0)) {
    659 		error_str = gettext("'a' option specified multiple times");
    660 		return (-1);
    661 	}
    662 	if ((opt == 'b') && (m_before != 0)) {
    663 		error_str = gettext("'b' option specified multiple times");
    664 		return (-1);
    665 	}
    666 	if (parse_time(optstr, opt))
    667 		return (-1);
    668 	return (0);
    669 }
    670 
    671 
    672 /*
    673  * .func	proc_class - process message class argument.
    674  * .desc	Process class type and see if it is for real.
    675  * .call	ret = proc_class(optstr).
    676  * .arg	optstr	- ptr to class.
    677  * .ret	0	- class has class.
    678  * .ret	-1	- class in no good.
    679  */
    680 int
    681 proc_class(char *optstr)
    682 {
    683 	if (flags & M_CLASS) {
    684 		error_str = gettext("'c' option specified multiple times");
    685 		return (-1);
    686 	}
    687 	flags |= M_CLASS;
    688 
    689 	if (getauditflagsbin(optstr, &mask) != 0) {
    690 		(void) sprintf(errbuf, gettext("unknown class (%s)"), optstr);
    691 		error_str = errbuf;
    692 		return (-1);
    693 	}
    694 
    695 	if (mask.am_success != mask.am_failure) {
    696 		flags |= M_SORF;
    697 	}
    698 
    699 	return (0);
    700 }
    701 
    702 
    703 /*
    704  * .func process_fileopt - process command line file options.
    705  * .desc Process the command line file options and gather the specified files
    706  *	together in file groups based upon file name suffix. The user can
    707  *	specify files explicitly on the command line or via a directory.
    708  *	This is called after the command line flags are processed (as
    709  *	denoted by '-').
    710  * .call	ret = process_fileopt(argc, argv, optindex).
    711  * .arg	argc	- current value of argc.
    712  * .arg	argv	- current value of argv.
    713  * .arg	optindex- current index into argv (as setup by getopt()).
    714  * .ret	0	- no errors detected.
    715  * .ret	-1	- error detected (message already printed).
    716  */
    717 int
    718 process_fileopt(int argc, char **argv, int optindex)
    719 {
    720 	int	f_mode = FM_ALLDIR;
    721 	char	f_dr[MAXNAMLEN+1];
    722 	char	*f_dir = f_dr;
    723 	char	*fname;
    724 	static char	*std = "standard input";
    725 	audit_fcb_t *fcb;
    726 	DIR * dirp;
    727 	struct dirent *dp;
    728 	audit_pcb_t *pcb;
    729 
    730 	/*
    731 	 * Take input from stdin, not any files.
    732 	 * Use a single fcb to do this.
    733 	 */
    734 	if (f_stdin) {
    735 		fcb = (audit_fcb_t *)a_calloc(1, sizeof (*fcb) + strlen(std));
    736 		(void) strcpy(fcb->fcb_file, std);
    737 		fcb->fcb_suffix = fcb->fcb_name = fcb->fcb_file;
    738 		fcb->fcb_next = NULL;
    739 		fcb->fcb_start = 0;
    740 		fcb->fcb_end = MAXLONG;		/* forever */
    741 		if ((pcb = get_next_pcb((char *)NULL)) == (audit_pcb_t *)NULL)
    742 			return (-1);
    743 		pcb->pcb_suffix = fcb->fcb_file;
    744 		pcb->pcb_dfirst = pcb->pcb_first = fcb;	/* one-item list */
    745 		pcb->pcb_dlast = pcb->pcb_last = fcb;
    746 		pcb->pcb_cur = fcb;
    747 	}
    748 	/*
    749 	 * No files specified on the command line.
    750 	 * Process a directory of files or subdirectories.
    751 	 */
    752 	else if (argc == optindex) {
    753 		/*
    754 		 * A specific server directory was requested.
    755 		 */
    756 		if (f_server) {
    757 			if (strchr(f_server, '/')) {	/* given full path */
    758 				f_dir = f_server;
    759 				f_mode = FM_ALLFILE;	/* all files here */
    760 			} else {		/* directory off audit root */
    761 				f_dir[0] = '\0';
    762 				(void) strcat(f_dir, f_root);
    763 				(void) strcat(f_dir, "/");
    764 				(void) strcat(f_dir, f_server);
    765 				f_mode = FM_ALLFILE;
    766 			}
    767 		}
    768 		/*
    769 		 * Gather all of the files in the directory 'f_dir'.
    770 		 */
    771 		if (f_mode == FM_ALLFILE) {
    772 			if (gather_dir(f_dir)) { /* get those files together */
    773 				return (-1);
    774 			}
    775 		} else {
    776 			/*
    777 			 * Gather all of the files in all of the
    778 			 * directories in 'f_root'.
    779 			 */
    780 			if ((dirp = opendir(f_root)) == NULL) {
    781 				(void) sprintf(errbuf, gettext(
    782 				    "%s can't open directory %s"), ar, f_root);
    783 				perror(errbuf);
    784 				return (-1);
    785 			}
    786 			/* read the directory and process all of the subs */
    787 			for (dp = readdir(dirp);
    788 			    dp != NULL; dp = readdir(dirp)) {
    789 				if (dp->d_name[0] == '.')
    790 					continue;
    791 				f_dir[0] = '\0';
    792 				(void) strcat(f_dir, f_root);
    793 				(void) strcat(f_dir, "/");
    794 				(void) strcat(f_dir, dp->d_name);
    795 				if (gather_dir(f_dir))	/* process a sub */
    796 					return (-1);
    797 			}
    798 			(void) closedir(dirp);
    799 		}
    800 	} else {
    801 		/*
    802 		 * User specified filenames on the comm and line.
    803 		 */
    804 		f_cmdline = TRUE;
    805 		for (; optindex < argc; optindex++) {
    806 			fname = argv[optindex];		/* get a filename */
    807 			if (proc_file(fname, FALSE))
    808 				return (-1);
    809 		}
    810 	}
    811 	return (0);
    812 }
    813 
    814 
    815 /*
    816  * .func	gather_dir - gather a directory's files together.
    817  * .desc	Process all of the files in a specific directory. The files may
    818  *	be checked for adherence to the file name form at.
    819  *	If the directory can't be opened that is ok - just print
    820  *	a message and continue.
    821  * .call	ret = gather_dir(dir).
    822  * .arg	dir	- ptr to full pathname of directory.
    823  * .ret	0	- no errors detected.
    824  * .ret	-1	- error detected (message already printed).
    825  */
    826 int
    827 gather_dir(char *dir)
    828 {
    829 	char	dname[MAXNAMLEN+1];
    830 	char	fname[MAXNAMLEN+1];
    831 	DIR * dirp;
    832 	struct dirent *dp;
    833 
    834 	(void) snprintf(dname, sizeof (dname), "%s/files", dir);
    835 
    836 	if ((dirp = opendir(dname)) == NULL) {
    837 		if (errno != ENOTDIR) {
    838 			(void) sprintf(errbuf,
    839 			    gettext("%s can't open directory - %s"), ar, dname);
    840 			perror(errbuf);
    841 		}
    842 		return (0);
    843 	}
    844 	for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
    845 		if (dp->d_name[0] == '.')	/* can't see hidden files */
    846 			continue;
    847 		fname[0] = '\0';
    848 		(void) strcat(fname, dname);	/* create pathname of file */
    849 		(void) strcat(fname, "/");
    850 		(void) strcat(fname, dp->d_name);
    851 		if (proc_file(fname, TRUE))
    852 			return (-1);
    853 	}
    854 	(void) closedir(dirp);
    855 	return (0);
    856 }
    857 
    858 
    859 /*
    860  * .func	proc_file - process a single candidate file.
    861  * .desc	Check out a file to see if it should be used in the merge.
    862  *	This includes checking the name (mode is TRUE) against the
    863  *	file format, checking access rights to the file, and thence
    864  *	getting and fcb and installing the fcb into the correct pcb.
    865  *	If the file fails then the fcb is not installed into a pcb
    866  *	and the file dissapears from view.
    867  * .call	proc_file(fname, mode).
    868  * .arg	fname	- ptr to full pathna me of file.
    869  * .arg	mode	- TRUE if checking adherence to file name format.
    870  * .ret	0	- no fatal errors detected.
    871  * .ret	-1	- fatal error detected - quit altogether
    872  *		  (message already printed).
    873  */
    874 int
    875 proc_file(char *fname, int mode)
    876 {
    877 	int reject = FALSE;
    878 	size_t len;
    879 	struct stat stat_buf;
    880 	audit_fcb_t *fcb, *fcbp, *fcbprev;
    881 	audit_pcb_t *pcb;
    882 
    883 	/*
    884 	 * See if it is a weird file like a directory or
    885 	 * character special (around here?).
    886 	 */
    887 	if (stat(fname, &stat_buf)) {
    888 		return (0);
    889 	}
    890 	if (!S_ISREG(stat_buf.st_mode))
    891 		return (0);
    892 	/*
    893 	 * Allocate a new fcb to hold fcb and full filename.
    894 	 */
    895 	len = sizeof (audit_fcb_t) + strlen(fname);
    896 	fcb = (audit_fcb_t *)a_calloc(1, len);
    897 	(void) strcpy(fcb->fcb_file, fname);
    898 	if (check_file(fcb, mode)) { /* check file name */
    899 		if (!f_quiet) {
    900 			(void) fprintf(stderr, "%s %s:\n  %s.\n", ar,
    901 			    error_str, fname);
    902 		}
    903 		reject = TRUE;
    904 	} else {
    905 		/*
    906 		 * Check against file criteria.
    907 		 * Check finish-time here, and start-time later on
    908 		 * while processing.
    909 		 * This is because the start time on a file can be after
    910 		 * the first record(s).
    911 		 */
    912 		if (f_complete && (fcb->fcb_flags & FF_NOTTERM) && !f_cmdline)
    913 			reject = TRUE;
    914 		if (!f_all && (fcb->fcb_end < m_after))
    915 			reject = TRUE;
    916 		if (f_machine) {
    917 			if (strlen(fcb->fcb_suffix) != strlen(f_machine) ||
    918 			    (strcmp(fcb->fcb_suffix, f_machine) != 0)) {
    919 				reject = TRUE;
    920 			}
    921 		}
    922 	}
    923 	if (reject == FALSE) {
    924 		filenum++;	/* count of total files to be processed */
    925 		fcb->fcb_next = NULL;
    926 		if ((pcb = get_next_pcb(fcb->fcb_suffix)) == NULL) {
    927 			return (-1);
    928 		}
    929 		/* Place FCB into the PCB in order - oldest first.  */
    930 		fcbp = pcb->pcb_first;
    931 		fcbprev = NULL;
    932 		while (fcbp != NULL) {
    933 			if (fcb->fcb_start < fcbp->fcb_start) {
    934 				if (fcbprev)
    935 					fcbprev->fcb_next = fcb;
    936 				else
    937 					pcb->pcb_dfirst = pcb->pcb_first = fcb;
    938 				fcb->fcb_next = fcbp;
    939 				break;
    940 			}
    941 			fcbprev = fcbp;
    942 			fcbp = fcbp->fcb_next;
    943 		}
    944 		/* younger than all || empty list */
    945 		if (!fcb->fcb_next) {
    946 			if (pcb->pcb_first == NULL)
    947 				pcb->pcb_dfirst = pcb->pcb_first = fcb;
    948 			pcb->pcb_dlast = pcb->pcb_last = fcb;
    949 			if (fcbprev)
    950 				fcbprev->fcb_next = fcb;
    951 		}
    952 	} else {
    953 		free((char *)fcb);	/* rejected */
    954 	}
    955 	return (0);
    956 }
    957 
    958 
    959 /*
    960  * .func	check_file - check filename and setup fcb.
    961  * .desc	Check adherence to the file format (do_check is TRUE) and setup
    962  *	the fcb with useful information.
    963  *	filename format: yyyymmddhhmmss.yyyymmddhhmmss.suffix
    964  *			 yyyymmddhhmmss.not_terminated.suffix
    965  *	If do_check is FALSE then still see if the filename does confirm
    966  *	to the format. If it does then extract useful information from
    967  *	it (start time and end time).  But if it doesn't then don't print
    968  *	any error messages.
    969  * .call	ret = check_file(fcb, do_check).
    970  * .arg	fcb	- ptr to fcb that holds the file.
    971  * .arg	do_check - if TRUE do check adherence to file format.
    972  * .ret	0	- no errors detected.
    973  * .ret	-1	- file failed somehow (error_str tells why).
    974  */
    975 int
    976 check_file(audit_fcb_t *fcb, int do_check)
    977 {
    978 	int	ret;
    979 	char	*namep, *slp;
    980 	char	errb[256];		/* build error message */
    981 	struct tm tme;
    982 
    983 	errb[0] = '\0';
    984 	/* get just the filename */
    985 	for (slp = namep = fcb->fcb_file; *namep; namep++) {
    986 		if (*namep == '/')
    987 			slp = namep + 1; /* slp -> the filename itself */
    988 	}
    989 	if (do_check == FALSE) {
    990 		fcb->fcb_end = MAXLONG;		/* forever */
    991 		fcb->fcb_suffix = NULL;
    992 		fcb->fcb_name = slp;
    993 		ret = 0;
    994 	} else {
    995 		ret = -1;
    996 	}
    997 	if ((int)strlen(slp) < 31) {
    998 		(void) sprintf(errbuf, gettext("filename too short (%d)"),
    999 		    strlen(slp));
   1000 		error_str = errbuf;
   1001 		return (ret);
   1002 	}
   1003 	/*
   1004 	 * Get working copy of filename.
   1005 	 */
   1006 	namep = (char *)a_calloc(1, strlen(slp) + 1);
   1007 	(void) strcpy(namep, slp);
   1008 	if (namep[14] != '.' || namep[29] != '.') {
   1009 		(void) sprintf(errbuf,
   1010 		    gettext("invalid filename format (%c or %c)"), namep[14],
   1011 		    namep[29]);
   1012 		error_str = errbuf;
   1013 		free(namep);
   1014 		return (ret);
   1015 	}
   1016 	namep[14] = '\0';			/* mark off start time */
   1017 	namep[29] = '\0';			/* mark off finish time */
   1018 	if (derive_date(namep, &tme)) {
   1019 		(void) strcat(errb, gettext("starting time-stamp invalid - "));
   1020 		(void) strcat(errb, error_str);
   1021 		(void) strcpy(errbuf, errb);
   1022 		error_str = errbuf;
   1023 		free(namep);
   1024 		return (ret);
   1025 	}
   1026 	/*
   1027 	 * Keep start time from filename. Use it to order files in
   1028 	 * the file list. Later we will update this when we read
   1029 	 * the first record from the file.
   1030 	 */
   1031 	fcb->fcb_start = tm_to_secs(&tme);
   1032 
   1033 	if (strcmp(&namep[15], "not_terminated") == 0) {
   1034 		fcb->fcb_end = MAXLONG;		/* forever */
   1035 		/*
   1036 		 * Only treat a 'not_terminated' file as such if
   1037 		 * it is not on the command line.
   1038 		 */
   1039 		if (do_check == TRUE)
   1040 			fcb->fcb_flags |= FF_NOTTERM;
   1041 	} else if (derive_date(&namep[15], &tme)) {
   1042 		(void) strcat(errb, gettext("ending time-stamp invalid - "));
   1043 		(void) strcat(errb, error_str);
   1044 		(void) strcpy(errbuf, errb);
   1045 		error_str = errbuf;
   1046 		free(namep);
   1047 		return (ret);
   1048 	} else {
   1049 		fcb->fcb_end = tm_to_secs(&tme);
   1050 	}
   1051 	fcb->fcb_name = slp;
   1052 	fcb->fcb_suffix = &slp[30];
   1053 	free(namep);
   1054 	return (0);
   1055 }
   1056 
   1057 
   1058 /*
   1059  * .func get_next_pcb - get a pcb to use.
   1060  * .desc	The pcb's in the array audit_pcbs are used to hold single file
   1061  *	groups in the form of a linked list. Each pcb holds files that
   1062  *	are tied together by a common suffix in the file name. Here we
   1063  *	get either 1. the existing pcb holding a specified sufix or
   1064  *	2. a new pcb if we can't find an existing one.
   1065  * .call	pcb = get_next_pcb(suffix).
   1066  * .arg	suffix	- ptr to suffix we are seeking.
   1067  * .ret	pcb	- ptr to pcb that hold s the sought suffix.
   1068  * .ret	NULL- serious failure in memory allocation. Quit processing.
   1069  */
   1070 audit_pcb_t *
   1071 get_next_pcb(char *suffix)
   1072 {
   1073 	int	i = 0;
   1074 	int	zerosize;
   1075 	unsigned int	size;
   1076 	audit_pcb_t *pcb;
   1077 
   1078 	/* Search through (maybe) entire array. */
   1079 	while (i < pcbsize) {
   1080 		pcb = &audit_pcbs[i++];
   1081 		if (pcb->pcb_first == NULL) {
   1082 			proc_pcb(pcb, suffix, i);
   1083 			return (pcb);	/* came to an unused one */
   1084 		}
   1085 		if (suffix) {
   1086 			if (strcmp(pcb->pcb_suffix, suffix) == 0)
   1087 				return (pcb);	/* matched one with suffix */
   1088 		}
   1089 	}
   1090 	/*
   1091 	 * Uh-oh, the entire array is used and we haven't gotten one yet.
   1092 	 * Allocate a bigger array.
   1093 	 */
   1094 	pcbsize += PCB_INC;
   1095 	size = pcbsize * sizeof (audit_pcb_t);
   1096 	zerosize = size - ((pcbsize - PCB_INC) * sizeof (audit_pcb_t));
   1097 	if ((audit_pcbs = (audit_pcb_t *)realloc((char *)audit_pcbs, size)) ==
   1098 	    NULL) {
   1099 		(void) sprintf(errbuf,
   1100 		    gettext("%s memory reallocation failed (%d bytes)"), ar,
   1101 		    size);
   1102 		perror(errbuf);
   1103 		audit_stats();		/* give user statistics on usage */
   1104 		return (NULL);		/* really bad thing to have happen */
   1105 	}
   1106 	/*
   1107 	 * Don't know if realloc clears the new memory like calloc would.
   1108 	 */
   1109 	(void) memset((void *) & audit_pcbs[pcbsize-PCB_INC], 0,
   1110 	    (size_t)zerosize);
   1111 	pcb = &audit_pcbs[pcbsize-PCB_INC];	/* allocate the first new one */
   1112 	proc_pcb(pcb, suffix, pcbsize - PCB_INC);
   1113 	return (pcb);
   1114 }
   1115 
   1116 
   1117 /*
   1118  * .func proc_pcb - process pcb.
   1119  * .desc	Common pcb processing for above routine.
   1120  * .call	proc_pcb(pcb, suffix, i).
   1121  * .arg	pcb	- ptr to pcb.
   1122  * .arg	suffix	- prt to suffix tha t ties this group together.
   1123  * .arg	i	- index into audit_pcbs[ ].
   1124  * .ret	void.
   1125  */
   1126 void
   1127 proc_pcb(audit_pcb_t *pcb, char *suffix, int i)
   1128 {
   1129 	if (suffix)
   1130 		pcb->pcb_suffix = suffix;
   1131 	pcbnum++;	/* one more pcb in use */
   1132 	pcb->pcb_size = AUDITBUFSIZE;
   1133 	pcb->pcb_rec = (char *)a_calloc(1, AUDITBUFSIZE);
   1134 	pcb->pcb_time = -1;
   1135 	pcb->pcb_flags |= PF_USEFILE;	/* note this one controls files */
   1136 	pcb->pcb_procno = i;	/* save index into audit_pcbs [] for id */
   1137 }
   1138 
   1139 
   1140 /*
   1141  * .func	proc_label - process label range argument.
   1142  * .desc	Parse label range lower-bound[;upper-bound]
   1143  * .call	ret = proc_label(optstr).
   1144  * .arg	opstr	- ptr to label range string
   1145  * .ret 0	- no errors detected.
   1146  * .ret -1	- errors detected (error_str set).
   1147  */
   1148 
   1149 int
   1150 proc_label(char *optstr)
   1151 {
   1152 	char	*p;
   1153 	int	error;
   1154 
   1155 	if (flags & M_LABEL) {
   1156 		error_str = gettext("'l' option specified multiple times");
   1157 		return (-1);
   1158 	}
   1159 	flags |= M_LABEL;
   1160 
   1161 	if ((m_label = malloc(sizeof (m_range_t))) == NULL) {
   1162 		return (-1);
   1163 	}
   1164 	m_label->lower_bound = NULL;
   1165 	m_label->upper_bound = NULL;
   1166 
   1167 	p = strchr(optstr, ';');
   1168 	if (p == NULL) {
   1169 		/* exact label match, lower and upper range bounds the same */
   1170 		if (str_to_label(optstr, &m_label->lower_bound, MAC_LABEL,
   1171 		    L_NO_CORRECTION, &error) == -1) {
   1172 			(void) sprintf(errbuf,
   1173 			    gettext("invalid sensitivity label (%s) err %d"),
   1174 			    optstr, error);
   1175 			error_str = errbuf;
   1176 			goto errout;
   1177 		}
   1178 		m_label->upper_bound = m_label->lower_bound;
   1179 		return (0);
   1180 	}
   1181 	if (p == optstr) {
   1182 		/* lower bound is not specified .. default is admin_low */
   1183 		if (str_to_label(ADMIN_LOW, &m_label->lower_bound, MAC_LABEL,
   1184 		    L_NO_CORRECTION, &error) == -1) {
   1185 			goto errout;
   1186 		}
   1187 
   1188 		p++;
   1189 		if (*p == '\0') {
   1190 			/* upper bound not specified .. default is admin_high */
   1191 			if (str_to_label(ADMIN_HIGH, &m_label->upper_bound,
   1192 			    MAC_LABEL, L_NO_CORRECTION, &error) == -1) {
   1193 				goto errout;
   1194 			}
   1195 		} else {
   1196 			if (str_to_label(p, &m_label->upper_bound, MAC_LABEL,
   1197 			    L_NO_CORRECTION, &error) == -1) {
   1198 				(void) sprintf(errbuf, gettext(
   1199 				    "invalid sensitivity label (%s) err %d"),
   1200 				    p, error);
   1201 				error_str = errbuf;
   1202 				goto errout;
   1203 			}
   1204 		}
   1205 		return (0);
   1206 	}
   1207 	*p++ = '\0';
   1208 	if (str_to_label(optstr, &m_label->lower_bound, MAC_LABEL,
   1209 	    L_NO_CORRECTION, &error) == -1) {
   1210 		(void) sprintf(errbuf,
   1211 		    gettext("invalid sensitivity label (%s) err %d"), optstr,
   1212 		    error);
   1213 		error_str = errbuf;
   1214 		goto errout;
   1215 	}
   1216 	if (*p == '\0') {
   1217 		/* upper bound is not specified .. default is admin_high */
   1218 		if (str_to_label(ADMIN_HIGH, &m_label->upper_bound,
   1219 		    MAC_LABEL, L_NO_CORRECTION, &error) == -1) {
   1220 			goto errout;
   1221 		}
   1222 	} else {
   1223 		if (str_to_label(p, &m_label->upper_bound, MAC_LABEL,
   1224 		    L_NO_CORRECTION, &error) == -1) {
   1225 			(void) sprintf(errbuf,
   1226 			    gettext("invalid sensitivity label (%s) err %d"),
   1227 			    p, error);
   1228 			error_str = errbuf;
   1229 			goto errout;
   1230 		}
   1231 	}
   1232 	/* make sure that upper bound dominates the lower bound */
   1233 	if (!bldominates(m_label->upper_bound, m_label->lower_bound)) {
   1234 		*--p = ';';
   1235 		(void) sprintf(errbuf,
   1236 		    gettext("invalid sensitivity label range (%s)"), optstr);
   1237 		error_str = errbuf;
   1238 		goto errout;
   1239 	}
   1240 	return (0);
   1241 
   1242 errout:
   1243 	m_label_free(m_label->upper_bound);
   1244 	m_label_free(m_label->lower_bound);
   1245 	free(m_label);
   1246 
   1247 	return (-1);
   1248 }
   1249 
   1250 /*
   1251  * proc_zonename - pick up zone name.
   1252  *
   1253  * all non-empty and not-too-long strings are valid since any name
   1254  * may be valid.
   1255  *
   1256  * ret 0:	non-empty string
   1257  * ret -1:	empty string or string is too long.
   1258  */
   1259 static int
   1260 proc_zonename(char *optstr)
   1261 {
   1262 	size_t	length = strlen(optstr);
   1263 	if ((length < 1) || (length > ZONENAME_MAX)) {
   1264 		(void) sprintf(errbuf,
   1265 		    gettext("invalid zone name: %s"), optstr);
   1266 		error_str = errbuf;
   1267 		return (-1);
   1268 	}
   1269 	zonename = strdup(optstr);
   1270 	flags |= M_ZONENAME;
   1271 	return (0);
   1272 }
   1273 
   1274 /*
   1275  * proc_frmi - set up frmi for pattern matching.
   1276  *	Logic ripped off of scf_walk_fmri()
   1277  *		Thanks to the smf team.
   1278  *
   1279  * ret 0:	OK
   1280  * ret -1:	error
   1281  */
   1282 static int
   1283 proc_fmri(char *optstr)
   1284 {
   1285 	if (strpbrk(optstr, "*?[") != NULL) {
   1286 		/* have a pattern to glob for */
   1287 
   1288 		fmri.sp_type = PATTERN_GLOB;
   1289 		if (optstr[0] == '*' ||
   1290 		    (strlen(optstr) >= 4 && optstr[3] == ':')) {
   1291 			fmri.sp_arg = strdup(optstr);
   1292 		} else if ((fmri.sp_arg = malloc(strlen(optstr) + 6)) != NULL) {
   1293 			(void) snprintf(fmri.sp_arg, strlen(optstr) + 6,
   1294 			    "svc:/%s", optstr);
   1295 		}
   1296 	} else {
   1297 		fmri.sp_type = PATTERN_PARTIAL;
   1298 		fmri.sp_arg = strdup(optstr);
   1299 	}
   1300 	if (fmri.sp_arg == NULL)
   1301 		return (-1);
   1302 
   1303 	return (0);
   1304 }
   1305