xref: /illumos-gate/usr/src/cmd/ptools/plgrp/plgrp.c (revision 4e5b757f)
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 /*
23  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * The plgrp utility allows a user to display and modify the home lgroup and
31  * lgroup affinities of the specified threads
32  */
33 
34 #include <ctype.h>
35 #include <errno.h>
36 #include <libintl.h>
37 #include <libproc.h>
38 #include <locale.h>
39 #include <signal.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <strings.h>
43 #include <unistd.h>
44 #include <libgen.h>
45 #include <sys/lgrp_user.h>
46 
47 
48 /*
49  * Delimiters
50  */
51 #define	DELIMIT_AFF	'/'	/* lgroup affinity from lgroups */
52 #define	DELIMIT_LGRP	","	/* lgroups from each other */
53 #define	DELIMIT_LWP	"/"	/* thread/LWP IDs from process ID */
54 #define	DELIMIT_RANGE	'-'	/* range of IDs (eg. lgroup) */
55 #define	DELIMIT_AFF_LST ','	/* list of affinities from another list */
56 
57 /*
58  * Exit values other than EXIT_{SUCCESS,FAILURE}
59  */
60 #define	EXIT_NONFATAL 2		/* non-fatal errors */
61 
62 /*
63  * Header and format strings
64  */
65 #define	HDR_PLGRP_AFF_GET	"     PID/LWPID    HOME  AFFINITY\n"
66 #define	HDR_PLGRP_AFF_SET	"     PID/LWPID    HOME       AFFINITY\n"
67 #define	HDR_PLGRP_HOME_GET	"     PID/LWPID    HOME\n"
68 #define	HDR_PLGRP_HOME_SET	"     PID/LWPID    HOME\n"
69 
70 /*
71  * Part of the HDR_PLGRP_AFF_SET header used to calculate space needed to
72  * represent changing home as old => new
73  */
74 #define	HDR_PLGRP_HOME_CHANGE	"HOME       "
75 
76 #define	FMT_AFF			"%d/%s"
77 #define	FMT_AFF_STR		"%s"
78 #define	FMT_HOME		"%-6d"
79 #define	FMT_NEWHOME		"%d => %d"
80 #define	FMT_THREAD		"%8d/%-8d"
81 
82 /*
83  * How much to allocate for lgroup bitmap array as it grows
84  */
85 #define	LGRP_BITMAP_CHUNK 8
86 
87 /*
88  * Strings that can be given for lgroups
89  */
90 #define	LGRP_ALL_STR		"all"
91 #define	LGRP_LEAVES_STR		"leaves"
92 #define	LGRP_ROOT_STR		"root"
93 
94 /*
95  * Strings corresponding to lgroup affinities
96  */
97 #define	LGRP_AFF_NONE_STR	"none"
98 #define	LGRP_AFF_STRONG_STR	"strong"
99 #define	LGRP_AFF_WEAK_STR	"weak"
100 
101 /*
102  * Invalid value for lgroup affinity
103  */
104 #define	LGRP_AFF_INVALID	-1
105 
106 /*
107  * Number of args needed for lgroup system call
108  */
109 #define	LGRPSYS_NARGS		3
110 
111 #ifndef	TEXT_DOMAIN			/* should be defined by cc -D */
112 #define	TEXT_DOMAIN	"SYS_TEST"	/* use this only if it wasn't */
113 #endif
114 
115 /*
116  * plgrp(1) operations
117  */
118 typedef enum plgrp_ops {
119 	PLGRP_AFFINITY_GET,
120 	PLGRP_AFFINITY_SET,
121 	PLGRP_HOME_GET,
122 	PLGRP_HOME_SET,
123 	PLGRP_NO_OP
124 } plgrp_ops_t;
125 
126 /*
127  * Arguments specified to plgrp(1) and any state needed to do everything
128  * that plgrp(1) does for one operation from inside Plwp_iter_all()
129  */
130 typedef struct plgrp_args {
131 	struct ps_prochandle	*Ph;		/* proc handle for process */
132 	const char		*lwps;		/* LWPs */
133 	lgrp_id_t		*lgrps;		/* lgroups */
134 	lgrp_affinity_t		*affs;		/* lgroup affinities */
135 	int			nlgrps;		/* number of lgroups */
136 	int			nelements;	/* number of elements */
137 	int			index;		/* index */
138 	int			nthreads;	/* threads processed */
139 	plgrp_ops_t		op;		/* operation */
140 } plgrp_args_t;
141 
142 /*
143  * How many signals caught from terminal
144  * We bail out as soon as possible when interrupt is set
145  */
146 static int	interrupt = 0;
147 
148 /*
149  * How many non-fatal errors ocurred
150  */
151 static int	nerrors = 0;
152 
153 /*
154  * Name of this program
155  */
156 static char	*progname;
157 
158 /*
159  * Root of the lgroup hierarchy
160  */
161 static lgrp_id_t root = LGRP_NONE;
162 
163 /*
164  * Bitmap of all lgroups in the system
165  */
166 static char *lgrps_bitmap = NULL;
167 
168 /*
169  * Size of lgrps_bitmap array
170  */
171 static int lgrps_bitmap_nelements = 0;
172 
173 /*
174  * Macro LGRP_VALID returns true when lgrp is present in the system.
175  */
176 #define	LGRP_VALID(lgrp) (lgrps_bitmap[lgrp] != 0)
177 
178 
179 /*
180  * Maximum lgroup value.
181  */
182 static int max_lgrpid = LGRP_NONE;
183 
184 /*
185  * Total possible number of lgroups
186  */
187 #define	NLGRPS (max_lgrpid + 1)
188 
189 
190 static void
191 usage(int rc)
192 {
193 	(void) fprintf(stderr,
194 	    gettext("Usage:\t%s [-h] <pid> | <core> [/lwps] ...\n"), progname);
195 	(void) fprintf(stderr,
196 	    gettext("\t%s [-F] -a <lgroup list> <pid>[/lwps] ...\n"), progname);
197 	(void) fprintf(stderr,
198 	    gettext("\t%s [-F] -A <lgroup list>/none|weak|strong[,...] "
199 	    " <pid>[/lwps] ...\n"), progname);
200 	(void) fprintf(stderr,
201 	    gettext("\t%s [-F] -H <lgroup list> <pid>[/lwps] ...\n"), progname);
202 	(void) fprintf(stderr,
203 	    gettext("\n\twhere <lgroup list> is a comma separated list of\n"
204 		"\tone or more of the following:\n\n"
205 		"\t  - lgroup ID\n"
206 		"\t  - Range of lgroup IDs specified as\n"
207 		"\t\t<start lgroup ID>-<end lgroup ID>\n"
208 		"\t  - \"all\"\n"
209 		"\t  - \"root\"\n"
210 		"\t  - \"leaves\"\n\n"));
211 
212 	exit(rc);
213 }
214 
215 /*
216  * Handler for catching signals from terminal
217  */
218 /* ARGSUSED */
219 static void
220 intr(int sig)
221 {
222 	interrupt++;
223 }
224 
225 
226 /*
227  * Return string name for given lgroup affinity
228  */
229 static char *
230 lgrp_affinity_string(lgrp_affinity_t aff)
231 {
232 	char *rc = "unknown";
233 
234 	switch (aff) {
235 	case LGRP_AFF_STRONG:
236 		rc = "strong";
237 		break;
238 	case LGRP_AFF_WEAK:
239 		rc = "weak";
240 		break;
241 	case LGRP_AFF_NONE:
242 		rc = "none";
243 		break;
244 	default:
245 		break;
246 	}
247 
248 	return (rc);
249 }
250 
251 
252 /*
253  * Add a new lgroup into lgroup array in "arg", growing lgroup and affinity
254  * arrays if necessary
255  */
256 static void
257 lgrps_add_lgrp(plgrp_args_t *arg, int id)
258 {
259 
260 	if (arg->nlgrps == arg->nelements) {
261 		arg->nelements += LGRP_BITMAP_CHUNK;
262 
263 		arg->lgrps = realloc(arg->lgrps,
264 		    arg->nelements * sizeof (lgrp_id_t));
265 		if (arg->lgrps == NULL) {
266 			(void) fprintf(stderr, gettext("%s: out of memory\n"),
267 			    progname);
268 			exit(EXIT_FAILURE);
269 		}
270 
271 		arg->affs = realloc(arg->affs,
272 		    arg->nelements * sizeof (lgrp_affinity_t));
273 
274 		if (arg->affs == NULL) {
275 			(void) fprintf(stderr, gettext("%s: out of memory\n"),
276 			    progname);
277 			exit(EXIT_FAILURE);
278 		}
279 	}
280 
281 	arg->lgrps[arg->nlgrps] = id;
282 	arg->affs[arg->nlgrps] = LGRP_AFF_INVALID;
283 	arg->nlgrps++;
284 }
285 
286 
287 /*
288  * Return an array having '1' for each lgroup present in given subtree under
289  * specified lgroup in lgroup hierarchy
290  */
291 static void
292 lgrps_bitmap_init(lgrp_cookie_t cookie, lgrp_id_t lgrpid, char **bitmap_array,
293 	int *bitmap_nelements)
294 {
295 	lgrp_id_t	*children;
296 	int		i;
297 	int		nchildren;
298 
299 	if (lgrpid < 0) {
300 		lgrpid = lgrp_root(cookie);
301 		if (lgrpid < 0)
302 			return;
303 	}
304 
305 	/*
306 	 * If new lgroup cannot fit, grow the array and fill unused portion
307 	 * with zeroes.
308 	 */
309 	while (lgrpid >= *bitmap_nelements) {
310 		*bitmap_nelements += LGRP_BITMAP_CHUNK;
311 		*bitmap_array = realloc(*bitmap_array,
312 		    *bitmap_nelements * sizeof (char));
313 		if (*bitmap_array == NULL) {
314 			(void) fprintf(stderr, gettext("%s: out of memory\n"),
315 			    progname);
316 			exit(EXIT_FAILURE);
317 		}
318 		bzero(*bitmap_array + NLGRPS,
319 		    (*bitmap_nelements - NLGRPS) * sizeof (char));
320 	}
321 
322 	/*
323 	 * Insert lgroup into bitmap and update max lgroup ID seen so far
324 	 */
325 	(*bitmap_array)[lgrpid] = 1;
326 	if (lgrpid > max_lgrpid)
327 		max_lgrpid = lgrpid;
328 
329 	/*
330 	 * Get children of specified lgroup and insert descendants of each
331 	 * of them
332 	 */
333 	nchildren = lgrp_children(cookie, lgrpid, NULL, 0);
334 	if (nchildren > 0) {
335 		children = malloc(nchildren * sizeof (lgrp_id_t));
336 		if (children == NULL) {
337 			(void) fprintf(stderr, gettext("%s: out of memory\n"),
338 			    progname);
339 			exit(EXIT_FAILURE);
340 		}
341 		if (lgrp_children(cookie, lgrpid, children, nchildren) !=
342 		    nchildren) {
343 			free(children);
344 			return;
345 		}
346 
347 		for (i = 0; i < nchildren; i++)
348 			lgrps_bitmap_init(cookie, children[i], bitmap_array,
349 			    bitmap_nelements);
350 
351 		free(children);
352 	}
353 }
354 
355 
356 /*
357  * Parse lgroup affinity from given string
358  *
359  * Return lgroup affinity or LGRP_AFF_INVALID if string doesn't match any
360  * existing lgroup affinity and return pointer to position just after affinity
361  * string.
362  */
363 static lgrp_affinity_t
364 parse_lgrp_affinity(char *string, char  **next)
365 {
366 	int rc = LGRP_AFF_INVALID;
367 
368 	if (string == NULL)
369 		return (LGRP_AFF_INVALID);
370 
371 	/*
372 	 * Skip delimiter
373 	 */
374 	if (string[0] == DELIMIT_AFF)
375 		string++;
376 
377 	/*
378 	 * Return lgroup affinity matching string
379 	 */
380 	if (strncmp(string, LGRP_AFF_NONE_STR, strlen(LGRP_AFF_NONE_STR))
381 	    == 0) {
382 		rc = LGRP_AFF_NONE;
383 		*next = string + strlen(LGRP_AFF_NONE_STR);
384 	} else if (strncmp(string,
385 			LGRP_AFF_WEAK_STR, strlen(LGRP_AFF_WEAK_STR)) == 0) {
386 		rc = LGRP_AFF_WEAK;
387 		*next = string + strlen(LGRP_AFF_WEAK_STR);
388 	} else if (strncmp(string, LGRP_AFF_STRONG_STR,
389 			strlen(LGRP_AFF_STRONG_STR)) == 0) {
390 		rc = LGRP_AFF_STRONG;
391 		*next = string + strlen(LGRP_AFF_STRONG_STR);
392 	}
393 
394 	return (rc);
395 }
396 
397 
398 /*
399  * Parse lgroups from given string
400  * Returns the set containing all lgroups parsed or NULL.
401  */
402 static int
403 parse_lgrps(lgrp_cookie_t cookie, plgrp_args_t *arg, char *s)
404 {
405 	lgrp_id_t	i;
406 	char		*token;
407 
408 	if (cookie == LGRP_COOKIE_NONE || s == NULL || NLGRPS <= 0)
409 		return (0);
410 
411 	/*
412 	 * Parse first lgroup (if any)
413 	 */
414 	token = strtok(s, DELIMIT_LGRP);
415 	if (token == NULL)
416 		return (-1);
417 
418 	do {
419 		/*
420 		 * Parse lgroups
421 		 */
422 		if (isdigit(*token)) {
423 			lgrp_id_t	first;
424 			lgrp_id_t	last;
425 			char		*p;
426 
427 			/*
428 			 * lgroup ID(s)
429 			 *
430 			 * Can be <lgroup ID>[-<lgroup ID>]
431 			 */
432 			p = strchr(token, DELIMIT_RANGE);
433 			first = atoi(token);
434 			if (p == NULL)
435 				last = first;
436 			else
437 				last = atoi(++p);
438 
439 			for (i = first; i <= last; i++) {
440 				/*
441 				 * Add valid lgroups to lgroup array
442 				 */
443 				if ((i >= 0) && (i < NLGRPS) && LGRP_VALID(i))
444 					lgrps_add_lgrp(arg, i);
445 				else  {
446 					(void) fprintf(stderr,
447 					    gettext("%s: bad lgroup %d\n"),
448 					    progname, i);
449 					nerrors++;
450 				}
451 			}
452 		} else if (strncmp(token, LGRP_ALL_STR,
453 				strlen(LGRP_ALL_STR)) == 0) {
454 			/*
455 			 * Add "all" lgroups to lgroups array
456 			 */
457 			for (i = 0; i < NLGRPS; i++) {
458 				if (LGRP_VALID(i))
459 					lgrps_add_lgrp(arg, i);
460 			}
461 		} else if (strncmp(token, LGRP_ROOT_STR,
462 				strlen(LGRP_ROOT_STR)) == 0) {
463 			if (root < 0)
464 				root = lgrp_root(cookie);
465 			lgrps_add_lgrp(arg, root);
466 		} else if (strncmp(token, LGRP_LEAVES_STR,
467 		    strlen(LGRP_LEAVES_STR)) == 0) {
468 			/*
469 			 * Add leaf lgroups to lgroups array
470 			 */
471 			for (i = 0; i < NLGRPS; i++) {
472 				if (LGRP_VALID(i) &&
473 				    lgrp_children(cookie, i, NULL, 0) == 0)
474 					lgrps_add_lgrp(arg, i);
475 			}
476 		} else {
477 			return (-1);
478 		}
479 	} while (token = strtok(NULL, DELIMIT_LGRP));
480 
481 	return (0);
482 }
483 
484 /*
485  * Print array of lgroup IDs, collapsing any consecutive runs of IDs into a
486  * range (eg. 2,3,4 into 2-4)
487  */
488 static void
489 print_lgrps(lgrp_id_t *lgrps, int nlgrps)
490 {
491 	lgrp_id_t	start;
492 	lgrp_id_t	end;
493 	int		i;
494 
495 	/*
496 	 * Initial range consists of the first element
497 	 */
498 	start = end = lgrps[0];
499 
500 	for (i = 1; i < nlgrps; i++) {
501 		lgrp_id_t	lgrpid;
502 
503 		lgrpid = lgrps[i];
504 		if (lgrpid == end + 1) {
505 			/*
506 			 * Got consecutive lgroup ID, so extend end of range
507 			 * without printing anything since the range may extend
508 			 * further
509 			 */
510 			end = lgrpid;
511 		} else {
512 			/*
513 			 * Next lgroup ID is not consecutive, so print lgroup
514 			 * IDs gotten so far.
515 			 */
516 			if (end == start) {		/* same value */
517 				(void) printf("%d,", (int)start);
518 			} else if (end > start + 1) {	/* range */
519 				(void) printf("%d-%d,", (int)start, (int)end);
520 			} else {			/* different values */
521 				(void) printf("%d,%d,", (int)start, (int)end);
522 			}
523 
524 			/*
525 			 * Try finding consecutive range starting from this
526 			 * lgroup ID
527 			 */
528 			start = end = lgrpid;
529 		}
530 	}
531 
532 	/*
533 	 * Print last lgroup ID(s)
534 	 */
535 	if (end == start) {
536 		(void) printf("%d", (int)start);
537 	} else if (end > start + 1) {
538 		(void) printf("%d-%d", (int)start, (int)end);
539 	} else {
540 		(void) printf("%d,%d", (int)start, (int)end);
541 	}
542 }
543 
544 /*
545  * Print lgroup affinities given array of lgroups, corresponding array of
546  * affinities, and number of elements.
547  * Skip any lgroups set to LGRP_NONE or having invalid affinity.
548  */
549 static void
550 print_affinities(lgrp_id_t *lgrps, lgrp_affinity_t *affs, int nelements)
551 {
552 	int		i;
553 	lgrp_id_t	*lgrps_none;
554 	lgrp_id_t	*lgrps_strong;
555 	lgrp_id_t	*lgrps_weak;
556 	int		nlgrps_none;
557 	int		nlgrps_strong;
558 	int		nlgrps_weak;
559 
560 	nlgrps_strong = nlgrps_weak = nlgrps_none = 0;
561 
562 	lgrps_strong = malloc(nelements * sizeof (lgrp_id_t));
563 	lgrps_weak = malloc(nelements * sizeof (lgrp_id_t));
564 	lgrps_none = malloc(nelements * sizeof (lgrp_id_t));
565 
566 	if (lgrps_strong == NULL || lgrps_weak == NULL || lgrps_none == NULL) {
567 		(void) fprintf(stderr, gettext("%s: out of memory\n"),
568 		    progname);
569 		interrupt = 1;
570 		return;
571 	}
572 
573 	/*
574 	 * Group lgroups by affinity
575 	 */
576 	for (i = 0; i < nelements; i++) {
577 		lgrp_id_t lgrpid = lgrps[i];
578 
579 		/*
580 		 * Skip any lgroups set to LGRP_NONE
581 		 */
582 		if (lgrpid == LGRP_NONE)
583 			continue;
584 
585 		switch (affs[i]) {
586 		case LGRP_AFF_STRONG:
587 			lgrps_strong[nlgrps_strong++] = lgrpid;
588 			break;
589 		case LGRP_AFF_WEAK:
590 			lgrps_weak[nlgrps_weak++] = lgrpid;
591 			break;
592 		case LGRP_AFF_NONE:
593 			lgrps_none[nlgrps_none++] = lgrpid;
594 			break;
595 		default:
596 			/*
597 			 * Skip any lgroups with invalid affinity.
598 			 */
599 			break;
600 		}
601 	}
602 
603 	/*
604 	 * Print all lgroups with same affinity together
605 	 */
606 	if (nlgrps_strong) {
607 		print_lgrps(lgrps_strong, nlgrps_strong);
608 		(void) printf("/%s", lgrp_affinity_string(LGRP_AFF_STRONG));
609 		if (nlgrps_weak || nlgrps_none)
610 			(void) printf("%c", DELIMIT_AFF_LST);
611 	}
612 
613 	if (nlgrps_weak) {
614 		print_lgrps(lgrps_weak, nlgrps_weak);
615 		(void) printf("/%s", lgrp_affinity_string(LGRP_AFF_WEAK));
616 		if (nlgrps_none)
617 			(void) printf("%c", DELIMIT_AFF_LST);
618 	}
619 
620 	if (nlgrps_none) {
621 		print_lgrps(lgrps_none, nlgrps_none);
622 		(void) printf("/%s", lgrp_affinity_string(LGRP_AFF_NONE));
623 	}
624 
625 	free(lgrps_strong);
626 	free(lgrps_weak);
627 	free(lgrps_none);
628 }
629 
630 
631 /*
632  * Print heading for specified operation
633  */
634 static void
635 print_heading(plgrp_ops_t op)
636 {
637 
638 	switch (op) {
639 	case PLGRP_AFFINITY_GET:
640 		(void) printf(HDR_PLGRP_AFF_GET);
641 		break;
642 
643 	case PLGRP_AFFINITY_SET:
644 		(void) printf(HDR_PLGRP_AFF_SET);
645 		break;
646 
647 	case PLGRP_HOME_GET:
648 		(void) printf(HDR_PLGRP_HOME_GET);
649 		break;
650 
651 	case PLGRP_HOME_SET:
652 		(void) printf(HDR_PLGRP_HOME_SET);
653 		break;
654 
655 	default:
656 		break;
657 	}
658 }
659 
660 /*
661  * Use /proc to call lgrp_affinity_get() in another process
662  */
663 static lgrp_affinity_t
664 Plgrp_affinity_get(struct ps_prochandle *Ph, idtype_t idtype, id_t id,
665     lgrp_id_t lgrp)
666 {
667 	lgrp_affinity_args_t	args;
668 	argdes_t		Pargd[3];
669 	argdes_t		*Pargdp;
670 	int			Pnargs;
671 	int			Pretval;
672 	sysret_t		retval;
673 	int			syscall;
674 
675 	/*
676 	 * Fill in arguments needed for syscall(SYS_lgrpsys,
677 	 * LGRP_SYS_AFFINITY_GET, 0, &args)
678 	 */
679 	syscall = SYS_lgrpsys;
680 
681 	args.idtype = idtype;
682 	args.id = id;
683 	args.lgrp = lgrp;
684 	args.aff = LGRP_AFF_INVALID;
685 
686 	/*
687 	 * Fill out /proc argument descriptors for syscall(SYS_lgrpsys,
688 	 * LGRP_SYS_AFFINITY_GET, idtype, id)
689 	 */
690 	Pnargs = LGRPSYS_NARGS;
691 	Pargdp = &Pargd[0];
692 	Pargdp->arg_value = LGRP_SYS_AFFINITY_GET;
693 	Pargdp->arg_object = NULL;
694 	Pargdp->arg_type = AT_BYVAL;
695 	Pargdp->arg_inout = AI_INPUT;
696 	Pargdp->arg_size = 0;
697 	Pargdp++;
698 
699 	Pargdp->arg_value = 0;
700 	Pargdp->arg_object = NULL;
701 	Pargdp->arg_type = AT_BYVAL;
702 	Pargdp->arg_inout = AI_INPUT;
703 	Pargdp->arg_size = 0;
704 	Pargdp++;
705 
706 	Pargdp->arg_value = 0;
707 	Pargdp->arg_object = &args;
708 	Pargdp->arg_type = AT_BYREF;
709 	Pargdp->arg_inout = AI_INPUT;
710 	Pargdp->arg_size = sizeof (lgrp_affinity_args_t);
711 	Pargdp++;
712 
713 	/*
714 	 * Have agent LWP call syscall with appropriate arguments in target
715 	 * process
716 	 */
717 	Pretval = Psyscall(Ph, &retval, syscall, Pnargs, &Pargd[0]);
718 	if (Pretval) {
719 		errno = (Pretval < 0) ? ENOSYS : Pretval;
720 		return (LGRP_AFF_INVALID);
721 	}
722 
723 	return (retval.sys_rval1);
724 }
725 
726 
727 /*
728  * Use /proc to call lgrp_affinity_set() in another process
729  */
730 static int
731 Plgrp_affinity_set(struct ps_prochandle *Ph, idtype_t idtype, id_t id,
732     lgrp_id_t lgrp, lgrp_affinity_t aff)
733 {
734 	lgrp_affinity_args_t	args;
735 	argdes_t		Pargd[3];
736 	argdes_t		*Pargdp;
737 	int			Pnargs;
738 	int			Pretval;
739 	sysret_t		retval;
740 	int			syscall;
741 
742 	/*
743 	 * Fill in arguments needed for syscall(SYS_lgrpsys,
744 	 * LGRP_SYS_AFFINITY_SET, 0, &args)
745 	 */
746 	syscall = SYS_lgrpsys;
747 
748 	args.idtype = idtype;
749 	args.id = id;
750 	args.lgrp = lgrp;
751 	args.aff = aff;
752 
753 	/*
754 	 * Fill out /proc argument descriptors for syscall(SYS_lgrpsys,
755 	 * LGRP_SYS_AFFINITY_SET, idtype, id)
756 	 */
757 	Pnargs = LGRPSYS_NARGS;
758 	Pargdp = &Pargd[0];
759 	Pargdp->arg_value = LGRP_SYS_AFFINITY_SET;
760 	Pargdp->arg_object = NULL;
761 	Pargdp->arg_type = AT_BYVAL;
762 	Pargdp->arg_inout = AI_INPUT;
763 	Pargdp->arg_size = 0;
764 	Pargdp++;
765 
766 	Pargdp->arg_value = 0;
767 	Pargdp->arg_object = NULL;
768 	Pargdp->arg_type = AT_BYVAL;
769 	Pargdp->arg_inout = AI_INPUT;
770 	Pargdp->arg_size = 0;
771 	Pargdp++;
772 
773 	Pargdp->arg_value = 0;
774 	Pargdp->arg_object = &args;
775 	Pargdp->arg_type = AT_BYREF;
776 	Pargdp->arg_inout = AI_INPUT;
777 	Pargdp->arg_size = sizeof (lgrp_affinity_args_t);
778 	Pargdp++;
779 
780 	/*
781 	 * Have agent LWP call syscall with appropriate arguments in
782 	 * target process
783 	 */
784 	Pretval = Psyscall(Ph, &retval, syscall, Pnargs, &Pargd[0]);
785 	if (Pretval) {
786 		errno = (Pretval < 0) ? ENOSYS : Pretval;
787 		return (-1);
788 	}
789 
790 	return (retval.sys_rval1);
791 }
792 
793 /*
794  * Use /proc to call lgrp_home() in another process
795  */
796 static lgrp_id_t
797 Plgrp_home(struct ps_prochandle *Ph, idtype_t idtype, id_t id)
798 {
799 	argdes_t		Pargd[3];
800 	argdes_t		*Pargdp;
801 	int			Pnargs;
802 	int			Pretval;
803 	sysret_t		retval;
804 	int			syscall;
805 
806 	/*
807 	 * Fill in arguments needed for syscall(SYS_lgrpsys,
808 	 * LGRP_SYS_HOME, idtype, id)
809 	 */
810 	syscall = SYS_lgrpsys;
811 
812 	/*
813 	 * Fill out /proc argument descriptors for syscall(SYS_lgrpsys,
814 	 * LGRP_SYS_HOME, idtype, id)
815 	 */
816 	Pnargs = LGRPSYS_NARGS;
817 	Pargdp = &Pargd[0];
818 	Pargdp->arg_value = LGRP_SYS_HOME;
819 	Pargdp->arg_object = NULL;
820 	Pargdp->arg_type = AT_BYVAL;
821 	Pargdp->arg_inout = AI_INPUT;
822 	Pargdp->arg_size = 0;
823 	Pargdp++;
824 
825 	Pargdp->arg_value = idtype;
826 	Pargdp->arg_object = NULL;
827 	Pargdp->arg_type = AT_BYVAL;
828 	Pargdp->arg_inout = AI_INPUT;
829 	Pargdp->arg_size = 0;
830 	Pargdp++;
831 
832 	Pargdp->arg_value = id;
833 	Pargdp->arg_object = NULL;
834 	Pargdp->arg_type = AT_BYVAL;
835 	Pargdp->arg_inout = AI_INPUT;
836 	Pargdp->arg_size = 0;
837 	Pargdp++;
838 
839 	/*
840 	 * Have agent LWP call syscall with appropriate arguments in
841 	 * target process
842 	 */
843 	Pretval = Psyscall(Ph, &retval, syscall, Pnargs, &Pargd[0]);
844 	if (Pretval) {
845 		errno = (Pretval < 0) ? ENOSYS : Pretval;
846 		return (-1);
847 	}
848 
849 	return (retval.sys_rval1);
850 }
851 
852 /*
853  * Use /proc to call lgrp_affinity_set(3LGRP) to set home lgroup of given
854  * thread
855  */
856 static int
857 Plgrp_home_set(struct ps_prochandle *Ph, idtype_t idtype, id_t id,
858     lgrp_id_t lgrp)
859 {
860 	return (Plgrp_affinity_set(Ph, idtype, id, lgrp,
861 	    LGRP_AFF_STRONG));
862 }
863 
864 
865 /*
866  * Do plgrp(1) operation on specified thread
867  */
868 static int
869 do_op(plgrp_args_t *plgrp_args, id_t pid, id_t lwpid,
870     const lwpsinfo_t *lwpsinfo)
871 {
872 	lgrp_affinity_t		*affs;
873 	lgrp_affinity_t		*cur_affs;
874 	lgrp_id_t		home;
875 	int			i;
876 	lgrp_affinity_t		*init_affs;
877 	lgrp_id_t		*lgrps;
878 	lgrp_id_t		*lgrps_changed;
879 	int			nlgrps;
880 	lgrp_id_t		old_home;
881 	lgrp_id_t		lgrpid;
882 	struct ps_prochandle	*Ph;
883 	int			nchanged;
884 
885 	/*
886 	 * No args, so nothing to do.
887 	 */
888 	if (plgrp_args == NULL)
889 		return (0);
890 
891 	/*
892 	 * Unpack plgrp(1) arguments and state needed to process this LWP
893 	 */
894 	Ph = plgrp_args->Ph;
895 	lgrps = plgrp_args->lgrps;
896 	affs = plgrp_args->affs;
897 	nlgrps = plgrp_args->nlgrps;
898 
899 	switch (plgrp_args->op) {
900 
901 	case PLGRP_HOME_GET:
902 		/*
903 		 * Get and display home lgroup for given LWP
904 		 */
905 		home = lwpsinfo->pr_lgrp;
906 		(void) printf(FMT_HOME"\n", (int)home);
907 		break;
908 
909 	case PLGRP_AFFINITY_GET:
910 		/*
911 		 * Get and display this LWP's home lgroup and affinities
912 		 * for specified lgroups
913 		 */
914 		home = lwpsinfo->pr_lgrp;
915 		(void) printf(FMT_HOME, (int)home);
916 
917 		/*
918 		 * Collect affinity values
919 		 */
920 		for (i = 0; i < nlgrps; i++) {
921 			affs[i] = Plgrp_affinity_get(Ph, P_LWPID, lwpid,
922 			    lgrps[i]);
923 
924 			if (affs[i] == LGRP_AFF_INVALID) {
925 				nerrors++;
926 				(void) fprintf(stderr,
927 				    gettext("%s: cannot get affinity"
928 					" for lgroup %d for %d/%d: %s\n"),
929 				    progname, lgrps[i], pid, lwpid,
930 				    strerror(errno));
931 			}
932 		}
933 
934 		/*
935 		 * Print affinities for each type.
936 		 */
937 		print_affinities(lgrps, affs, nlgrps);
938 		(void) printf("\n");
939 
940 		break;
941 
942 	case PLGRP_HOME_SET:
943 		/*
944 		 * Get home lgroup before and after setting it and display
945 		 * change.  If more than one lgroup and one LWP are specified,
946 		 * then home LWPs to lgroups in round robin fashion.
947 		 */
948 		old_home = lwpsinfo->pr_lgrp;
949 
950 		i = plgrp_args->index;
951 		if (Plgrp_home_set(Ph, P_LWPID, lwpid, lgrps[i]) != 0) {
952 			nerrors++;
953 			(void) fprintf(stderr,
954 			    gettext("%s: cannot set home lgroup of %d/%d"
955 				" to lgroup %d: %s\n"),
956 				progname, pid, lwpid, lgrps[i],
957 			    strerror(errno));
958 			(void) printf("\n");
959 		} else {
960 			int len;
961 			int width = strlen(HDR_PLGRP_HOME_CHANGE);
962 
963 			home = Plgrp_home(Ph, P_LWPID, lwpid);
964 
965 			if (home < 0) {
966 				(void) fprintf(stderr,
967 				    gettext("%s cannot get home lgroup for"
968 					" %d/%d: %s\n"),
969 				    progname, pid, lwpid, strerror(errno));
970 				nerrors++;
971 			}
972 
973 			len = printf(FMT_NEWHOME, (int)old_home, (int)home);
974 			if (len < width)
975 				(void) printf("%*c\n", (int)(width - len), ' ');
976 		}
977 
978 		plgrp_args->index = (i + 1) % nlgrps;
979 
980 		break;
981 
982 	case PLGRP_AFFINITY_SET:
983 		/*
984 		 * Set affinities for specified lgroups and print old and new
985 		 * affinities and any resulting change in home lgroups
986 		 */
987 
988 		/*
989 		 * Get initial home lgroup as it may change.
990 		 */
991 		old_home = lwpsinfo->pr_lgrp;
992 
993 		/*
994 		 * Need to allocate arrays indexed by lgroup (ID) for
995 		 * affinities and lgroups because user may specify affinity
996 		 * for same lgroup multiple times....
997 		 *
998 		 * Keeping these arrays by lgroup (ID) eliminates any
999 		 * duplication and makes it easier to just print initial and
1000 		 * final lgroup affinities (instead of trying to keep a list
1001 		 * of lgroups specified which may include duplicates)
1002 		 */
1003 		init_affs = malloc(NLGRPS * sizeof (lgrp_affinity_t));
1004 		cur_affs = malloc(NLGRPS * sizeof (lgrp_affinity_t));
1005 		lgrps_changed = malloc(NLGRPS * sizeof (lgrp_id_t));
1006 
1007 		if (init_affs == NULL || cur_affs == NULL ||
1008 		    lgrps_changed == NULL) {
1009 			(void) fprintf(stderr, gettext("%s: out of memory\n"),
1010 			    progname);
1011 			Prelease(Ph, PRELEASE_RETAIN);
1012 			exit(EXIT_NONFATAL);
1013 		}
1014 
1015 		/*
1016 		 * Initialize current and initial lgroup affinities and
1017 		 * lgroups changed
1018 		 */
1019 		for (lgrpid = 0; lgrpid < NLGRPS; lgrpid++) {
1020 
1021 			if (!LGRP_VALID(lgrpid)) {
1022 				init_affs[lgrpid] = LGRP_AFF_INVALID;
1023 			} else {
1024 				init_affs[lgrpid] =
1025 				    Plgrp_affinity_get(Ph, P_LWPID,
1026 					lwpid, lgrpid);
1027 
1028 				if (init_affs[lgrpid] == LGRP_AFF_INVALID) {
1029 					nerrors++;
1030 					(void) fprintf(stderr,
1031 					    gettext("%s: cannot get"
1032 						" affinity for lgroup %d"
1033 						" for %d/%d: %s\n"),
1034 					    progname, lgrpid, pid, lwpid,
1035 					    strerror(errno));
1036 				}
1037 			}
1038 
1039 			cur_affs[lgrpid] = init_affs[lgrpid];
1040 			lgrps_changed[lgrpid] = LGRP_NONE;
1041 		}
1042 
1043 		/*
1044 		 * Change affinities.
1045 		 */
1046 		for (i = 0; i < nlgrps; i++) {
1047 			lgrp_affinity_t	aff = affs[i];
1048 
1049 			lgrpid = lgrps[i];
1050 
1051 			/*
1052 			 * If the suggested affinity is the same as the current
1053 			 * one, skip this lgroup.
1054 			 */
1055 			if (aff == cur_affs[lgrpid])
1056 				continue;
1057 
1058 			/*
1059 			 * Set affinity to the new value
1060 			 */
1061 			if (Plgrp_affinity_set(Ph, P_LWPID, lwpid, lgrpid,
1062 				aff) < 0) {
1063 				nerrors++;
1064 				(void) fprintf(stderr,
1065 				    gettext("%s: cannot set"
1066 					" %s affinity for lgroup %d"
1067 					" for %d/%d: %s\n"),
1068 				    progname, lgrp_affinity_string(aff),
1069 				    lgrpid, pid, lwpid,
1070 				    strerror(errno));
1071 				continue;
1072 			}
1073 
1074 			/*
1075 			 * Get the new value and verify that it changed as
1076 			 * expected.
1077 			 */
1078 			cur_affs[lgrpid] =
1079 			    Plgrp_affinity_get(Ph, P_LWPID, lwpid, lgrpid);
1080 
1081 			if (cur_affs[lgrpid] == LGRP_AFF_INVALID) {
1082 				nerrors++;
1083 				(void) fprintf(stderr,
1084 				    gettext("%s: cannot get"
1085 					" affinity for lgroup %d"
1086 					" for %d/%d: %s\n"),
1087 				    progname, lgrpid, pid, lwpid,
1088 				    strerror(errno));
1089 				continue;
1090 			}
1091 
1092 			if (aff != cur_affs[lgrpid]) {
1093 				(void) fprintf(stderr,
1094 				    gettext("%s: affinity for"
1095 					" lgroup %d is set to %d instead of %d"
1096 					" for %d/%d\n"),
1097 				    progname, lgrpid, cur_affs[lgrpid], aff,
1098 				    pid, lwpid);
1099 				nerrors++;
1100 			}
1101 		}
1102 
1103 		/*
1104 		 * Compare current and initial affinities and mark lgroups with
1105 		 * changed affinities.
1106 		 */
1107 		nchanged = 0;
1108 		for (lgrpid = 0; lgrpid < NLGRPS; lgrpid++) {
1109 			if (init_affs[lgrpid] != cur_affs[lgrpid]) {
1110 				lgrps_changed[lgrpid] = lgrpid;
1111 				nchanged++;
1112 			}
1113 		}
1114 
1115 		if (nchanged == 0) {
1116 			/*
1117 			 * Nothing changed, so just print current affinities for
1118 			 * specified lgroups.
1119 			 */
1120 			for (i = 0; i < nlgrps; i++) {
1121 				lgrps_changed[lgrps[i]] = lgrps[i];
1122 			}
1123 
1124 			(void) printf("%-*d",
1125 			    (int)strlen(HDR_PLGRP_HOME_CHANGE),
1126 			    (int)old_home);
1127 
1128 			print_affinities(lgrps_changed, cur_affs, NLGRPS);
1129 			(void) printf("\n");
1130 		} else {
1131 			int width = strlen(HDR_PLGRP_HOME_CHANGE);
1132 
1133 			/*
1134 			 * Some lgroup affinities changed, so display old
1135 			 * and new home lgroups for thread and its old and new
1136 			 * affinities for affected lgroups
1137 			 */
1138 			home = Plgrp_home(Ph, P_LWPID, lwpid);
1139 			if (home < 0) {
1140 				(void) fprintf(stderr,
1141 				    gettext("%s: cannot get home"
1142 					" for %d/%d: %s\n"),
1143 				    progname, pid, lwpid, strerror(errno));
1144 				nerrors++;
1145 			}
1146 			if (old_home != home) {
1147 				int len;
1148 
1149 				/*
1150 				 * Fit string into fixed width
1151 				 */
1152 				len = printf(FMT_NEWHOME,
1153 				    (int)old_home, (int)home);
1154 				if (len < width)
1155 					(void) printf("%*c", width - len, ' ');
1156 			} else {
1157 				(void) printf("%-*d", width, (int)home);
1158 			}
1159 
1160 			/*
1161 			 * Print change in affinities from old to new
1162 			 */
1163 			print_affinities(lgrps_changed, init_affs, NLGRPS);
1164 			(void) printf(" => ");
1165 			print_affinities(lgrps_changed, cur_affs, NLGRPS);
1166 			(void) printf("\n");
1167 		}
1168 
1169 		free(lgrps_changed);
1170 		free(init_affs);
1171 		free(cur_affs);
1172 
1173 		break;
1174 
1175 	default:
1176 		break;
1177 	}
1178 
1179 	return (0);
1180 }
1181 
1182 
1183 /*
1184  * Routine called by Plwp_iter_all() as it iterates through LWPs of another
1185  * process
1186  */
1187 /* ARGSUSED */
1188 static int
1189 Plwp_iter_handler(void *arg, const lwpstatus_t *lwpstatus,
1190     const lwpsinfo_t *lwpsinfo)
1191 {
1192 	id_t			lwpid;
1193 	struct ps_prochandle	*Ph;
1194 	const pstatus_t		*pstatus;
1195 	plgrp_args_t		*plgrp_args;
1196 
1197 	/*
1198 	 * Nothing to do if no arguments
1199 	 */
1200 	if (arg == NULL || interrupt)
1201 		return (0);
1202 
1203 	/*
1204 	 * Unpack plgrp(1) arguments and state needed to process this LWP
1205 	 */
1206 	plgrp_args = arg;
1207 	Ph = plgrp_args->Ph;
1208 
1209 	/*
1210 	 * Just return if no /proc handle for process
1211 	 */
1212 	if (Ph == NULL)
1213 		return (0);
1214 
1215 	pstatus = Pstatus(Ph);
1216 
1217 	/*
1218 	 * Skip agent LWP and any LWPs that weren't specified
1219 	 */
1220 	lwpid = lwpsinfo->pr_lwpid;
1221 	if (lwpid == pstatus->pr_agentid ||
1222 	    !proc_lwp_in_set(plgrp_args->lwps, lwpid))
1223 		return (0);
1224 
1225 	plgrp_args->nthreads++;
1226 
1227 	/*
1228 	 * Do all plgrp(1) operations specified on given thread
1229 	 */
1230 	(void) printf(FMT_THREAD" ", (int)pstatus->pr_pid, (int)lwpid);
1231 	return (do_op(plgrp_args, pstatus->pr_pid, lwpid, lwpsinfo));
1232 }
1233 
1234 /*
1235  * Get target process specified in "pidstring" argument to do operation(s)
1236  * specified in "plgrp_todo" using /proc and agent LWP
1237  */
1238 static void
1239 do_process(char *pidstring, plgrp_args_t *plgrp_todo, int force)
1240 {
1241 	int			error;
1242 	const char		*lwps;
1243 	struct ps_prochandle	*Ph;
1244 
1245 	/*
1246 	 * Nothing to do, so return.
1247 	 */
1248 	if (plgrp_todo == NULL || interrupt)
1249 		return;
1250 
1251 	/*
1252 	 * Grab target process or core and return
1253 	 * /proc handle for process and string of LWP
1254 	 * IDs
1255 	 */
1256 	Ph = proc_arg_xgrab(pidstring, NULL,
1257 	    PR_ARG_ANY, force | PGRAB_RETAIN | PGRAB_NOSTOP, &error, &lwps);
1258 	if (Ph == NULL) {
1259 		(void) fprintf(stderr,
1260 		    gettext("%s: Unable to grab process %s: %s\n"),
1261 		    progname, pidstring, Pgrab_error(error));
1262 		nerrors++;
1263 		return;
1264 	}
1265 
1266 	/*
1267 	 * Fill in remaining plgrp(1) arguments and state needed to do
1268 	 * plgrp(1) operation(s) on desired LWPs in our handler
1269 	 * called by Plwp_iter_all() as it iterates over LWPs
1270 	 * in given process
1271 	 */
1272 	plgrp_todo->Ph = Ph;
1273 	plgrp_todo->lwps = lwps;
1274 
1275 	/*
1276 	 * Iterate over LWPs in process and do specified
1277 	 * operation(s) on those specified
1278 	 */
1279 	if (Plwp_iter_all(Ph, Plwp_iter_handler, plgrp_todo) != 0) {
1280 		(void) fprintf(stderr,
1281 		    gettext("%s: error iterating over threads\n"),
1282 		    progname);
1283 		nerrors++;
1284 	}
1285 
1286 	Prelease(Ph, PRELEASE_RETAIN);
1287 }
1288 
1289 
1290 /*
1291  * Parse command line and kick off any resulting actions
1292  *
1293  * plgrp(1) has the following command line syntax:
1294  *
1295  *	plgrp [-h] <pid> | <core> [/lwps] ...
1296  *	plgrp [-F] -a <lgroup>,... <pid>[/lwps] ...
1297  *	plgrp [-F] -H <lgroup>,... <pid>[/lwps] ...
1298  *	plgrp [-F] -A <lgroup>,... [/none|weak|strong] ... <pid>[/lwps] ...
1299  *
1300  *	where <lgroup> is an lgroup ID, "all", "root", "leaves".
1301  */
1302 int
1303 main(int argc, char *argv[])
1304 {
1305 	lgrp_affinity_t		aff;
1306 	char			*affstring;
1307 	int			c;
1308 	lgrp_cookie_t		cookie;
1309 	int			Fflag;
1310 	int			i;
1311 	int			opt_seen;
1312 	plgrp_args_t		plgrp_todo;
1313 	char			*s;
1314 
1315 	(void) setlocale(LC_ALL, "");
1316 	(void) textdomain(TEXT_DOMAIN);
1317 
1318 	opt_seen = 0;
1319 
1320 	/*
1321 	 * Get name of program
1322 	 */
1323 	progname = basename(argv[0]);
1324 
1325 	/*
1326 	 * Not much to do when only name of program given
1327 	 */
1328 	if (argc == 1)
1329 		usage(0);
1330 
1331 	/*
1332 	 * Catch signals from terminal, so they can be handled asynchronously
1333 	 * when we're ready instead of when we're not (;-)
1334 	 */
1335 	if (sigset(SIGHUP, SIG_IGN) == SIG_DFL)
1336 		(void) sigset(SIGHUP, intr);
1337 	if (sigset(SIGINT, SIG_IGN) == SIG_DFL)
1338 		(void) sigset(SIGINT, intr);
1339 	if (sigset(SIGQUIT, SIG_IGN) == SIG_DFL)
1340 		(void) sigset(SIGQUIT, intr);
1341 	(void) sigset(SIGPIPE, intr);
1342 	(void) sigset(SIGTERM, intr);
1343 
1344 	/*
1345 	 * Take snapshot of lgroup hierarchy
1346 	 */
1347 	cookie = lgrp_init(LGRP_VIEW_OS);
1348 	if (cookie == LGRP_COOKIE_NONE) {
1349 		(void) fprintf(stderr,
1350 		    gettext("%s: Fatal error: cannot get lgroup"
1351 			" information from the OS: %s\n"),
1352 		    progname, strerror(errno));
1353 		return (EXIT_FAILURE);
1354 	}
1355 
1356 	root = lgrp_root(cookie);
1357 	lgrps_bitmap_init(cookie, root, &lgrps_bitmap, &lgrps_bitmap_nelements);
1358 
1359 	/*
1360 	 * Remember arguments and state needed to do plgrp(1) operation
1361 	 * on desired LWPs
1362 	 */
1363 	bzero(&plgrp_todo, sizeof (plgrp_args_t));
1364 	plgrp_todo.op = PLGRP_HOME_GET;
1365 
1366 	/*
1367 	 * Parse options
1368 	 */
1369 	opterr = 0;
1370 	Fflag = 0;
1371 	while (!interrupt && (c = getopt(argc, argv, "a:A:FhH:")) != -1) {
1372 		/*
1373 		 * Parse option and only allow one option besides -F to be
1374 		 * specified
1375 		 */
1376 		switch (c) {
1377 
1378 		case 'h':	/* Get home lgroup */
1379 			/*
1380 			 * Only allow one option (besides -F) to be specified
1381 			 */
1382 			if (opt_seen)
1383 				usage(EXIT_FAILURE);
1384 			opt_seen = 1;
1385 
1386 			plgrp_todo.op = PLGRP_HOME_GET;
1387 			break;
1388 
1389 		case 'H':	/* Set home lgroup */
1390 
1391 			/*
1392 			 * Fail if already specified option (besides -F)
1393 			 * or no more arguments
1394 			 */
1395 			if (opt_seen || optind >= argc) {
1396 				usage(EXIT_FAILURE);
1397 			}
1398 			opt_seen = 1;
1399 
1400 			plgrp_todo.op = PLGRP_HOME_SET;
1401 
1402 			if (parse_lgrps(cookie, &plgrp_todo, optarg) < 0)
1403 				usage(EXIT_FAILURE);
1404 
1405 			/* If there are no valid lgroups exit immediately */
1406 			if (plgrp_todo.nlgrps == 0) {
1407 				(void) fprintf(stderr,
1408 				    gettext("%s: no valid lgroups"
1409 					" specified for -%c\n\n"),
1410 				    progname, c);
1411 				    usage(EXIT_FAILURE);
1412 			}
1413 
1414 			break;
1415 
1416 		case 'a':	/* Get lgroup affinity */
1417 
1418 			/*
1419 			 * Fail if already specified option (besides -F)
1420 			 * or no more arguments
1421 			 */
1422 			if (opt_seen || optind >= argc) {
1423 				usage(EXIT_FAILURE);
1424 			}
1425 			opt_seen = 1;
1426 
1427 			plgrp_todo.op = PLGRP_AFFINITY_GET;
1428 
1429 			if (parse_lgrps(cookie, &plgrp_todo, optarg) < 0)
1430 				usage(EXIT_FAILURE);
1431 
1432 			/* If there are no valid lgroups exit immediately */
1433 			if (plgrp_todo.nlgrps == 0) {
1434 				(void) fprintf(stderr,
1435 				    gettext("%s: no valid lgroups specified"
1436 					" for -%c\n\n"),
1437 				    progname, c);
1438 				    usage(EXIT_FAILURE);
1439 			}
1440 
1441 			break;
1442 
1443 		case 'A':	/* Set lgroup affinity */
1444 
1445 			/*
1446 			 * Fail if already specified option (besides -F)
1447 			 * or no more arguments
1448 			 */
1449 			if (opt_seen || optind >= argc) {
1450 				usage(EXIT_FAILURE);
1451 			}
1452 			opt_seen = 1;
1453 
1454 			plgrp_todo.op = PLGRP_AFFINITY_SET;
1455 
1456 			/*
1457 			 * 'affstring' is the unparsed prtion of the affinity
1458 			 * specification like 1,2/none,2/weak,0/strong
1459 			 *
1460 			 * 'next' is the next affinity specification to parse.
1461 			 */
1462 			affstring = optarg;
1463 			while (affstring != NULL && strlen(affstring) > 0) {
1464 				char	*next;
1465 
1466 				/*
1467 				 * affstring points to the first affinity
1468 				 * specification. Split the string by
1469 				 * DELIMIT_AFF separator and parse lgroups and
1470 				 * affinity value separately.
1471 				 */
1472 				s = strchr(affstring, DELIMIT_AFF);
1473 				if (s == NULL) {
1474 					(void) fprintf(stderr,
1475 					    gettext("%s: invalid "
1476 						"syntax >%s<\n"),
1477 					    progname, affstring);
1478 					usage(EXIT_FAILURE);
1479 				}
1480 
1481 				aff = parse_lgrp_affinity(s, &next);
1482 				if (aff == LGRP_AFF_INVALID) {
1483 					(void) fprintf(stderr,
1484 					    gettext("%s: invalid "
1485 						"affinity >%s<\n"),
1486 					    progname, affstring);
1487 					usage(EXIT_FAILURE);
1488 				}
1489 
1490 				/*
1491 				 * next should either point to the empty string
1492 				 * or to the DELIMIT_AFF_LST separator.
1493 				 */
1494 				if (*next != '\0') {
1495 					if (*next != DELIMIT_AFF_LST) {
1496 						(void) fprintf(stderr,
1497 						    gettext("%s: invalid "
1498 							"syntax >%s<\n"),
1499 						    progname, next);
1500 						usage(EXIT_FAILURE);
1501 					}
1502 					*next = '\0';
1503 					next++;
1504 				}
1505 
1506 
1507 				/*
1508 				 * Now parse the list of lgroups
1509 				 */
1510 				if (parse_lgrps(cookie, &plgrp_todo,
1511 					affstring) < 0) {
1512 					usage(EXIT_FAILURE);
1513 				}
1514 
1515 				/*
1516 				 * Set desired affinity for specified lgroup to
1517 				 * the specified affinity.
1518 				 */
1519 				for (i = 0; i < plgrp_todo.nlgrps; i++) {
1520 					if (plgrp_todo.affs[i] ==
1521 					    LGRP_AFF_INVALID)
1522 						plgrp_todo.affs[i] = aff;
1523 				}
1524 
1525 				/*
1526 				 * We processed the leftmost element of the
1527 				 * list. Advance affstr to the remaining part of
1528 				 * the list. and repeat.
1529 				 */
1530 				affstring = next;
1531 			}
1532 
1533 			/*
1534 			 * If there are no valid lgroups, exit immediately
1535 			 */
1536 			if (plgrp_todo.nlgrps == 0) {
1537 				(void) fprintf(stderr,
1538 				    gettext("%s: no valid lgroups specified "
1539 				    "for -%c\n\n"), progname, c);
1540 				    usage(EXIT_FAILURE);
1541 			}
1542 
1543 			break;
1544 
1545 		case 'F':	/* Force */
1546 
1547 			/*
1548 			 * Only allow one occurrence
1549 			 */
1550 			if (Fflag != 0) {
1551 				usage(EXIT_FAILURE);
1552 			}
1553 
1554 			/*
1555 			 * Set flag to force /proc to grab process even though
1556 			 * it's been grabbed by another process already
1557 			 */
1558 			Fflag = PGRAB_FORCE;
1559 			break;
1560 
1561 		case '?':	/* Unrecognized option */
1562 		default:
1563 			usage(EXIT_FAILURE);
1564 			break;
1565 
1566 		}
1567 	}
1568 
1569 	/*
1570 	 * Should have more arguments left at least for PID or core
1571 	 */
1572 	if (optind >= argc)
1573 		usage(EXIT_FAILURE);
1574 
1575 	(void) lgrp_fini(cookie);
1576 
1577 	/*
1578 	 * Print heading and process each [pid | core]/lwps argument
1579 	 */
1580 	print_heading(plgrp_todo.op);
1581 	for (i = optind; i < argc && !interrupt; i++) {
1582 		do_process(argv[i], &plgrp_todo, Fflag);
1583 	}
1584 
1585 	if (plgrp_todo.nthreads == 0) {
1586 		(void) fprintf(stderr, gettext("%s: no matching LWPs found\n"),
1587 		    progname);
1588 	}
1589 
1590 	return ((nerrors ||interrupt) ? EXIT_NONFATAL : EXIT_SUCCESS);
1591 }
1592