Home | History | Annotate | Download | only in ctstat
      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 2008 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     27 
     28 #include <sys/types.h>
     29 #include <sys/ctfs.h>
     30 #include <stdio.h>
     31 #include <stdlib.h>
     32 #include <unistd.h>
     33 #include <fcntl.h>
     34 #include <string.h>
     35 #include <errno.h>
     36 #include <libuutil.h>
     37 #include <sys/contract/process.h>
     38 #include <limits.h>
     39 #include <libcontract.h>
     40 #include <libcontract_priv.h>
     41 #include <dirent.h>
     42 
     43 #include <locale.h>
     44 #include <langinfo.h>
     45 
     46 static int opt_verbose = 0;
     47 static int opt_showall = 0;
     48 
     49 /*
     50  * usage
     51  *
     52  * Educate the user.
     53  */
     54 static void
     55 usage(void)
     56 {
     57 	(void) fprintf(stderr, gettext("Usage: %s [-a] [-i ctidlist] "
     58 	    "[-t typelist] [-v] [interval [count]]\n"), uu_getpname());
     59 	exit(UU_EXIT_USAGE);
     60 }
     61 
     62 /*
     63  * mystrtoul
     64  *
     65  * Convert a string into an int in [0, INT_MAX].  Exit if the argument
     66  * doen't fit this description.
     67  */
     68 static int
     69 mystrtoul(const char *arg)
     70 {
     71 	unsigned int result;
     72 
     73 	if (uu_strtoint(arg, &result, sizeof (result), 10, 0, INT_MAX) == -1) {
     74 		uu_warn(gettext("invalid numerical argument \"%s\"\n"), arg);
     75 		usage();
     76 	}
     77 
     78 	return (result);
     79 }
     80 
     81 /*
     82  * int_compar
     83  *
     84  * A simple integer comparator.  Also used for id_ts, since they're the
     85  * same thing.
     86  */
     87 static int
     88 int_compar(const void *a1, const void *a2)
     89 {
     90 	int id1 = *(int *)a1;
     91 	int id2 = *(int *)a2;
     92 
     93 	if (id1 > id2)
     94 		return (1);
     95 	if (id2 > id1)
     96 		return (-1);
     97 	return (0);
     98 }
     99 
    100 typedef struct optvect {
    101 	const char	*option;
    102 	uint_t		bit;
    103 } optvect_t;
    104 
    105 static optvect_t option_params[] = {
    106 	{ "inherit", CT_PR_INHERIT },
    107 	{ "noorphan", CT_PR_NOORPHAN },
    108 	{ "pgrponly", CT_PR_PGRPONLY },
    109 	{ "regent", CT_PR_REGENT },
    110 	{ NULL }
    111 };
    112 
    113 static optvect_t option_events[] = {
    114 	{ "core", CT_PR_EV_CORE },
    115 	{ "signal", CT_PR_EV_SIGNAL },
    116 	{ "hwerr", CT_PR_EV_HWERR },
    117 	{ "empty", CT_PR_EV_EMPTY },
    118 	{ "fork", CT_PR_EV_FORK },
    119 	{ "exit", CT_PR_EV_EXIT },
    120 	{ NULL }
    121 };
    122 
    123 /*
    124  * print_bits
    125  *
    126  * Display a set whose membership is identified by a bitfield.
    127  */
    128 static void
    129 print_bits(uint_t bits, optvect_t *desc)
    130 {
    131 	int i, printed = 0;
    132 
    133 	for (i = 0; desc[i].option; i++)
    134 		if (desc[i].bit & bits) {
    135 			if (printed)
    136 				(void) putchar(' ');
    137 			printed = 1;
    138 			(void) fputs(desc[i].option, stdout);
    139 		}
    140 	if (printed)
    141 		(void) putchar('\n');
    142 	else
    143 		(void) puts("none");
    144 }
    145 
    146 /*
    147  * print_ids
    148  *
    149  * Display a list of ids, sorted.
    150  */
    151 static void
    152 print_ids(id_t *ids, uint_t nids)
    153 {
    154 	int i;
    155 	int first = 1;
    156 
    157 	qsort(ids, nids, sizeof (int), int_compar);
    158 
    159 	for (i = 0; i < nids; i++) {
    160 		/*LINTED*/
    161 		(void) printf(" %d" + first, ids[i]);
    162 		first = 0;
    163 	}
    164 	if (first)
    165 		(void) puts("none");
    166 	else
    167 		(void) putchar('\n');
    168 }
    169 
    170 typedef void printfunc_t(ct_stathdl_t);
    171 
    172 /*
    173  * A structure defining a displayed field.  Includes a label to be
    174  * printed along side the field value, and a function which extracts
    175  * the data from a status structure, formats it, and displays it on
    176  * stdout.
    177  */
    178 typedef struct verbout {
    179 	const char	*label;	/* field label */
    180 	printfunc_t	*func;	/* field display function */
    181 } verbout_t;
    182 
    183 /*
    184  * verb_cookie
    185  *
    186  * Used to display an error encountered when reading a contract status
    187  * field.
    188  */
    189 static void
    190 verb_error(int err)
    191 {
    192 	(void) printf("(error: %s)\n", strerror(err));
    193 }
    194 
    195 /*
    196  * verb_cookie
    197  *
    198  * Display the contract's cookie.
    199  */
    200 static void
    201 verb_cookie(ct_stathdl_t hdl)
    202 {
    203 	(void) printf("%#llx\n", ct_status_get_cookie(hdl));
    204 }
    205 
    206 /*
    207  * verb_info
    208  *
    209  * Display the parameters in the parameter set.
    210  */
    211 static void
    212 verb_param(ct_stathdl_t hdl)
    213 {
    214 	uint_t param;
    215 	int err;
    216 
    217 	if (err = ct_pr_status_get_param(hdl, &param))
    218 		verb_error(err);
    219 	else
    220 		print_bits(param, option_params);
    221 }
    222 
    223 /*
    224  * verb_info
    225  *
    226  * Display the events in the informative event set.
    227  */
    228 static void
    229 verb_info(ct_stathdl_t hdl)
    230 {
    231 	print_bits(ct_status_get_informative(hdl), option_events);
    232 }
    233 
    234 /*
    235  * verb_crit
    236  *
    237  * Display the events in the critical event set.
    238  */
    239 static void
    240 verb_crit(ct_stathdl_t hdl)
    241 {
    242 	print_bits(ct_status_get_critical(hdl), option_events);
    243 }
    244 
    245 /*
    246  * verb_fatal
    247  *
    248  * Display the events in the fatal event set.
    249  */
    250 static void
    251 verb_fatal(ct_stathdl_t hdl)
    252 {
    253 	uint_t event;
    254 	int err;
    255 
    256 	if (err = ct_pr_status_get_fatal(hdl, &event))
    257 		verb_error(err);
    258 	else
    259 		print_bits(event, option_events);
    260 }
    261 
    262 /*
    263  * verb_members
    264  *
    265  * Display the list of member contracts.
    266  */
    267 static void
    268 verb_members(ct_stathdl_t hdl)
    269 {
    270 	pid_t *pids;
    271 	uint_t npids;
    272 	int err;
    273 
    274 	if (err = ct_pr_status_get_members(hdl, &pids, &npids)) {
    275 		verb_error(err);
    276 		return;
    277 	}
    278 
    279 	print_ids(pids, npids);
    280 }
    281 
    282 /*
    283  * verb_inherit
    284  *
    285  * Display the list of inherited contracts.
    286  */
    287 static void
    288 verb_inherit(ct_stathdl_t hdl)
    289 {
    290 	ctid_t *ctids;
    291 	uint_t nctids;
    292 	int err;
    293 
    294 	if (err = ct_pr_status_get_contracts(hdl, &ctids, &nctids))
    295 		verb_error(err);
    296 	else
    297 		print_ids(ctids, nctids);
    298 }
    299 
    300 /*
    301  * verb_svc_fmri
    302  *
    303  * Display the process contract service fmri
    304  */
    305 static void
    306 verb_svc_fmri(ct_stathdl_t hdl)
    307 {
    308 	char *svc_fmri;
    309 	int err;
    310 	if (err = ct_pr_status_get_svc_fmri(hdl, &svc_fmri))
    311 		verb_error(err);
    312 	else
    313 		(void) printf("%s\n", svc_fmri);
    314 }
    315 
    316 /*
    317  * verb_svc_aux
    318  *
    319  * Display the process contract service fmri auxiliar
    320  */
    321 static void
    322 verb_svc_aux(ct_stathdl_t hdl)
    323 {
    324 	char *svc_aux;
    325 	int err;
    326 	if (err = ct_pr_status_get_svc_aux(hdl, &svc_aux))
    327 		verb_error(err);
    328 	else
    329 		(void) printf("%s\n", svc_aux);
    330 }
    331 
    332 /*
    333  * verb_svc_ctid
    334  *
    335  * Display the process contract service fmri ctid
    336  */
    337 static void
    338 verb_svc_ctid(ct_stathdl_t hdl)
    339 {
    340 	ctid_t svc_ctid;
    341 	int err;
    342 	if (err = ct_pr_status_get_svc_ctid(hdl, &svc_ctid))
    343 		verb_error(err);
    344 	else
    345 		(void) printf("%ld\n", svc_ctid);
    346 }
    347 
    348 /*
    349  * verb_svc_creator
    350  *
    351  * Display the process contract creator's execname
    352  */
    353 static void
    354 verb_svc_creator(ct_stathdl_t hdl)
    355 {
    356 	char *svc_creator;
    357 	int err;
    358 	if (err = ct_pr_status_get_svc_creator(hdl, &svc_creator))
    359 		verb_error(err);
    360 	else
    361 		(void) printf("%s\n", svc_creator);
    362 }
    363 
    364 /*
    365  * Common contract status fields.
    366  */
    367 static verbout_t vcommon[] = {
    368 	"cookie", verb_cookie,
    369 	NULL,
    370 };
    371 
    372 /*
    373  * Process contract-specific status fields.
    374  * The critical and informative event sets are here because the event
    375  * names are contract-specific.  They are listed first, however, so
    376  * they are displayed adjacent to the "normal" common output.
    377  */
    378 static verbout_t vprocess[] = {
    379 	"informative event set", verb_info,
    380 	"critical event set", verb_crit,
    381 	"fatal event set", verb_fatal,
    382 	"parameter set", verb_param,
    383 	"member processes", verb_members,
    384 	"inherited contracts", verb_inherit,
    385 	"service fmri", verb_svc_fmri,
    386 	"service fmri ctid", verb_svc_ctid,
    387 	"creator", verb_svc_creator,
    388 	"aux", verb_svc_aux,
    389 	NULL
    390 };
    391 
    392 /*
    393  * print_verbose
    394  *
    395  * Displays a contract's verbose status, common fields first.
    396  */
    397 static void
    398 print_verbose(ct_stathdl_t hdl, verbout_t *spec, verbout_t *common)
    399 {
    400 	int i;
    401 	int tmp, maxwidth = 0;
    402 
    403 	/*
    404 	 * Compute the width of all the fields.
    405 	 */
    406 	for (i = 0; common[i].label; i++)
    407 		if ((tmp = strlen(common[i].label)) > maxwidth)
    408 			maxwidth = tmp;
    409 	if (spec)
    410 		for (i = 0; spec[i].label; i++)
    411 			if ((tmp = strlen(spec[i].label)) > maxwidth)
    412 				maxwidth = tmp;
    413 	maxwidth += 2;
    414 
    415 	/*
    416 	 * Display the data.
    417 	 */
    418 	for (i = 0; common[i].label; i++) {
    419 		tmp = printf("\t%s", common[i].label);
    420 		if (tmp < 0)
    421 			tmp = 0;
    422 		(void) printf("%-*s", maxwidth - tmp + 1, ":");
    423 		common[i].func(hdl);
    424 	}
    425 	if (spec)
    426 		for (i = 0; spec[i].label; i++) {
    427 			(void) printf("\t%s%n", spec[i].label, &tmp);
    428 			(void) printf("%-*s", maxwidth - tmp + 1, ":");
    429 			spec[i].func(hdl);
    430 		}
    431 }
    432 
    433 struct {
    434 	const char *name;
    435 	verbout_t *verbout;
    436 } cttypes[] = {
    437 	{ "process", vprocess },
    438 	{ NULL }
    439 };
    440 
    441 /*
    442  * get_type
    443  *
    444  * Given a type name, return an index into the above array of types.
    445  */
    446 static int
    447 get_type(const char *typestr)
    448 {
    449 	int i;
    450 	for (i = 0; cttypes[i].name; i++)
    451 		if (strcmp(cttypes[i].name, typestr) == 0)
    452 			return (i);
    453 	uu_die(gettext("invalid contract type: %s\n"), typestr);
    454 	/* NOTREACHED */
    455 }
    456 
    457 /*
    458  * print_header
    459  *
    460  * Display the status header.
    461  */
    462 static void
    463 print_header(void)
    464 {
    465 	(void) printf("%-8s%-8s%-8s%-8s%-8s%-8s%-8s%-8s\n", "CTID", "ZONEID",
    466 	    "TYPE", "STATE", "HOLDER", "EVENTS", "QTIME", "NTIME");
    467 }
    468 
    469 /*
    470  * print_contract
    471  *
    472  * Display status for contract ID 'id' from type directory 'dir'.  If
    473  * only contracts of a specific set of types should be displayed,
    474  * 'types' will be a sorted list of type indices of length 'ntypes'.
    475  */
    476 static void
    477 print_contract(const char *dir, ctid_t id, verbout_t *spec,
    478     int *types, int ntypes)
    479 {
    480 	ct_stathdl_t status;
    481 	char hstr[100], qstr[20], nstr[20];
    482 	ctstate_t state;
    483 	int fd = 0;
    484 	int t;
    485 
    486 	/*
    487 	 * Open and obtain status.
    488 	 */
    489 	if ((fd = contract_open(id, dir, "status", O_RDONLY)) == -1) {
    490 		if (errno == ENOENT)
    491 			return;
    492 		uu_die(gettext("could not open contract status file"));
    493 	}
    494 
    495 	if (errno = ct_status_read(fd, opt_verbose ? CTD_ALL : CTD_COMMON,
    496 	    &status))
    497 		uu_die(gettext("failed to get contract status for %d"), id);
    498 	(void) close(fd);
    499 
    500 	/*
    501 	 * Unless otherwise directed, don't display dead contracts.
    502 	 */
    503 	state = ct_status_get_state(status);
    504 	if (!opt_showall && state == CTS_DEAD) {
    505 		ct_status_free(status);
    506 		return;
    507 	}
    508 
    509 	/*
    510 	 * If we are only allowed to display certain contract types,
    511 	 * perform that filtering here.  We stash a copy of spec so we
    512 	 * don't have to recompute it later.
    513 	 */
    514 	if (types) {
    515 		int key = get_type(ct_status_get_type(status));
    516 		spec = cttypes[key].verbout;
    517 		if (bsearch(&key, types, ntypes, sizeof (int), int_compar) ==
    518 		    NULL) {
    519 			ct_status_free(status);
    520 			return;
    521 		}
    522 	}
    523 
    524 	/*
    525 	 * Precompute those fields which have both textual and
    526 	 * numerical values.
    527 	 */
    528 	if ((state == CTS_OWNED) || (state == CTS_INHERITED))
    529 		(void) snprintf(hstr, sizeof (hstr), "%ld",
    530 		    ct_status_get_holder(status));
    531 	else
    532 		(void) snprintf(hstr, sizeof (hstr), "%s", "-");
    533 
    534 	if ((t = ct_status_get_qtime(status)) == -1) {
    535 		qstr[0] = nstr[0] = '-';
    536 		qstr[1] = nstr[1] = '\0';
    537 	} else {
    538 		(void) snprintf(qstr, sizeof (qstr), "%d", t);
    539 		(void) snprintf(nstr, sizeof (nstr), "%d",
    540 		    ct_status_get_ntime(status));
    541 	}
    542 
    543 	/*
    544 	 * Emit the contract's status.
    545 	 */
    546 	(void) printf("%-7ld %-7ld %-7s %-7s %-7s %-7d %-7s %-8s\n",
    547 	    ct_status_get_id(status),
    548 	    ct_status_get_zoneid(status),
    549 	    ct_status_get_type(status),
    550 	    (state == CTS_OWNED) ? "owned" :
    551 	    (state == CTS_INHERITED) ? "inherit" :
    552 	    (state == CTS_ORPHAN) ? "orphan" : "dead", hstr,
    553 	    ct_status_get_nevents(status), qstr, nstr);
    554 
    555 	/*
    556 	 * Emit verbose status information, if requested.  If we
    557 	 * weren't provided a verbose output spec or didn't compute it
    558 	 * earlier, do it now.
    559 	 */
    560 	if (opt_verbose) {
    561 		if (spec == NULL)
    562 			spec = cttypes[get_type(ct_status_get_type(status))].
    563 			    verbout;
    564 		print_verbose(status, spec, vcommon);
    565 	}
    566 
    567 	ct_status_free(status);
    568 }
    569 
    570 /*
    571  * scan_type
    572  *
    573  * Display all contracts of the requested type.
    574  */
    575 static void
    576 scan_type(int typeno)
    577 {
    578 	DIR *dir;
    579 	struct dirent64 *de;
    580 	char path[PATH_MAX];
    581 
    582 	verbout_t *vo = cttypes[typeno].verbout;
    583 	const char *type = cttypes[typeno].name;
    584 
    585 	if (snprintf(path, PATH_MAX, CTFS_ROOT "/%s", type) >= PATH_MAX ||
    586 	    (dir = opendir(path)) == NULL)
    587 		uu_die(gettext("bad contract type: %s\n"), type);
    588 	while ((de = readdir64(dir)) != NULL) {
    589 		/*
    590 		 * Eliminate special files (e.g. '.', '..').
    591 		 */
    592 		if (de->d_name[0] < '0' || de->d_name[0] > '9')
    593 			continue;
    594 		print_contract(type, mystrtoul(de->d_name), vo, NULL, 0);
    595 	}
    596 	(void) closedir(dir);
    597 }
    598 
    599 /*
    600  * scan_ids
    601  *
    602  * Display all contracts with the requested IDs.
    603  */
    604 static void
    605 scan_ids(ctid_t *ids, int nids)
    606 {
    607 	int i;
    608 	for (i = 0; i < nids; i++)
    609 		print_contract("all", ids[i], NULL, NULL, 0);
    610 }
    611 
    612 /*
    613  * scan_all
    614  *
    615  * Display the union of the requested IDs and types.  So that the
    616  * output is sorted by contract ID, it takes the slow road by testing
    617  * each entry in /system/contract/all against its criteria.  Used when
    618  * the number of types is greater than 1, when we have a mixture of
    619  * types and ids, or no lists were provided at all.
    620  */
    621 static void
    622 scan_all(int *types, int ntypes, ctid_t *ids, int nids)
    623 {
    624 	DIR *dir;
    625 	struct dirent64 *de;
    626 	const char *path = CTFS_ROOT "/all";
    627 	int key, test;
    628 
    629 	if ((dir = opendir(path)) == NULL)
    630 		uu_die(gettext("could not open %s"), path);
    631 	while ((de = readdir64(dir)) != NULL) {
    632 		/*
    633 		 * Eliminate special files (e.g. '.', '..').
    634 		 */
    635 		if (de->d_name[0] < '0' || de->d_name[0] > '9')
    636 			continue;
    637 		key = mystrtoul(de->d_name);
    638 
    639 		/*
    640 		 * If we are given IDs to look at and this contract
    641 		 * isn't in the ID list, or if we weren't given a list
    642 		 * if IDs but were given a list of types, provide the
    643 		 * list of acceptable types to print_contract.
    644 		 */
    645 		test = nids ? (bsearch(&key, ids, nids, sizeof (int),
    646 		    int_compar) == NULL) : (ntypes != 0);
    647 		print_contract("all", key, NULL, (test ? types : NULL), ntypes);
    648 	}
    649 	(void) closedir(dir);
    650 }
    651 
    652 /*
    653  * walk_args
    654  *
    655  * Apply fp to each token in the comma- or space- separated argument
    656  * string str and store the results in the array starting at results.
    657  */
    658 static int
    659 walk_args(const char *str, int (*fp)(const char *), int *results)
    660 {
    661 	char *copy, *token;
    662 	int count = 0;
    663 
    664 	if ((copy = strdup(str)) == NULL)
    665 		uu_die(gettext("strdup() failed"));
    666 
    667 	token = strtok(copy, ", ");
    668 	if (token == NULL) {
    669 		free(copy);
    670 		return (0);
    671 	}
    672 
    673 	do {
    674 		if (fp)
    675 			*(results++) = fp(token);
    676 		count++;
    677 	} while (token = strtok(NULL, ", "));
    678 	free(copy);
    679 
    680 	return (count);
    681 }
    682 
    683 /*
    684  * parse
    685  *
    686  * Parse the comma- or space- separated string str, using fp to covert
    687  * the tokens to integers.  Append the list of integers to the array
    688  * pointed to by *idps, growing the array if necessary.
    689  */
    690 static int
    691 parse(const char *str, int **idsp, int nids, int (*fp)(const char *fp))
    692 {
    693 	int count;
    694 	int *array;
    695 
    696 	count = walk_args(str, NULL, NULL);
    697 	if (count == 0)
    698 		return (0);
    699 
    700 	if ((array = calloc(nids + count, sizeof (int))) == NULL)
    701 		uu_die(gettext("calloc() failed"));
    702 
    703 	if (*idsp) {
    704 		(void) memcpy(array, *idsp, nids * sizeof (int));
    705 		free(*idsp);
    706 	}
    707 
    708 	(void) walk_args(str, fp, array + nids);
    709 
    710 	*idsp = array;
    711 	return (count + nids);
    712 }
    713 
    714 /*
    715  * parse_ids
    716  *
    717  * Extract a list of ids from the comma- or space- separated string str
    718  * and append them to the array *idsp, growing it if necessary.
    719  */
    720 static int
    721 parse_ids(const char *arg, int **idsp, int nids)
    722 {
    723 	return (parse(arg, idsp, nids, mystrtoul));
    724 }
    725 
    726 /*
    727  * parse_types
    728  *
    729  * Extract a list of types from the comma- or space- separated string
    730  * str and append them to the array *idsp, growing it if necessary.
    731  */
    732 static int
    733 parse_types(const char *arg, int **typesp, int ntypes)
    734 {
    735 	return (parse(arg, typesp, ntypes, get_type));
    736 }
    737 
    738 /*
    739  * compact
    740  *
    741  * Sorts and removes duplicates from array.  Initial size of array is
    742  * in *size; final size is stored in *size.
    743  */
    744 static void
    745 compact(int *array, int *size)
    746 {
    747 	int i, j, last = -1;
    748 
    749 	qsort(array, *size, sizeof (int), int_compar);
    750 	for (i = j = 0; i < *size; i++) {
    751 		if (array[i] != last) {
    752 			last = array[i];
    753 			array[j++] = array[i];
    754 		}
    755 	}
    756 	*size = j;
    757 }
    758 
    759 int
    760 main(int argc, char **argv)
    761 {
    762 	unsigned int interval = 0, count = 1;
    763 	ctid_t	*ids = NULL;
    764 	int	*types = NULL;
    765 	int	nids = 0, ntypes = 0;
    766 	int	i, s;
    767 
    768 	(void) setlocale(LC_ALL, "");
    769 	(void) textdomain(TEXT_DOMAIN);
    770 
    771 	(void) uu_setpname(argv[0]);
    772 
    773 	while ((s = getopt(argc, argv, "ai:t:v")) != EOF) {
    774 		switch (s) {
    775 		case 'a':
    776 			opt_showall = 1;
    777 			break;
    778 		case 'i':
    779 			nids = parse_ids(optarg, (int **)&ids, nids);
    780 			break;
    781 		case 't':
    782 			ntypes = parse_types(optarg, &types, ntypes);
    783 			break;
    784 		case 'v':
    785 			opt_verbose = 1;
    786 			break;
    787 		default:
    788 			usage();
    789 		}
    790 	}
    791 
    792 	argc -= optind;
    793 	argv += optind;
    794 
    795 	if (argc > 2 || argc < 0)
    796 		usage();
    797 
    798 	if (argc > 0) {
    799 		interval = mystrtoul(argv[0]);
    800 		count = 0;
    801 	}
    802 
    803 	if (argc > 1) {
    804 		count = mystrtoul(argv[1]);
    805 		if (count == 0)
    806 			return (0);
    807 	}
    808 
    809 	if (nids)
    810 		compact((int *)ids, &nids);
    811 	if (ntypes)
    812 		compact(types, &ntypes);
    813 
    814 	for (i = 0; count == 0 || i < count; i++) {
    815 		if (i)
    816 			(void) sleep(interval);
    817 		print_header();
    818 		if (nids && ntypes)
    819 			scan_all(types, ntypes, ids, nids);
    820 		else if (ntypes == 1)
    821 			scan_type(*types);
    822 		else if (nids)
    823 			scan_ids(ids, nids);
    824 		else
    825 			scan_all(types, ntypes, ids, nids);
    826 	}
    827 
    828 	return (0);
    829 }
    830