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]
22 * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
25 #include <sys/types.h>
34 #include <sys/contract/process.h>
35 #include <sys/contract/device.h>
37 #include <libcontract.h>
38 #include <libcontract_priv.h>
44 #include "statcommon.h"
46 static uint_t timestamp_fmt
= NODATE
;
48 static int opt_verbose
= 0;
49 static int opt_showall
= 0;
59 (void) fprintf(stderr
, gettext("Usage: %s [-a] [-i ctidlist] "
60 "[-t typelist] [-T d|u] [-v] [interval [count]]\n"), uu_getpname());
67 * Convert a string into an int in [0, INT_MAX]. Exit if the argument
68 * doen't fit this description.
71 mystrtoul(const char *arg
)
75 if (uu_strtoint(arg
, &result
, sizeof (result
), 10, 0, INT_MAX
) == -1) {
76 uu_warn(gettext("invalid numerical argument \"%s\"\n"), arg
);
86 * A simple integer comparator. Also used for id_ts, since they're the
90 int_compar(const void *a1
, const void *a2
)
102 typedef struct optvect
{
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
},
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
},
128 * Display a set whose membership is identified by a bitfield.
131 print_bits(uint_t bits
, optvect_t
*desc
)
135 for (i
= 0; desc
[i
].option
; i
++)
136 if (desc
[i
].bit
& bits
) {
140 (void) fputs(desc
[i
].option
, stdout
);
143 (void) putchar('\n');
151 * Display a list of ids, sorted.
154 print_ids(id_t
*ids
, uint_t nids
)
159 qsort(ids
, nids
, sizeof (int), int_compar
);
161 for (i
= 0; i
< nids
; i
++) {
163 (void) printf(" %d" + first
, ids
[i
]);
169 (void) putchar('\n');
172 typedef void printfunc_t(ct_stathdl_t
);
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
180 typedef struct verbout
{
181 const char *label
; /* field label */
182 printfunc_t
*func
; /* field display function */
188 * Used to display an error encountered when reading a contract status
194 (void) printf("(error: %s)\n", strerror(err
));
200 * Display the contract's cookie.
203 verb_cookie(ct_stathdl_t hdl
)
205 (void) printf("%#llx\n", ct_status_get_cookie(hdl
));
211 * Display the parameters in the parameter set.
214 verb_param(ct_stathdl_t hdl
)
219 if (err
= ct_pr_status_get_param(hdl
, ¶m
))
222 print_bits(param
, option_params
);
228 * Display the events in the informative event set.
231 verb_info(ct_stathdl_t hdl
)
233 print_bits(ct_status_get_informative(hdl
), option_events
);
239 * Display the events in the critical event set.
242 verb_crit(ct_stathdl_t hdl
)
244 print_bits(ct_status_get_critical(hdl
), option_events
);
250 * Display the minor device
253 verb_minor(ct_stathdl_t hdl
)
258 if (err
= ct_dev_status_get_minor(hdl
, &buf
))
261 (void) printf("%s\n", buf
);
267 * Display the state of the device
270 verb_dev_state(ct_stathdl_t hdl
)
275 if (err
= ct_dev_status_get_dev_state(hdl
, &state
))
278 (void) printf("%s\n", state
== CT_DEV_EV_ONLINE
? "online" :
279 state
== CT_DEV_EV_DEGRADED
? "degraded" : "offline");
285 * Display the events in the fatal event set.
288 verb_fatal(ct_stathdl_t hdl
)
293 if (err
= ct_pr_status_get_fatal(hdl
, &event
))
296 print_bits(event
, option_events
);
302 * Display the list of member contracts.
305 verb_members(ct_stathdl_t hdl
)
311 if (err
= ct_pr_status_get_members(hdl
, &pids
, &npids
)) {
316 print_ids(pids
, npids
);
322 * Display the list of inherited contracts.
325 verb_inherit(ct_stathdl_t hdl
)
331 if (err
= ct_pr_status_get_contracts(hdl
, &ctids
, &nctids
))
334 print_ids(ctids
, nctids
);
340 * Display the process contract service fmri
343 verb_svc_fmri(ct_stathdl_t hdl
)
347 if (err
= ct_pr_status_get_svc_fmri(hdl
, &svc_fmri
))
350 (void) printf("%s\n", svc_fmri
);
356 * Display the process contract service fmri auxiliar
359 verb_svc_aux(ct_stathdl_t hdl
)
363 if (err
= ct_pr_status_get_svc_aux(hdl
, &svc_aux
))
366 (void) printf("%s\n", svc_aux
);
372 * Display the process contract service fmri ctid
375 verb_svc_ctid(ct_stathdl_t hdl
)
379 if (err
= ct_pr_status_get_svc_ctid(hdl
, &svc_ctid
))
382 (void) printf("%ld\n", svc_ctid
);
388 * Display the process contract creator's execname
391 verb_svc_creator(ct_stathdl_t hdl
)
395 if (err
= ct_pr_status_get_svc_creator(hdl
, &svc_creator
))
398 (void) printf("%s\n", svc_creator
);
402 * Common contract status fields.
404 static verbout_t vcommon
[] = {
405 "cookie", verb_cookie
,
410 * Process contract-specific status fields.
411 * The critical and informative event sets are here because the event
412 * names are contract-specific. They are listed first, however, so
413 * they are displayed adjacent to the "normal" common output.
415 static verbout_t vprocess
[] = {
416 "informative event set", verb_info
,
417 "critical event set", verb_crit
,
418 "fatal event set", verb_fatal
,
419 "parameter set", verb_param
,
420 "member processes", verb_members
,
421 "inherited contracts", verb_inherit
,
422 "service fmri", verb_svc_fmri
,
423 "service fmri ctid", verb_svc_ctid
,
424 "creator", verb_svc_creator
,
429 static verbout_t vdevice
[] = {
430 "device", verb_minor
,
431 "dev_state", verb_dev_state
,
438 * Displays a contract's verbose status, common fields first.
441 print_verbose(ct_stathdl_t hdl
, verbout_t
*spec
, verbout_t
*common
)
444 int tmp
, maxwidth
= 0;
447 * Compute the width of all the fields.
449 for (i
= 0; common
[i
].label
; i
++)
450 if ((tmp
= strlen(common
[i
].label
)) > maxwidth
)
453 for (i
= 0; spec
[i
].label
; i
++)
454 if ((tmp
= strlen(spec
[i
].label
)) > maxwidth
)
461 for (i
= 0; common
[i
].label
; i
++) {
462 tmp
= printf("\t%s", common
[i
].label
);
465 (void) printf("%-*s", maxwidth
- tmp
+ 1, ":");
469 for (i
= 0; spec
[i
].label
; i
++) {
470 (void) printf("\t%s%n", spec
[i
].label
, &tmp
);
471 (void) printf("%-*s", maxwidth
- tmp
+ 1, ":");
480 { "process", vprocess
},
481 { "device", vdevice
},
488 * Given a type name, return an index into the above array of types.
491 get_type(const char *typestr
)
494 for (i
= 0; cttypes
[i
].name
; i
++)
495 if (strcmp(cttypes
[i
].name
, typestr
) == 0)
497 uu_die(gettext("invalid contract type: %s\n"), typestr
);
504 * Display the status header.
509 (void) printf("%-8s%-8s%-8s%-8s%-8s%-8s%-8s%-8s\n", "CTID", "ZONEID",
510 "TYPE", "STATE", "HOLDER", "EVENTS", "QTIME", "NTIME");
516 * Display status for contract ID 'id' from type directory 'dir'. If
517 * only contracts of a specific set of types should be displayed,
518 * 'types' will be a sorted list of type indices of length 'ntypes'.
521 print_contract(const char *dir
, ctid_t id
, verbout_t
*spec
,
522 int *types
, int ntypes
)
525 char hstr
[100], qstr
[20], nstr
[20];
531 * Open and obtain status.
533 if ((fd
= contract_open(id
, dir
, "status", O_RDONLY
)) == -1) {
536 uu_die(gettext("could not open contract status file"));
539 if (errno
= ct_status_read(fd
, opt_verbose
? CTD_ALL
: CTD_COMMON
,
541 uu_die(gettext("failed to get contract status for %d"), id
);
545 * Unless otherwise directed, don't display dead contracts.
547 state
= ct_status_get_state(status
);
548 if (!opt_showall
&& state
== CTS_DEAD
) {
549 ct_status_free(status
);
554 * If we are only allowed to display certain contract types,
555 * perform that filtering here. We stash a copy of spec so we
556 * don't have to recompute it later.
559 int key
= get_type(ct_status_get_type(status
));
560 spec
= cttypes
[key
].verbout
;
561 if (bsearch(&key
, types
, ntypes
, sizeof (int), int_compar
) ==
563 ct_status_free(status
);
569 * Precompute those fields which have both textual and
572 if ((state
== CTS_OWNED
) || (state
== CTS_INHERITED
))
573 (void) snprintf(hstr
, sizeof (hstr
), "%ld",
574 ct_status_get_holder(status
));
576 (void) snprintf(hstr
, sizeof (hstr
), "%s", "-");
578 if ((t
= ct_status_get_qtime(status
)) == -1) {
579 qstr
[0] = nstr
[0] = '-';
580 qstr
[1] = nstr
[1] = '\0';
582 (void) snprintf(qstr
, sizeof (qstr
), "%d", t
);
583 (void) snprintf(nstr
, sizeof (nstr
), "%d",
584 ct_status_get_ntime(status
));
588 * Emit the contract's status.
590 (void) printf("%-7ld %-7ld %-7s %-7s %-7s %-7d %-7s %-8s\n",
591 ct_status_get_id(status
),
592 ct_status_get_zoneid(status
),
593 ct_status_get_type(status
),
594 (state
== CTS_OWNED
) ? "owned" :
595 (state
== CTS_INHERITED
) ? "inherit" :
596 (state
== CTS_ORPHAN
) ? "orphan" : "dead", hstr
,
597 ct_status_get_nevents(status
), qstr
, nstr
);
600 * Emit verbose status information, if requested. If we
601 * weren't provided a verbose output spec or didn't compute it
602 * earlier, do it now.
606 spec
= cttypes
[get_type(ct_status_get_type(status
))].
608 print_verbose(status
, spec
, vcommon
);
611 ct_status_free(status
);
617 * Display all contracts of the requested type.
620 scan_type(int typeno
)
626 verbout_t
*vo
= cttypes
[typeno
].verbout
;
627 const char *type
= cttypes
[typeno
].name
;
629 if (snprintf(path
, PATH_MAX
, CTFS_ROOT
"/%s", type
) >= PATH_MAX
||
630 (dir
= opendir(path
)) == NULL
)
631 uu_die(gettext("bad contract type: %s\n"), type
);
632 while ((de
= readdir64(dir
)) != NULL
) {
634 * Eliminate special files (e.g. '.', '..').
636 if (de
->d_name
[0] < '0' || de
->d_name
[0] > '9')
638 print_contract(type
, mystrtoul(de
->d_name
), vo
, NULL
, 0);
640 (void) closedir(dir
);
646 * Display all contracts with the requested IDs.
649 scan_ids(ctid_t
*ids
, int nids
)
652 for (i
= 0; i
< nids
; i
++)
653 print_contract("all", ids
[i
], NULL
, NULL
, 0);
659 * Display the union of the requested IDs and types. So that the
660 * output is sorted by contract ID, it takes the slow road by testing
661 * each entry in /system/contract/all against its criteria. Used when
662 * the number of types is greater than 1, when we have a mixture of
663 * types and ids, or no lists were provided at all.
666 scan_all(int *types
, int ntypes
, ctid_t
*ids
, int nids
)
670 const char *path
= CTFS_ROOT
"/all";
673 if ((dir
= opendir(path
)) == NULL
)
674 uu_die(gettext("could not open %s"), path
);
675 while ((de
= readdir64(dir
)) != NULL
) {
677 * Eliminate special files (e.g. '.', '..').
679 if (de
->d_name
[0] < '0' || de
->d_name
[0] > '9')
681 key
= mystrtoul(de
->d_name
);
684 * If we are given IDs to look at and this contract
685 * isn't in the ID list, or if we weren't given a list
686 * if IDs but were given a list of types, provide the
687 * list of acceptable types to print_contract.
689 test
= nids
? (bsearch(&key
, ids
, nids
, sizeof (int),
690 int_compar
) == NULL
) : (ntypes
!= 0);
691 print_contract("all", key
, NULL
, (test
? types
: NULL
), ntypes
);
693 (void) closedir(dir
);
699 * Apply fp to each token in the comma- or space- separated argument
700 * string str and store the results in the array starting at results.
703 walk_args(const char *str
, int (*fp
)(const char *), int *results
)
708 if ((copy
= strdup(str
)) == NULL
)
709 uu_die(gettext("strdup() failed"));
711 token
= strtok(copy
, ", ");
719 *(results
++) = fp(token
);
721 } while (token
= strtok(NULL
, ", "));
730 * Parse the comma- or space- separated string str, using fp to covert
731 * the tokens to integers. Append the list of integers to the array
732 * pointed to by *idps, growing the array if necessary.
735 parse(const char *str
, int **idsp
, int nids
, int (*fp
)(const char *fp
))
740 count
= walk_args(str
, NULL
, NULL
);
744 if ((array
= calloc(nids
+ count
, sizeof (int))) == NULL
)
745 uu_die(gettext("calloc() failed"));
748 (void) memcpy(array
, *idsp
, nids
* sizeof (int));
752 (void) walk_args(str
, fp
, array
+ nids
);
755 return (count
+ nids
);
761 * Extract a list of ids from the comma- or space- separated string str
762 * and append them to the array *idsp, growing it if necessary.
765 parse_ids(const char *arg
, int **idsp
, int nids
)
767 return (parse(arg
, idsp
, nids
, mystrtoul
));
773 * Extract a list of types from the comma- or space- separated string
774 * str and append them to the array *idsp, growing it if necessary.
777 parse_types(const char *arg
, int **typesp
, int ntypes
)
779 return (parse(arg
, typesp
, ntypes
, get_type
));
785 * Sorts and removes duplicates from array. Initial size of array is
786 * in *size; final size is stored in *size.
789 compact(int *array
, int *size
)
793 qsort(array
, *size
, sizeof (int), int_compar
);
794 for (i
= j
= 0; i
< *size
; i
++) {
795 if (array
[i
] != last
) {
797 array
[j
++] = array
[i
];
804 main(int argc
, char **argv
)
806 unsigned int interval
= 0, count
= 1;
809 int nids
= 0, ntypes
= 0;
812 (void) setlocale(LC_ALL
, "");
813 (void) textdomain(TEXT_DOMAIN
);
815 (void) uu_setpname(argv
[0]);
817 while ((s
= getopt(argc
, argv
, "ai:T:t:v")) != EOF
) {
823 nids
= parse_ids(optarg
, (int **)&ids
, nids
);
828 timestamp_fmt
= UDATE
;
829 else if (*optarg
== 'd')
830 timestamp_fmt
= DDATE
;
838 ntypes
= parse_types(optarg
, &types
, ntypes
);
851 if (argc
> 2 || argc
< 0)
855 interval
= mystrtoul(argv
[0]);
860 count
= mystrtoul(argv
[1]);
866 compact((int *)ids
, &nids
);
868 compact(types
, &ntypes
);
870 for (i
= 0; count
== 0 || i
< count
; i
++) {
872 (void) sleep(interval
);
873 if (timestamp_fmt
!= NODATE
)
874 print_timestamp(timestamp_fmt
);
877 scan_all(types
, ntypes
, ids
, nids
);
878 else if (ntypes
== 1)
883 scan_all(types
, ntypes
, ids
, nids
);