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