xref: /illumos-gate/usr/src/cmd/ptools/plgrp/plgrp.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
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 2008 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 			if (init_affs != NULL)
1013 				free(init_affs);
1014 			if (cur_affs != NULL)
1015 				free(cur_affs);
1016 			nerrors++;
1017 			return (EXIT_NONFATAL);
1018 		}
1019 
1020 		/*
1021 		 * Initialize current and initial lgroup affinities and
1022 		 * lgroups changed
1023 		 */
1024 		for (lgrpid = 0; lgrpid < NLGRPS; lgrpid++) {
1025 
1026 			if (!LGRP_VALID(lgrpid)) {
1027 				init_affs[lgrpid] = LGRP_AFF_INVALID;
1028 			} else {
1029 				init_affs[lgrpid] =
1030 				    Plgrp_affinity_get(Ph, P_LWPID,
1031 					lwpid, lgrpid);
1032 
1033 				if (init_affs[lgrpid] == LGRP_AFF_INVALID) {
1034 					nerrors++;
1035 					(void) fprintf(stderr,
1036 					    gettext("%s: cannot get"
1037 						" affinity for lgroup %d"
1038 						" for %d/%d: %s\n"),
1039 					    progname, lgrpid, pid, lwpid,
1040 					    strerror(errno));
1041 				}
1042 			}
1043 
1044 			cur_affs[lgrpid] = init_affs[lgrpid];
1045 			lgrps_changed[lgrpid] = LGRP_NONE;
1046 		}
1047 
1048 		/*
1049 		 * Change affinities.
1050 		 */
1051 		for (i = 0; i < nlgrps; i++) {
1052 			lgrp_affinity_t	aff = affs[i];
1053 
1054 			lgrpid = lgrps[i];
1055 
1056 			/*
1057 			 * If the suggested affinity is the same as the current
1058 			 * one, skip this lgroup.
1059 			 */
1060 			if (aff == cur_affs[lgrpid])
1061 				continue;
1062 
1063 			/*
1064 			 * Set affinity to the new value
1065 			 */
1066 			if (Plgrp_affinity_set(Ph, P_LWPID, lwpid, lgrpid,
1067 				aff) < 0) {
1068 				nerrors++;
1069 				(void) fprintf(stderr,
1070 				    gettext("%s: cannot set"
1071 					" %s affinity for lgroup %d"
1072 					" for %d/%d: %s\n"),
1073 				    progname, lgrp_affinity_string(aff),
1074 				    lgrpid, pid, lwpid,
1075 				    strerror(errno));
1076 				continue;
1077 			}
1078 
1079 			/*
1080 			 * Get the new value and verify that it changed as
1081 			 * expected.
1082 			 */
1083 			cur_affs[lgrpid] =
1084 			    Plgrp_affinity_get(Ph, P_LWPID, lwpid, lgrpid);
1085 
1086 			if (cur_affs[lgrpid] == LGRP_AFF_INVALID) {
1087 				nerrors++;
1088 				(void) fprintf(stderr,
1089 				    gettext("%s: cannot get"
1090 					" affinity for lgroup %d"
1091 					" for %d/%d: %s\n"),
1092 				    progname, lgrpid, pid, lwpid,
1093 				    strerror(errno));
1094 				continue;
1095 			}
1096 
1097 			if (aff != cur_affs[lgrpid]) {
1098 				(void) fprintf(stderr,
1099 				    gettext("%s: affinity for"
1100 					" lgroup %d is set to %d instead of %d"
1101 					" for %d/%d\n"),
1102 				    progname, lgrpid, cur_affs[lgrpid], aff,
1103 				    pid, lwpid);
1104 				nerrors++;
1105 			}
1106 		}
1107 
1108 		/*
1109 		 * Compare current and initial affinities and mark lgroups with
1110 		 * changed affinities.
1111 		 */
1112 		nchanged = 0;
1113 		for (lgrpid = 0; lgrpid < NLGRPS; lgrpid++) {
1114 			if (init_affs[lgrpid] != cur_affs[lgrpid]) {
1115 				lgrps_changed[lgrpid] = lgrpid;
1116 				nchanged++;
1117 			}
1118 		}
1119 
1120 		if (nchanged == 0) {
1121 			/*
1122 			 * Nothing changed, so just print current affinities for
1123 			 * specified lgroups.
1124 			 */
1125 			for (i = 0; i < nlgrps; i++) {
1126 				lgrps_changed[lgrps[i]] = lgrps[i];
1127 			}
1128 
1129 			(void) printf("%-*d",
1130 			    (int)strlen(HDR_PLGRP_HOME_CHANGE),
1131 			    (int)old_home);
1132 
1133 			print_affinities(lgrps_changed, cur_affs, NLGRPS);
1134 			(void) printf("\n");
1135 		} else {
1136 			int width = strlen(HDR_PLGRP_HOME_CHANGE);
1137 
1138 			/*
1139 			 * Some lgroup affinities changed, so display old
1140 			 * and new home lgroups for thread and its old and new
1141 			 * affinities for affected lgroups
1142 			 */
1143 			home = Plgrp_home(Ph, P_LWPID, lwpid);
1144 			if (home < 0) {
1145 				(void) fprintf(stderr,
1146 				    gettext("%s: cannot get home"
1147 					" for %d/%d: %s\n"),
1148 				    progname, pid, lwpid, strerror(errno));
1149 				nerrors++;
1150 			}
1151 			if (old_home != home) {
1152 				int len;
1153 
1154 				/*
1155 				 * Fit string into fixed width
1156 				 */
1157 				len = printf(FMT_NEWHOME,
1158 				    (int)old_home, (int)home);
1159 				if (len < width)
1160 					(void) printf("%*c", width - len, ' ');
1161 			} else {
1162 				(void) printf("%-*d", width, (int)home);
1163 			}
1164 
1165 			/*
1166 			 * Print change in affinities from old to new
1167 			 */
1168 			print_affinities(lgrps_changed, init_affs, NLGRPS);
1169 			(void) printf(" => ");
1170 			print_affinities(lgrps_changed, cur_affs, NLGRPS);
1171 			(void) printf("\n");
1172 		}
1173 
1174 		free(lgrps_changed);
1175 		free(init_affs);
1176 		free(cur_affs);
1177 
1178 		break;
1179 
1180 	default:
1181 		break;
1182 	}
1183 
1184 	return (0);
1185 }
1186 
1187 
1188 /*
1189  * Routine called by Plwp_iter_all() as it iterates through LWPs of another
1190  * process
1191  */
1192 /* ARGSUSED */
1193 static int
1194 Plwp_iter_handler(void *arg, const lwpstatus_t *lwpstatus,
1195     const lwpsinfo_t *lwpsinfo)
1196 {
1197 	id_t			lwpid;
1198 	struct ps_prochandle	*Ph;
1199 	const pstatus_t		*pstatus;
1200 	plgrp_args_t		*plgrp_args;
1201 
1202 	/*
1203 	 * Nothing to do if no arguments
1204 	 */
1205 	if (arg == NULL || interrupt)
1206 		return (0);
1207 
1208 	/*
1209 	 * Unpack plgrp(1) arguments and state needed to process this LWP
1210 	 */
1211 	plgrp_args = arg;
1212 	Ph = plgrp_args->Ph;
1213 
1214 	/*
1215 	 * Just return if no /proc handle for process
1216 	 */
1217 	if (Ph == NULL)
1218 		return (0);
1219 
1220 	pstatus = Pstatus(Ph);
1221 
1222 	/*
1223 	 * Skip agent LWP and any LWPs that weren't specified
1224 	 */
1225 	lwpid = lwpsinfo->pr_lwpid;
1226 	if (lwpid == pstatus->pr_agentid ||
1227 	    !proc_lwp_in_set(plgrp_args->lwps, lwpid))
1228 		return (0);
1229 
1230 	plgrp_args->nthreads++;
1231 
1232 	/*
1233 	 * Do all plgrp(1) operations specified on given thread
1234 	 */
1235 	(void) printf(FMT_THREAD" ", (int)pstatus->pr_pid, (int)lwpid);
1236 	return (do_op(plgrp_args, pstatus->pr_pid, lwpid, lwpsinfo));
1237 }
1238 
1239 /*
1240  * Get target process specified in "pidstring" argument to do operation(s)
1241  * specified in "plgrp_todo" using /proc and agent LWP
1242  */
1243 static void
1244 do_process(char *pidstring, plgrp_args_t *plgrp_todo, int force)
1245 {
1246 	int			error;
1247 	const char		*lwps;
1248 	struct ps_prochandle	*Ph;
1249 
1250 	/*
1251 	 * Nothing to do, so return.
1252 	 */
1253 	if (plgrp_todo == NULL || interrupt)
1254 		return;
1255 
1256 	/*
1257 	 * Grab target process or core and return
1258 	 * /proc handle for process and string of LWP
1259 	 * IDs
1260 	 */
1261 	Ph = proc_arg_xgrab(pidstring, NULL,
1262 	    PR_ARG_ANY, force | PGRAB_RETAIN | PGRAB_NOSTOP, &error, &lwps);
1263 	if (Ph == NULL) {
1264 		(void) fprintf(stderr,
1265 		    gettext("%s: Unable to grab process %s: %s\n"),
1266 		    progname, pidstring, Pgrab_error(error));
1267 		nerrors++;
1268 		return;
1269 	}
1270 
1271 	/*
1272 	 * Fill in remaining plgrp(1) arguments and state needed to do
1273 	 * plgrp(1) operation(s) on desired LWPs in our handler
1274 	 * called by Plwp_iter_all() as it iterates over LWPs
1275 	 * in given process
1276 	 */
1277 	plgrp_todo->Ph = Ph;
1278 	plgrp_todo->lwps = lwps;
1279 
1280 	/*
1281 	 * Iterate over LWPs in process and do specified
1282 	 * operation(s) on those specified
1283 	 */
1284 	if (Plwp_iter_all(Ph, Plwp_iter_handler, plgrp_todo) != 0) {
1285 		(void) fprintf(stderr,
1286 		    gettext("%s: error iterating over threads\n"),
1287 		    progname);
1288 		nerrors++;
1289 	}
1290 
1291 	Prelease(Ph, PRELEASE_RETAIN);
1292 }
1293 
1294 
1295 /*
1296  * Parse command line and kick off any resulting actions
1297  *
1298  * plgrp(1) has the following command line syntax:
1299  *
1300  *	plgrp [-h] <pid> | <core> [/lwps] ...
1301  *	plgrp [-F] -a <lgroup>,... <pid>[/lwps] ...
1302  *	plgrp [-F] -H <lgroup>,... <pid>[/lwps] ...
1303  *	plgrp [-F] -A <lgroup>,... [/none|weak|strong] ... <pid>[/lwps] ...
1304  *
1305  *	where <lgroup> is an lgroup ID, "all", "root", "leaves".
1306  */
1307 int
1308 main(int argc, char *argv[])
1309 {
1310 	lgrp_affinity_t		aff;
1311 	char			*affstring;
1312 	int			c;
1313 	lgrp_cookie_t		cookie;
1314 	int			Fflag;
1315 	int			i;
1316 	int			opt_seen;
1317 	plgrp_args_t		plgrp_todo;
1318 	char			*s;
1319 
1320 	(void) setlocale(LC_ALL, "");
1321 	(void) textdomain(TEXT_DOMAIN);
1322 
1323 	opt_seen = 0;
1324 
1325 	/*
1326 	 * Get name of program
1327 	 */
1328 	progname = basename(argv[0]);
1329 
1330 	/*
1331 	 * Not much to do when only name of program given
1332 	 */
1333 	if (argc == 1)
1334 		usage(0);
1335 
1336 	/*
1337 	 * Catch signals from terminal, so they can be handled asynchronously
1338 	 * when we're ready instead of when we're not (;-)
1339 	 */
1340 	if (sigset(SIGHUP, SIG_IGN) == SIG_DFL)
1341 		(void) sigset(SIGHUP, intr);
1342 	if (sigset(SIGINT, SIG_IGN) == SIG_DFL)
1343 		(void) sigset(SIGINT, intr);
1344 	if (sigset(SIGQUIT, SIG_IGN) == SIG_DFL)
1345 		(void) sigset(SIGQUIT, intr);
1346 	(void) sigset(SIGPIPE, intr);
1347 	(void) sigset(SIGTERM, intr);
1348 
1349 	/*
1350 	 * Take snapshot of lgroup hierarchy
1351 	 */
1352 	cookie = lgrp_init(LGRP_VIEW_OS);
1353 	if (cookie == LGRP_COOKIE_NONE) {
1354 		(void) fprintf(stderr,
1355 		    gettext("%s: Fatal error: cannot get lgroup"
1356 			" information from the OS: %s\n"),
1357 		    progname, strerror(errno));
1358 		return (EXIT_FAILURE);
1359 	}
1360 
1361 	root = lgrp_root(cookie);
1362 	lgrps_bitmap_init(cookie, root, &lgrps_bitmap, &lgrps_bitmap_nelements);
1363 
1364 	/*
1365 	 * Remember arguments and state needed to do plgrp(1) operation
1366 	 * on desired LWPs
1367 	 */
1368 	bzero(&plgrp_todo, sizeof (plgrp_args_t));
1369 	plgrp_todo.op = PLGRP_HOME_GET;
1370 
1371 	/*
1372 	 * Parse options
1373 	 */
1374 	opterr = 0;
1375 	Fflag = 0;
1376 	while (!interrupt && (c = getopt(argc, argv, "a:A:FhH:")) != -1) {
1377 		/*
1378 		 * Parse option and only allow one option besides -F to be
1379 		 * specified
1380 		 */
1381 		switch (c) {
1382 
1383 		case 'h':	/* Get home lgroup */
1384 			/*
1385 			 * Only allow one option (besides -F) to be specified
1386 			 */
1387 			if (opt_seen)
1388 				usage(EXIT_FAILURE);
1389 			opt_seen = 1;
1390 
1391 			plgrp_todo.op = PLGRP_HOME_GET;
1392 			break;
1393 
1394 		case 'H':	/* Set home lgroup */
1395 
1396 			/*
1397 			 * Fail if already specified option (besides -F)
1398 			 * or no more arguments
1399 			 */
1400 			if (opt_seen || optind >= argc) {
1401 				usage(EXIT_FAILURE);
1402 			}
1403 			opt_seen = 1;
1404 
1405 			plgrp_todo.op = PLGRP_HOME_SET;
1406 
1407 			if (parse_lgrps(cookie, &plgrp_todo, optarg) < 0)
1408 				usage(EXIT_FAILURE);
1409 
1410 			/* If there are no valid lgroups exit immediately */
1411 			if (plgrp_todo.nlgrps == 0) {
1412 				(void) fprintf(stderr,
1413 				    gettext("%s: no valid lgroups"
1414 					" specified for -%c\n\n"),
1415 				    progname, c);
1416 				    usage(EXIT_FAILURE);
1417 			}
1418 
1419 			break;
1420 
1421 		case 'a':	/* Get lgroup affinity */
1422 
1423 			/*
1424 			 * Fail if already specified option (besides -F)
1425 			 * or no more arguments
1426 			 */
1427 			if (opt_seen || optind >= argc) {
1428 				usage(EXIT_FAILURE);
1429 			}
1430 			opt_seen = 1;
1431 
1432 			plgrp_todo.op = PLGRP_AFFINITY_GET;
1433 
1434 			if (parse_lgrps(cookie, &plgrp_todo, optarg) < 0)
1435 				usage(EXIT_FAILURE);
1436 
1437 			/* If there are no valid lgroups exit immediately */
1438 			if (plgrp_todo.nlgrps == 0) {
1439 				(void) fprintf(stderr,
1440 				    gettext("%s: no valid lgroups specified"
1441 					" for -%c\n\n"),
1442 				    progname, c);
1443 				    usage(EXIT_FAILURE);
1444 			}
1445 
1446 			break;
1447 
1448 		case 'A':	/* Set lgroup affinity */
1449 
1450 			/*
1451 			 * Fail if already specified option (besides -F)
1452 			 * or no more arguments
1453 			 */
1454 			if (opt_seen || optind >= argc) {
1455 				usage(EXIT_FAILURE);
1456 			}
1457 			opt_seen = 1;
1458 
1459 			plgrp_todo.op = PLGRP_AFFINITY_SET;
1460 
1461 			/*
1462 			 * 'affstring' is the unparsed prtion of the affinity
1463 			 * specification like 1,2/none,2/weak,0/strong
1464 			 *
1465 			 * 'next' is the next affinity specification to parse.
1466 			 */
1467 			affstring = optarg;
1468 			while (affstring != NULL && strlen(affstring) > 0) {
1469 				char	*next;
1470 
1471 				/*
1472 				 * affstring points to the first affinity
1473 				 * specification. Split the string by
1474 				 * DELIMIT_AFF separator and parse lgroups and
1475 				 * affinity value separately.
1476 				 */
1477 				s = strchr(affstring, DELIMIT_AFF);
1478 				if (s == NULL) {
1479 					(void) fprintf(stderr,
1480 					    gettext("%s: invalid "
1481 						"syntax >%s<\n"),
1482 					    progname, affstring);
1483 					usage(EXIT_FAILURE);
1484 				}
1485 
1486 				aff = parse_lgrp_affinity(s, &next);
1487 				if (aff == LGRP_AFF_INVALID) {
1488 					(void) fprintf(stderr,
1489 					    gettext("%s: invalid "
1490 						"affinity >%s<\n"),
1491 					    progname, affstring);
1492 					usage(EXIT_FAILURE);
1493 				}
1494 
1495 				/*
1496 				 * next should either point to the empty string
1497 				 * or to the DELIMIT_AFF_LST separator.
1498 				 */
1499 				if (*next != '\0') {
1500 					if (*next != DELIMIT_AFF_LST) {
1501 						(void) fprintf(stderr,
1502 						    gettext("%s: invalid "
1503 							"syntax >%s<\n"),
1504 						    progname, next);
1505 						usage(EXIT_FAILURE);
1506 					}
1507 					*next = '\0';
1508 					next++;
1509 				}
1510 
1511 
1512 				/*
1513 				 * Now parse the list of lgroups
1514 				 */
1515 				if (parse_lgrps(cookie, &plgrp_todo,
1516 					affstring) < 0) {
1517 					usage(EXIT_FAILURE);
1518 				}
1519 
1520 				/*
1521 				 * Set desired affinity for specified lgroup to
1522 				 * the specified affinity.
1523 				 */
1524 				for (i = 0; i < plgrp_todo.nlgrps; i++) {
1525 					if (plgrp_todo.affs[i] ==
1526 					    LGRP_AFF_INVALID)
1527 						plgrp_todo.affs[i] = aff;
1528 				}
1529 
1530 				/*
1531 				 * We processed the leftmost element of the
1532 				 * list. Advance affstr to the remaining part of
1533 				 * the list. and repeat.
1534 				 */
1535 				affstring = next;
1536 			}
1537 
1538 			/*
1539 			 * If there are no valid lgroups, exit immediately
1540 			 */
1541 			if (plgrp_todo.nlgrps == 0) {
1542 				(void) fprintf(stderr,
1543 				    gettext("%s: no valid lgroups specified "
1544 				    "for -%c\n\n"), progname, c);
1545 				    usage(EXIT_FAILURE);
1546 			}
1547 
1548 			break;
1549 
1550 		case 'F':	/* Force */
1551 
1552 			/*
1553 			 * Only allow one occurrence
1554 			 */
1555 			if (Fflag != 0) {
1556 				usage(EXIT_FAILURE);
1557 			}
1558 
1559 			/*
1560 			 * Set flag to force /proc to grab process even though
1561 			 * it's been grabbed by another process already
1562 			 */
1563 			Fflag = PGRAB_FORCE;
1564 			break;
1565 
1566 		case '?':	/* Unrecognized option */
1567 		default:
1568 			usage(EXIT_FAILURE);
1569 			break;
1570 
1571 		}
1572 	}
1573 
1574 	/*
1575 	 * Should have more arguments left at least for PID or core
1576 	 */
1577 	if (optind >= argc)
1578 		usage(EXIT_FAILURE);
1579 
1580 	(void) lgrp_fini(cookie);
1581 
1582 	/*
1583 	 * Print heading and process each [pid | core]/lwps argument
1584 	 */
1585 	print_heading(plgrp_todo.op);
1586 	(void) proc_initstdio();
1587 
1588 	for (i = optind; i < argc && !interrupt; i++) {
1589 		(void) proc_flushstdio();
1590 		do_process(argv[i], &plgrp_todo, Fflag);
1591 	}
1592 
1593 	(void) proc_finistdio();
1594 
1595 	if (plgrp_todo.nthreads == 0) {
1596 		(void) fprintf(stderr, gettext("%s: no matching LWPs found\n"),
1597 		    progname);
1598 	}
1599 
1600 	return ((nerrors ||interrupt) ? EXIT_NONFATAL : EXIT_SUCCESS);
1601 }
1602