xref: /illumos-gate/usr/src/cmd/psrinfo/psrinfo.c (revision 554bfeed)
1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright (c) 2012 DEY Storage Systems, Inc.  All rights reserved.
14  * Copyright 2012 Nexenta Systems, Inc.  All rights reserved.
15  * Copyright 2019 Joyent, Inc.
16  */
17 
18 /*
19  * This implements psrinfo(1M), a utility to report various information
20  * about processors, cores, and threads (virtual cpus).  This is mostly
21  * intended for human consumption - this utility doesn't do much more than
22  * simply process kstats for human readability.
23  *
24  * All the relevant kstats are in the cpu_info kstat module.
25  */
26 
27 #include <sys/sysmacros.h>
28 
29 #include <stdbool.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <unistd.h>
33 #include <string.h>
34 #include <kstat.h>
35 #include <libintl.h>
36 #include <locale.h>
37 #include <libgen.h>
38 #include <ctype.h>
39 #include <errno.h>
40 #include <err.h>
41 
42 #include <libdevinfo.h>
43 
44 #define	_(x)	gettext(x)
45 #if XGETTEXT
46 	/* These CPU states are here for benefit of xgettext */
47 	_("on-line")
48 	_("off-line")
49 	_("faulted")
50 	_("powered-off")
51 	_("no-intr")
52 	_("spare")
53 	_("unknown")
54 	_("disabled")
55 #endif
56 
57 /*
58  * We deal with sorted linked lists, where the sort key is usually the
59  * cpu id, core id, or chip id.  We generalize this with simple node.
60  */
61 struct link {
62 	long		l_id;
63 	struct link	*l_next;
64 	void		*l_ptr;
65 };
66 
67 /*
68  * A physical chip.  A chip can contain multiple cores and virtual cpus.
69  */
70 struct pchip {
71 	struct link	p_link;
72 	int		p_ncore;
73 	int		p_nvcpu;
74 	struct link	*p_cores;
75 	struct link	*p_vcpus;
76 	int		p_doit;
77 };
78 
79 struct core {
80 	struct link	c_link;
81 	struct link	c_link_pchip;
82 
83 	int		c_nvcpu;
84 	int		c_doit;
85 
86 	struct pchip	*c_pchip;
87 	struct link	*c_vcpus;
88 };
89 
90 struct vcpu {
91 	struct link	v_link;
92 
93 	struct link	v_link_core;
94 	struct link	v_link_pchip;
95 
96 	int		v_doit;
97 
98 	struct pchip	*v_pchip;
99 	struct core	*v_core;
100 
101 	char		*v_state;
102 	long		v_state_begin;
103 	char		*v_cpu_type;
104 	char		*v_fpu_type;
105 	long		v_clock_mhz;
106 	long		v_pchip_id;	/* 1 per socket */
107 	char		*v_impl;
108 	char		*v_brand;
109 	char		*v_socket;
110 	long		v_core_id;	/* n per chip_id */
111 };
112 
113 static struct link *pchips = NULL;
114 static struct link *cores = NULL;
115 static struct link *vcpus = NULL;
116 
117 static uint_t nr_cpus;
118 static uint_t nr_cores;
119 static uint_t nr_chips;
120 
121 static const char *cmdname;
122 
123 static void
usage(char * msg)124 usage(char *msg)
125 {
126 	if (msg != NULL)
127 		(void) fprintf(stderr, "%s: %s\n", cmdname, msg);
128 	(void) fprintf(stderr, _("usage: \n"
129 	    "\t%s -r propname\n"
130 	    "\t%s [-v] [-p] [processor_id ...]\n"
131 	    "\t%s -s [-p] processor_id\n"
132 	    "\t%s -t [-S <state> | -c | -p]\n"),
133 	    cmdname, cmdname, cmdname, cmdname);
134 	exit(2);
135 }
136 
137 /* like perror, but includes the command name */
138 static void
die(const char * msg)139 die(const char *msg)
140 {
141 	(void) fprintf(stderr, "%s: %s: %s\n", cmdname, msg, strerror(errno));
142 	exit(2);
143 }
144 
145 static char *
mystrdup(const char * src)146 mystrdup(const char *src)
147 {
148 	char *dst;
149 
150 	if ((dst = strdup(src)) == NULL)
151 		die(_("strdup() failed"));
152 	return (dst);
153 }
154 
155 static void *
zalloc(size_t size)156 zalloc(size_t size)
157 {
158 	void *ptr;
159 
160 	if ((ptr = calloc(1, size)) == NULL)
161 		die(_("calloc() failed"));
162 	return (ptr);
163 }
164 
165 /*
166  * Insert a new node on a list, at the insertion point given.
167  */
168 static void
ins_link(struct link ** ins,struct link * item)169 ins_link(struct link **ins, struct link *item)
170 {
171 	item->l_next = *ins;
172 	*ins = item;
173 }
174 
175 /*
176  * Find an id on a sorted list.  If the requested id is not found,
177  * then the insertpt will be set (if not null) to the location where
178  * a new node should be inserted with ins_link (see above).
179  */
180 static void *
find_link(void * list,int id,struct link *** insertpt)181 find_link(void *list, int id, struct link ***insertpt)
182 {
183 	struct link **ins = list;
184 	struct link *l;
185 
186 	while ((l = *ins) != NULL) {
187 		if (l->l_id == id)
188 			return (l->l_ptr);
189 		if (l->l_id > id)
190 			break;
191 		ins = &l->l_next;
192 	}
193 	if (insertpt != NULL)
194 		*insertpt = ins;
195 	return (NULL);
196 }
197 
198 /*
199  * Print the linked list of ids in parens, taking care to collapse
200  * ranges, so instead of (0 1 2 3) it should print (0-3).
201  */
202 static void
print_links(struct link * l)203 print_links(struct link *l)
204 {
205 	int	start = -1;
206 	int	end = 0;
207 
208 	(void) printf(" (");
209 	while (l != NULL) {
210 		if (start < 0) {
211 			start = l->l_id;
212 		}
213 		end = l->l_id;
214 		if ((l->l_next == NULL) ||
215 		    (l->l_next->l_id > (l->l_id + 1))) {
216 			/* end of the contiguous group */
217 			if (start == end) {
218 				(void) printf("%d", start);
219 			} else {
220 				(void) printf("%d-%d", start, end);
221 			}
222 			if (l->l_next)
223 				(void) printf(" ");
224 			start = -1;
225 		}
226 		l = l->l_next;
227 	}
228 	(void) printf(")");
229 }
230 
231 static const char *
timestr(long t)232 timestr(long t)
233 {
234 	static char buffer[256];
235 	(void) strftime(buffer, sizeof (buffer), _("%m/%d/%Y %T"),
236 	    localtime(&t));
237 	return (buffer);
238 }
239 
240 static void
print_vp(int nspec)241 print_vp(int nspec)
242 {
243 	struct pchip *chip;
244 	struct core *core;
245 	struct vcpu *vcpu;
246 	struct link *l1, *l2;
247 	int len;
248 	for (l1 = pchips; l1; l1 = l1->l_next) {
249 
250 		chip = l1->l_ptr;
251 
252 		if ((nspec != 0) && (chip->p_doit == 0))
253 			continue;
254 
255 		vcpu = chip->p_vcpus->l_ptr;
256 
257 		/*
258 		 * Note that some of the way these strings are broken up are
259 		 * to accommodate the legacy translations so that we won't
260 		 * have to retranslate for this utility.
261 		 */
262 		if ((chip->p_ncore == 1) || (chip->p_ncore == chip->p_nvcpu)) {
263 			(void) printf(_("%s has %d virtual %s"),
264 			    _("The physical processor"),
265 			    chip->p_nvcpu,
266 			    chip->p_nvcpu > 1 ?
267 			    _("processors") :
268 			    _("processor"));
269 		} else {
270 			(void) printf(_("%s has %d %s and %d virtual %s"),
271 			    _("The physical processor"),
272 			    chip->p_ncore, _("cores"),
273 			    chip->p_nvcpu,
274 			    chip->p_nvcpu > 1 ?
275 			    _("processors") : _("processor"));
276 		}
277 
278 		print_links(chip->p_vcpus);
279 		(void) putchar('\n');
280 
281 		if ((chip->p_ncore == 1) || (chip->p_ncore == chip->p_nvcpu)) {
282 			if (strlen(vcpu->v_impl)) {
283 				(void) printf("  %s\n", vcpu->v_impl);
284 			}
285 			if (((len = strlen(vcpu->v_brand)) != 0) &&
286 			    (strncmp(vcpu->v_brand, vcpu->v_impl, len) != 0))
287 				(void) printf("\t%s", vcpu->v_brand);
288 			if (strcmp(vcpu->v_socket, "Unknown") != 0)
289 				(void) printf("\t[ %s: %s ]", _("Socket"),
290 				    vcpu->v_socket);
291 			(void) putchar('\n');
292 		} else {
293 			for (l2 = chip->p_cores; l2; l2 = l2->l_next) {
294 				core = l2->l_ptr;
295 				(void) printf(_("  %s has %d virtual %s"),
296 				    _("The core"),
297 				    core->c_nvcpu,
298 				    chip->p_nvcpu > 1 ?
299 				    _("processors") : _("processor"));
300 				print_links(core->c_vcpus);
301 				(void) putchar('\n');
302 			}
303 			if (strlen(vcpu->v_impl)) {
304 				(void) printf("    %s\n", vcpu->v_impl);
305 			}
306 			if (((len = strlen(vcpu->v_brand)) != 0) &&
307 			    (strncmp(vcpu->v_brand, vcpu->v_impl, len) != 0))
308 				(void) printf("      %s\n", vcpu->v_brand);
309 		}
310 	}
311 }
312 
313 static void
print_ps(void)314 print_ps(void)
315 {
316 	int online = 1;
317 	struct pchip *p = NULL;
318 	struct vcpu *v;
319 	struct link *l;
320 
321 	/*
322 	 * Report "1" if all cpus colocated on the same chip are online.
323 	 */
324 	for (l = pchips; l != NULL; l = l->l_next) {
325 		p = l->l_ptr;
326 		if (p->p_doit)
327 			break;
328 	}
329 	if (p == NULL)
330 		return;	/* should never happen! */
331 	for (l = p->p_vcpus; l != NULL; l = l->l_next) {
332 		v = l->l_ptr;
333 		if (strcmp(v->v_state, "on-line") != 0) {
334 			online = 0;
335 			break;
336 		}
337 	}
338 
339 	(void) printf("%d\n", online);
340 }
341 
342 static void
print_s(void)343 print_s(void)
344 {
345 	struct link *l;
346 
347 	/*
348 	 * Find the processor (there will be only one) that we selected,
349 	 * and report whether or not it is online.
350 	 */
351 	for (l = vcpus; l != NULL; l = l->l_next) {
352 		struct vcpu *v = l->l_ptr;
353 		if (v->v_doit) {
354 			(void) printf("%d\n",
355 			    strcmp(v->v_state, "on-line") == 0 ? 1 : 0);
356 			return;
357 		}
358 	}
359 }
360 
361 static void
print_p(int nspec)362 print_p(int nspec)
363 {
364 	struct		link *l1, *l2;
365 	int		online = 0;
366 
367 	/*
368 	 * Print the number of physical packages with at least one processor
369 	 * online.
370 	 */
371 	for (l1 = pchips; l1 != NULL; l1 = l1->l_next) {
372 		struct pchip *p = l1->l_ptr;
373 		if ((nspec == 0) || (p->p_doit)) {
374 
375 			for (l2 = p->p_vcpus; l2 != NULL; l2 = l2->l_next) {
376 				struct vcpu *v = l2->l_ptr;
377 				if (strcmp(v->v_state, "on-line") == 0) {
378 					online++;
379 					break;
380 				}
381 			}
382 		}
383 	}
384 	(void) printf("%d\n", online);
385 }
386 
387 static void
print_v(int nspec)388 print_v(int nspec)
389 {
390 	struct link	*l;
391 
392 	for (l = vcpus; l != NULL; l = l->l_next) {
393 		struct vcpu *v = l->l_ptr;
394 
395 		if ((nspec != 0) && (!v->v_doit))
396 			continue;
397 		(void) printf(_("Status of virtual processor %d as of: "),
398 		    l->l_id);
399 		(void) printf("%s\n", timestr(time(NULL)));
400 		(void) printf(_("  %s since %s.\n"),
401 		    _(v->v_state), timestr(v->v_state_begin));
402 		if (v->v_clock_mhz) {
403 			(void) printf(
404 			    _("  The %s processor operates at %llu MHz,\n"),
405 			    v->v_cpu_type, (unsigned long long)v->v_clock_mhz);
406 		} else {
407 			(void) printf(
408 			    _("  The %s processor operates at " \
409 			    "an unknown frequency,\n"), v->v_cpu_type);
410 		}
411 		switch (*v->v_fpu_type) {
412 		case '\0':
413 			(void) printf(
414 			    _("\tand has no floating point processor.\n"));
415 			break;
416 		case 'a': case 'A':
417 		case 'e': case 'E':
418 		case 'i': case 'I':
419 		case 'o': case 'O':
420 		case 'u': case 'U':
421 		case 'y': case 'Y':
422 			(void) printf(
423 			    _("\tand has an %s floating point processor.\n"),
424 			    v->v_fpu_type);
425 			break;
426 		default:
427 			(void) printf(
428 			    _("\tand has a %s floating point processor.\n"),
429 			    v->v_fpu_type);
430 			break;
431 		}
432 	}
433 }
434 
435 static void
print_normal(int nspec)436 print_normal(int nspec)
437 {
438 	struct link	*l;
439 	struct vcpu	*v;
440 
441 	for (l = vcpus; l != NULL; l = l->l_next) {
442 		v = l->l_ptr;
443 		if ((nspec == 0) || (v->v_doit)) {
444 			(void) printf(_("%d\t%-8s  since %s\n"),
445 			    l->l_id, _(v->v_state), timestr(v->v_state_begin));
446 		}
447 	}
448 }
449 
450 static bool
valid_propname(const char * propname)451 valid_propname(const char *propname)
452 {
453 	size_t i;
454 
455 	const char *props[] = {
456 		"smt_enabled",
457 	};
458 
459 	for (i = 0; i < ARRAY_SIZE(props); i++) {
460 		if (strcmp(propname, props[i]) == 0)
461 			break;
462 	}
463 
464 	return (i != ARRAY_SIZE(props));
465 }
466 
467 static void
read_property(const char * propname)468 read_property(const char *propname)
469 {
470 	di_prop_t prop = DI_PROP_NIL;
471 	di_node_t root_node;
472 	bool show_all = strcmp(propname, "all") == 0;
473 
474 	if (!show_all && !valid_propname(propname))
475 		errx(EXIT_FAILURE, _("unknown CPU property %s"), propname);
476 
477 	if ((root_node = di_init("/", DINFOPROP)) == NULL)
478 		err(EXIT_FAILURE, _("failed to read root node"));
479 
480 	while ((prop = di_prop_sys_next(root_node, prop)) != DI_PROP_NIL) {
481 		const char *name = di_prop_name(prop);
482 		char *val;
483 		int nr_vals;
484 
485 		if (!valid_propname(name))
486 			continue;
487 
488 		if (!show_all && strcmp(di_prop_name(prop), propname) != 0)
489 			continue;
490 
491 		if ((nr_vals = di_prop_strings(prop, &val)) < 1) {
492 			err(EXIT_FAILURE,
493 			    _("error reading property %s"), name);
494 		} else if (nr_vals != 1) {
495 			errx(EXIT_FAILURE, _("invalid property %s"), name);
496 		}
497 
498 		printf("%s=%s\n", name, val);
499 
500 		if (!show_all)
501 			exit(EXIT_SUCCESS);
502 	}
503 
504 	if (!show_all)
505 		errx(EXIT_FAILURE, _("property %s was not found"), propname);
506 
507 	di_fini(root_node);
508 }
509 
510 static void
print_total(int opt_c,int opt_p,const char * opt_S)511 print_total(int opt_c, int opt_p, const char *opt_S)
512 {
513 	uint_t count = 0;
514 
515 	if (opt_c) {
516 		printf("%u\n", nr_cores);
517 		return;
518 	} else if (opt_p) {
519 		printf("%u\n", nr_chips);
520 		return;
521 	} else if (opt_S == NULL || strcmp(opt_S, "all") == 0) {
522 		printf("%u\n", nr_cpus);
523 		return;
524 	}
525 
526 
527 	for (struct link *l = vcpus; l != NULL; l = l->l_next) {
528 		struct vcpu *v = l->l_ptr;
529 		if (strcmp(opt_S, v->v_state) == 0)
530 			count++;
531 	}
532 
533 	printf("%u\n", count);
534 }
535 
536 int
main(int argc,char ** argv)537 main(int argc, char **argv)
538 {
539 	kstat_ctl_t	*kc;
540 	kstat_t		*ksp;
541 	kstat_named_t	*knp;
542 	struct vcpu	*vc;
543 	struct core	*core;
544 	struct pchip	*chip;
545 	struct link	**ins;
546 	char		*s;
547 	int		nspec;
548 	int		optc;
549 	int		opt_c = 0;
550 	int		opt_p = 0;
551 	const char	*opt_r = NULL;
552 	const char	*opt_S = NULL;
553 	int		opt_s = 0;
554 	int		opt_t = 0;
555 	int		opt_v = 0;
556 	int		ex = 0;
557 
558 	cmdname = basename(argv[0]);
559 
560 
561 	(void) setlocale(LC_ALL, "");
562 #if !defined(TEXT_DOMAIN)
563 #define	TEXT_DOMAIN	"SYS_TEST"
564 #endif
565 	(void) textdomain(TEXT_DOMAIN);
566 
567 	/* collect the kstats */
568 	if ((kc = kstat_open()) == NULL)
569 		die(_("kstat_open() failed"));
570 
571 	if ((ksp = kstat_lookup(kc, "cpu_info", -1, NULL)) == NULL)
572 		die(_("kstat_lookup() failed"));
573 
574 	for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) {
575 
576 		if (strcmp(ksp->ks_module, "cpu_info") != 0)
577 			continue;
578 		if (kstat_read(kc, ksp, NULL) == -1)
579 			die(_("kstat_read() failed"));
580 
581 		vc = find_link(&vcpus, ksp->ks_instance, &ins);
582 		if (vc == NULL) {
583 			vc = zalloc(sizeof (struct vcpu));
584 			vc->v_link.l_id = ksp->ks_instance;
585 			vc->v_link_core.l_id = ksp->ks_instance;
586 			vc->v_link_pchip.l_id = ksp->ks_instance;
587 			vc->v_link.l_ptr = vc;
588 			vc->v_link_core.l_ptr = vc;
589 			vc->v_link_pchip.l_ptr = vc;
590 			ins_link(ins, &vc->v_link);
591 			nr_cpus++;
592 		}
593 
594 		if ((knp = kstat_data_lookup(ksp, "state")) != NULL) {
595 			vc->v_state = mystrdup(knp->value.c);
596 		} else {
597 			vc->v_state = "unknown";
598 		}
599 
600 		if ((knp = kstat_data_lookup(ksp, "cpu_type")) != NULL) {
601 			vc->v_cpu_type = mystrdup(knp->value.c);
602 		}
603 		if ((knp = kstat_data_lookup(ksp, "fpu_type")) != NULL) {
604 			vc->v_fpu_type = mystrdup(knp->value.c);
605 		}
606 
607 		if ((knp = kstat_data_lookup(ksp, "state_begin")) != NULL) {
608 			vc->v_state_begin = knp->value.l;
609 		}
610 
611 		if ((knp = kstat_data_lookup(ksp, "clock_MHz")) != NULL) {
612 			vc->v_clock_mhz = knp->value.l;
613 		}
614 
615 		if ((knp = kstat_data_lookup(ksp, "brand")) == NULL) {
616 			vc->v_brand = _("(unknown)");
617 		} else {
618 			vc->v_brand = mystrdup(knp->value.str.addr.ptr);
619 		}
620 
621 		if ((knp = kstat_data_lookup(ksp, "socket_type")) == NULL) {
622 			vc->v_socket = "Unknown";
623 		} else {
624 			vc->v_socket = mystrdup(knp->value.str.addr.ptr);
625 		}
626 
627 		if ((knp = kstat_data_lookup(ksp, "implementation")) == NULL) {
628 			vc->v_impl = _("(unknown)");
629 		} else {
630 			vc->v_impl = mystrdup(knp->value.str.addr.ptr);
631 		}
632 		/*
633 		 * Legacy code removed the chipid and cpuid fields... we
634 		 * do the same for compatibility.  Note that the original
635 		 * pattern is a bit strange, and we have to emulate this because
636 		 * on SPARC we *do* emit these.  The original pattern we are
637 		 * emulating is: $impl =~ s/(cpuid|chipid)\s*\w+\s+//;
638 		 */
639 		if ((s = strstr(vc->v_impl, "chipid")) != NULL) {
640 			char *x = s + strlen("chipid");
641 			while (isspace(*x))
642 				x++;
643 			if ((!isalnum(*x)) && (*x != '_'))
644 				goto nochipid;
645 			while (isalnum(*x) || (*x == '_'))
646 				x++;
647 			if (!isspace(*x))
648 				goto nochipid;
649 			while (isspace(*x))
650 				x++;
651 			(void) strcpy(s, x);
652 		}
653 nochipid:
654 		if ((s = strstr(vc->v_impl, "cpuid")) != NULL) {
655 			char *x = s + strlen("cpuid");
656 			while (isspace(*x))
657 				x++;
658 			if ((!isalnum(*x)) && (*x != '_'))
659 				goto nocpuid;
660 			while (isalnum(*x) || (*x == '_'))
661 				x++;
662 			if (!isspace(*x))
663 				goto nocpuid;
664 			while (isspace(*x))
665 				x++;
666 			(void) strcpy(s, x);
667 		}
668 nocpuid:
669 
670 		if ((knp = kstat_data_lookup(ksp, "chip_id")) != NULL)
671 			vc->v_pchip_id = knp->value.l;
672 		chip = find_link(&pchips, vc->v_pchip_id, &ins);
673 		if (chip == NULL) {
674 			chip = zalloc(sizeof (struct pchip));
675 			chip->p_link.l_id = vc->v_pchip_id;
676 			chip->p_link.l_ptr = chip;
677 			ins_link(ins, &chip->p_link);
678 			nr_chips++;
679 		}
680 		vc->v_pchip = chip;
681 
682 		if ((knp = kstat_data_lookup(ksp, "core_id")) != NULL)
683 			vc->v_core_id = knp->value.l;
684 		core = find_link(&cores, vc->v_core_id, &ins);
685 		if (core == NULL) {
686 			core = zalloc(sizeof (struct core));
687 			core->c_link.l_id = vc->v_core_id;
688 			core->c_link.l_ptr = core;
689 			core->c_link_pchip.l_id = vc->v_core_id;
690 			core->c_link_pchip.l_ptr = core;
691 			core->c_pchip = chip;
692 			ins_link(ins, &core->c_link);
693 			chip->p_ncore++;
694 			(void) find_link(&chip->p_cores, core->c_link.l_id,
695 			    &ins);
696 			ins_link(ins, &core->c_link_pchip);
697 			nr_cores++;
698 		}
699 		vc->v_core = core;
700 
701 
702 
703 		/* now put other linkages in place */
704 		(void) find_link(&chip->p_vcpus, vc->v_link.l_id, &ins);
705 		ins_link(ins, &vc->v_link_pchip);
706 		chip->p_nvcpu++;
707 
708 		(void) find_link(&core->c_vcpus, vc->v_link.l_id, &ins);
709 		ins_link(ins, &vc->v_link_core);
710 		core->c_nvcpu++;
711 	}
712 
713 	(void) kstat_close(kc);
714 
715 	nspec = 0;
716 
717 	while ((optc = getopt(argc, argv, "cpr:S:stv")) != EOF) {
718 		switch (optc) {
719 		case 'c':
720 			opt_c = 1;
721 			break;
722 		case 'p':
723 			opt_p = 1;
724 			break;
725 		case 'r':
726 			opt_r = optarg;
727 			break;
728 		case 'S':
729 			opt_S = optarg;
730 			break;
731 		case 's':
732 			opt_s = 1;
733 			break;
734 		case 't':
735 			opt_t = 1;
736 			break;
737 		case 'v':
738 			opt_v = 1;
739 			break;
740 		default:
741 			usage(NULL);
742 		}
743 	}
744 
745 	if (opt_r != NULL) {
746 		if (optind != argc)
747 			usage(_("cannot specify CPUs with -r"));
748 		if (opt_c || opt_p || opt_S != NULL || opt_s || opt_t || opt_v)
749 			usage(_("cannot specify other arguments with -r"));
750 
751 		read_property(opt_r);
752 		return (EXIT_SUCCESS);
753 	}
754 
755 	if (opt_t != 0) {
756 		if (optind != argc)
757 			usage(_("cannot specify CPUs with -t"));
758 		if (opt_s || opt_v)
759 			usage(_("cannot specify -s or -v with -t"));
760 		if (opt_S != NULL && (opt_c || opt_p))
761 			usage(_("cannot specify CPU state with -c or -p"));
762 		if (opt_c && opt_p)
763 			usage(_("cannot specify -c and -p"));
764 
765 		print_total(opt_c, opt_p, opt_S);
766 		return (EXIT_SUCCESS);
767 	}
768 
769 	if (opt_S != NULL || opt_c)
770 		usage(_("cannot specify -S or -c without -t"));
771 
772 	while (optind < argc) {
773 		long id;
774 		char *eptr;
775 		struct link *l;
776 		id = strtol(argv[optind], &eptr, 10);
777 		l = find_link(&vcpus, id, NULL);
778 		if ((*eptr != '\0') || (l == NULL)) {
779 			(void) fprintf(stderr,
780 			    _("%s: processor %s: Invalid argument\n"),
781 			    cmdname, argv[optind]);
782 			ex = 2;
783 		} else {
784 			((struct vcpu *)l->l_ptr)->v_doit = 1;
785 			((struct vcpu *)l->l_ptr)->v_pchip->p_doit = 1;
786 			((struct vcpu *)l->l_ptr)->v_core->c_doit = 1;
787 		}
788 		nspec++;
789 		optind++;
790 	}
791 
792 	if (opt_s && opt_v) {
793 		usage(_("options -s and -v are mutually exclusive"));
794 	}
795 	if (opt_s && nspec != 1) {
796 		usage(_("must specify exactly one processor if -s used"));
797 	}
798 	if (opt_v && opt_p) {
799 		print_vp(nspec);
800 	} else if (opt_s && opt_p) {
801 		print_ps();
802 	} else if (opt_p) {
803 		print_p(nspec);
804 	} else if (opt_v) {
805 		print_v(nspec);
806 	} else if (opt_s) {
807 		print_s();
808 	} else {
809 		print_normal(nspec);
810 	}
811 
812 	return (ex);
813 }
814