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