xref: /illumos-gate/usr/src/cmd/psrset/psrset.c (revision 7adfacf32dfbb1d0705cc8a39ffa7fc8a1aeff66)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 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  * psrset - create and manage processor sets
31  */
32 
33 #include <sys/types.h>
34 #include <sys/procset.h>
35 #include <sys/processor.h>
36 #include <sys/pset.h>
37 #include <fcntl.h>
38 #include <stdio.h>
39 #include <errno.h>
40 #include <dirent.h>
41 #include <locale.h>
42 #include <string.h>
43 #include <limits.h>
44 #include <procfs.h>
45 #include <libproc.h>
46 #include <stdarg.h>
47 #include <priv.h>
48 
49 #if !defined(TEXT_DOMAIN)		/* should be defined by cc -D */
50 #define	TEXT_DOMAIN 	"SYS_TEST"	/* Use this only if it wasn't */
51 #endif
52 
53 #define	MAX_PROCFS_PATH	80
54 
55 #define	ERR_OK		0		/* exit status for success */
56 #define	ERR_FAIL	1		/* exit status for errors */
57 #define	ERR_USAGE	2		/* exit status for usage errors */
58 
59 static char *progname;
60 static int errors;
61 static char cflag;
62 static char dflag;
63 static char aflag;
64 static char rflag;
65 static char iflag;
66 static char bflag;
67 static char uflag;
68 static char Uflag;
69 static char qflag;
70 static char Qflag;
71 static char pflag;
72 static char nflag;
73 static char fflag;
74 static char Fflag;
75 static char eflag;
76 
77 extern int pset_assign_forced(psetid_t, processorid_t, psetid_t *);
78 
79 /*PRINTFLIKE1*/
80 static void
81 warn(char *format, ...)
82 {
83 	int err = errno;
84 	va_list alist;
85 
86 	(void) fprintf(stderr, "%s: ", progname);
87 	va_start(alist, format);
88 	(void) vfprintf(stderr, format, alist);
89 	va_end(alist);
90 	if (strchr(format, '\n') == NULL)
91 		(void) fprintf(stderr, ": %s\n", strerror(err));
92 }
93 
94 /*PRINTFLIKE1*/
95 static void
96 die(char *format, ...)
97 {
98 	int err = errno;
99 	va_list alist;
100 
101 	(void) fprintf(stderr, "%s: ", progname);
102 	va_start(alist, format);
103 	(void) vfprintf(stderr, format, alist);
104 	va_end(alist);
105 	if (strchr(format, '\n') == NULL)
106 		(void) fprintf(stderr, ": %s\n", strerror(err));
107 	exit(ERR_FAIL);
108 }
109 
110 static prpriv_t *orig_priv;
111 
112 static struct ps_prochandle *
113 grab_proc(id_t pid)
114 {
115 	int ret;
116 	prpriv_t *new_priv;
117 	priv_set_t *eff_set, *perm_set;
118 	struct ps_prochandle *Pr;
119 
120 	if ((Pr = Pgrab(pid, 0, &ret)) == NULL) {
121 		warn(gettext("cannot control process %d: %s\n"),
122 		    (int)pid, Pgrab_error(ret));
123 		errors = ERR_FAIL;
124 		return (NULL);
125 	}
126 
127 	/*
128 	 * If target process does not have required PRIV_SYS_RES_CONFIG
129 	 * privilege, assign it temporarily to that process' effective
130 	 * and permitted sets so that it can call pset_bind(2).
131 	 */
132 	if ((new_priv = proc_get_priv(pid)) == NULL) {
133 		warn(gettext("unable to get privileges for process %d: %s\n"),
134 		    pid, strerror(errno));
135 		errors = ERR_FAIL;
136 		Prelease(Pr, 0);
137 		return (NULL);
138 	}
139 
140 	if (Pcreate_agent(Pr) != 0) {
141 		warn(gettext("cannot control process %d\n"), (int)pid);
142 		errors = ERR_FAIL;
143 		Prelease(Pr, 0);
144 		free(new_priv);
145 		return (NULL);
146 	}
147 
148 	eff_set = (priv_set_t *)&new_priv->pr_sets[new_priv->pr_setsize *
149 	    priv_getsetbyname(PRIV_EFFECTIVE)];
150 	perm_set = (priv_set_t *)&new_priv->pr_sets[new_priv->pr_setsize *
151 	    priv_getsetbyname(PRIV_PERMITTED)];
152 	if (!priv_ismember(eff_set, PRIV_SYS_RES_CONFIG)) {
153 		/*
154 		 * Save original privileges
155 		 */
156 		if ((orig_priv = proc_get_priv(pid)) == NULL) {
157 			warn(gettext("unable to get privileges for "
158 			    "process %d: %s\n"), pid, strerror(errno));
159 			errors = ERR_FAIL;
160 			Pdestroy_agent(Pr);
161 			Prelease(Pr, 0);
162 			free(new_priv);
163 			return (NULL);
164 		}
165 		(void) priv_addset(eff_set, PRIV_SYS_RES_CONFIG);
166 		(void) priv_addset(perm_set, PRIV_SYS_RES_CONFIG);
167 		/*
168 		 * We don't want to leave a process with elevated privileges,
169 		 * so make sure the process dies if we exit unexpectedly.
170 		 */
171 		if (Psetflags(Pr, PR_KLC) != 0 ||
172 		    Psetpriv(Pr, new_priv) != 0) {
173 			warn(gettext("unable to set process privileges for "
174 			    "process %d: %s\n"), pid, strerror(errno));
175 			(void) Punsetflags(Pr, PR_KLC);
176 			free(new_priv);
177 			free(orig_priv);
178 			Pdestroy_agent(Pr);
179 			Prelease(Pr, 0);
180 			orig_priv = NULL;
181 			errors = ERR_FAIL;
182 			return (NULL);
183 		}
184 	}
185 	free(new_priv);
186 
187 	return (Pr);
188 }
189 
190 static void
191 rele_proc(struct ps_prochandle *Pr)
192 {
193 	if (Pr == NULL)
194 		return;
195 	if (orig_priv != NULL) {
196 		if (Psetpriv(Pr, orig_priv) != 0) {
197 			/*
198 			 * If this fails, we can't leave a process with
199 			 * elevated privileges, so we have to release the
200 			 * process from libproc, knowing that it will
201 			 * be killed (since we set the PR_KLC flag).
202 			 */
203 			Pdestroy_agent(Pr);
204 			warn(gettext("cannot relinquish privileges for "
205 			    "process %d.  The process was killed\n"),
206 			    Ppsinfo(Pr)->pr_pid);
207 			errors = ERR_FAIL;
208 		} else {
209 			(void) Punsetflags(Pr, PR_KLC);
210 			Pdestroy_agent(Pr);
211 		}
212 		free(orig_priv);
213 		orig_priv = NULL;
214 	} else {
215 		Pdestroy_agent(Pr);
216 	}
217 	Prelease(Pr, 0);
218 }
219 
220 static void
221 bind_err(psetid_t pset, id_t pid, id_t lwpid, int err)
222 {
223 	char    *msg;
224 
225 	switch (pset) {
226 	case PS_NONE:
227 		msg = gettext("unbind");
228 		break;
229 	case PS_QUERY:
230 		msg = gettext("query");
231 		break;
232 	default:
233 		msg = gettext("bind");
234 		break;
235 	}
236 
237 	errno = err;
238 	if (lwpid == -1)
239 		warn(gettext("cannot %s pid %d"), msg, pid);
240 	else
241 		warn(gettext("cannot %s lwpid %d/%d"), msg, pid, lwpid);
242 }
243 
244 /*
245  * Output for create.
246  */
247 static void
248 create_out(psetid_t pset)
249 {
250 	(void) printf("%s %d\n", gettext("created processor set"), pset);
251 }
252 
253 /*
254  * Output for assign.
255  */
256 static void
257 assign_out(processorid_t cpu, psetid_t old, psetid_t new)
258 {
259 	if (old == PS_NONE) {
260 		if (new == PS_NONE)
261 			(void) printf(gettext("processor %d: was not assigned,"
262 			    " now not assigned\n"), cpu);
263 		else
264 			(void) printf(gettext("processor %d: was not assigned,"
265 			    " now %d\n"), cpu, new);
266 	} else {
267 		if (new == PS_NONE)
268 			(void) printf(gettext("processor %d: was %d, "
269 			    "now not assigned\n"), cpu, old);
270 		else
271 			(void) printf(gettext("processor %d: was %d, "
272 			    "now %d\n"), cpu, old, new);
273 	}
274 }
275 
276 /*
277  * Output for query.
278  */
279 static void
280 query_out(id_t pid, id_t lwpid, psetid_t pset)
281 {
282 	char *proclwp;
283 	char pidstr[21];
284 
285 	if (lwpid == -1) {
286 		(void) snprintf(pidstr, 20, "%d", pid);
287 		proclwp = "process";
288 	} else {
289 		(void) snprintf(pidstr, 20, "%d/%d", pid, lwpid);
290 		proclwp = "lwp";
291 	}
292 
293 	if (pset == PS_NONE)
294 		(void) printf(gettext("%s id %s: not bound\n"),
295 		    proclwp, pidstr);
296 	else
297 		(void) printf(gettext("%s id %s: %d\n"), proclwp, pidstr, pset);
298 }
299 
300 /*
301  * Output for info.
302  */
303 static void
304 info_out(psetid_t pset, int type, uint_t numcpus, processorid_t *cpus)
305 {
306 	int i;
307 	if (type == PS_SYSTEM)
308 		(void) printf(gettext("system processor set %d:"), pset);
309 	else
310 		(void) printf(gettext("user processor set %d:"), pset);
311 	if (numcpus == 0)
312 		(void) printf(gettext(" empty"));
313 	else if (numcpus > 1)
314 		(void) printf(gettext(" processors"));
315 	else
316 		(void) printf(gettext(" processor"));
317 	for (i = 0; i < numcpus; i++)
318 		(void) printf(" %d", cpus[i]);
319 	(void) printf("\n");
320 }
321 
322 /*
323  * Output for print.
324  */
325 static void
326 print_out(processorid_t cpu, psetid_t pset)
327 {
328 	if (pset == PS_NONE)
329 		(void) printf(gettext("processor %d: not assigned\n"), cpu);
330 	else
331 		(void) printf(gettext("processor %d: %d\n"), cpu, pset);
332 }
333 
334 /*
335  * Output for bind.
336  */
337 static void
338 bind_out(id_t pid, id_t lwpid, psetid_t old, psetid_t new)
339 {
340 	char *proclwp;
341 	char pidstr[21];
342 
343 	if (lwpid == -1) {
344 		(void) snprintf(pidstr, 20, "%d", pid);
345 		proclwp = "process";
346 	} else {
347 		(void) snprintf(pidstr, 20, "%d/%d", pid, lwpid);
348 		proclwp = "lwp";
349 	}
350 
351 	if (old == PS_NONE) {
352 		if (new == PS_NONE)
353 			(void) printf(gettext("%s id %s: was not bound, "
354 				"now not bound\n"), proclwp, pidstr);
355 		else
356 			(void) printf(gettext("%s id %s: was not bound, "
357 				"now %d\n"), proclwp, pidstr, new);
358 	} else {
359 		if (new == PS_NONE)
360 			(void) printf(gettext("%s id %s: was %d, "
361 				"now not bound\n"), proclwp, pidstr, old);
362 		else
363 			(void) printf(gettext("%s id %s: was %d, "
364 				"now %d\n"), proclwp, pidstr, old, new);
365 	}
366 }
367 
368 static void
369 bind_lwp(struct ps_prochandle *Pr, id_t pid, id_t lwpid, psetid_t pset)
370 {
371 	psetid_t old_pset;
372 
373 	if (pr_pset_bind(Pr, pset, P_LWPID, lwpid, &old_pset) < 0) {
374 		bind_err(pset, pid, lwpid, errno);
375 		errors = ERR_FAIL;
376 	} else {
377 		if (qflag)
378 			query_out(pid, lwpid, old_pset);
379 		else
380 			bind_out(pid, lwpid, old_pset, pset);
381 	}
382 }
383 
384 static int
385 do_cpu(psetid_t pset, processorid_t cpu, int print, int mustexist)
386 {
387 	psetid_t old_pset;
388 	int err;
389 
390 	if ((!Fflag && pset_assign(pset, cpu, &old_pset) != 0) ||
391 	    (Fflag && pset_assign_forced(pset, cpu, &old_pset) != 0)) {
392 		if (errno == EINVAL && !mustexist)
393 			return (EINVAL);
394 		err = errno;
395 
396 		switch (pset) {
397 		case PS_NONE:
398 			warn(gettext("cannot remove processor %d"), cpu);
399 			break;
400 		case PS_QUERY:
401 			warn(gettext("cannot query processor %d"), cpu);
402 			break;
403 		default:
404 			warn(gettext("cannot assign processor %d"), cpu);
405 			break;
406 		}
407 		return (err);
408 	}
409 	if (print)
410 		print_out(cpu, old_pset);
411 	else
412 		assign_out(cpu, old_pset, pset);
413 	return (0);
414 }
415 
416 static int
417 do_range(psetid_t pset, processorid_t first, processorid_t last, int print)
418 {
419 	processorid_t cpu;
420 	int error = ERR_OK;
421 	int err;
422 	int found_one = 0;
423 
424 	for (cpu = first; cpu <= last; cpu++) {
425 		if ((err = do_cpu(pset, cpu, print, 0)) == 0)
426 			found_one = 1;
427 		else if (err != EINVAL)
428 			error = ERR_FAIL;
429 	}
430 	if (!found_one && error == ERR_OK) {
431 		warn(gettext("no processors in range %d-%d\n"), first, last);
432 		error = ERR_FAIL;
433 	}
434 	return (error);
435 }
436 
437 static int
438 do_info(psetid_t pset)
439 {
440 	int	type;
441 	uint_t	numcpus;
442 	processorid_t	*cpus;
443 
444 	numcpus = (uint_t)sysconf(_SC_NPROCESSORS_MAX);
445 	cpus = (processorid_t *)
446 	    malloc(numcpus * sizeof (processorid_t));
447 	if (cpus == NULL) {
448 		warn(gettext("memory allocation failed"));
449 		return (ERR_FAIL);
450 	}
451 	if (pset_info(pset, &type, &numcpus, cpus) != 0) {
452 		warn(gettext("cannot get info for processor set %d"), pset);
453 		free(cpus);
454 		return (ERR_FAIL);
455 	}
456 	info_out(pset, type, numcpus, cpus);
457 	free(cpus);
458 	return (ERR_OK);
459 }
460 
461 static int
462 do_destroy(psetid_t pset)
463 {
464 	if (pset_destroy(pset) != 0) {
465 		warn(gettext("could not remove processor set %d"), pset);
466 		return (ERR_FAIL);
467 	}
468 	(void) printf(gettext("removed processor set %d\n"), pset);
469 	return (ERR_OK);
470 }
471 
472 static int
473 do_intr(psetid_t pset, int flag)
474 {
475 	uint_t i, numcpus;
476 	processorid_t *cpus;
477 	int error = ERR_OK;
478 
479 	numcpus = (uint_t)sysconf(_SC_NPROCESSORS_MAX);
480 	cpus = (processorid_t *)
481 	    malloc(numcpus * sizeof (processorid_t));
482 	if (cpus == NULL) {
483 		warn(gettext("memory allocation failed"));
484 		return (ERR_FAIL);
485 	}
486 	if (pset_info(pset, NULL, &numcpus, cpus) != 0) {
487 		warn(gettext(
488 		    "cannot set interrupt status for processor set %d"), pset);
489 		free(cpus);
490 		return (ERR_FAIL);
491 	}
492 	for (i = 0; i < numcpus; i++) {
493 		int status = p_online(cpus[i], P_STATUS);
494 		if (status != P_OFFLINE && status != P_POWEROFF &&
495 		    status != flag) {
496 			if (p_online(cpus[i], flag) == -1) {
497 				warn(gettext("processor %d"), cpus[i]);
498 				error = ERR_FAIL;
499 			}
500 		}
501 	}
502 	free(cpus);
503 	return (error);
504 }
505 
506 /*
507  * Query the type and CPUs for all active processor sets in the system.
508  */
509 static int
510 info_all(void)
511 {
512 	psetid_t *psetlist;
513 	uint_t	npsets, oldnpsets;
514 	int	i;
515 	int	errors = ERR_OK;
516 
517 	if (pset_list(NULL, &npsets) != 0) {
518 		warn(gettext("cannot get number of processor sets"));
519 		return (1);
520 	}
521 	for (;;) {
522 		psetlist = malloc(sizeof (psetid_t) * npsets);
523 		if (psetlist == NULL) {
524 			warn(gettext("memory allocation failed"));
525 			return (ERR_FAIL);
526 		}
527 		oldnpsets = npsets;
528 		if (pset_list(psetlist, &npsets) != 0) {
529 			warn(gettext("cannot get list of processor sets"));
530 			free(psetlist);
531 			return (ERR_FAIL);
532 		}
533 		if (npsets <= oldnpsets)
534 			break;
535 		free(psetlist);
536 	}
537 
538 	for (i = 0; i < npsets; i++) {
539 		if (do_info(psetlist[i]))
540 			errors = ERR_FAIL;
541 	}
542 	free(psetlist);
543 	return (errors);
544 }
545 
546 /*
547  * Query the processor set assignments for all CPUs in the system.
548  */
549 static int
550 print_all(void)
551 {
552 	psetid_t	pset;
553 	processorid_t cpuid, max_cpuid;
554 	int	errors = ERR_OK;
555 
556 	max_cpuid = (processorid_t)sysconf(_SC_CPUID_MAX);
557 	for (cpuid = 0; cpuid <= max_cpuid; cpuid++) {
558 		if (pset_assign(PS_QUERY, cpuid, &pset) == 0) {
559 			if (pset != PS_NONE)
560 				print_out(cpuid, pset);
561 		} else if (errno != EINVAL) {
562 			warn(gettext("cannot query processor %d"), cpuid);
563 			errors = ERR_FAIL;
564 		}
565 	}
566 	return (errors);
567 }
568 
569 /*ARGSUSED*/
570 static int
571 query_all_proc(psinfo_t *psinfo, lwpsinfo_t *lwpsinfo, void *arg)
572 {
573 	id_t pid = psinfo->pr_pid;
574 	psetid_t binding;
575 
576 	if (pset_bind(PS_QUERY, P_PID, pid, &binding) < 0) {
577 		/*
578 		 * Ignore search errors.  The process may have exited
579 		 * since we read the directory.
580 		 */
581 		if (errno == ESRCH)
582 			return (0);
583 		bind_err(PS_QUERY, pid, -1, errno);
584 		errors = ERR_FAIL;
585 		return (0);
586 	}
587 	if (binding != PS_NONE)
588 		query_out(pid, -1, binding);
589 	return (0);
590 }
591 
592 static int
593 query_all_lwp(psinfo_t *psinfo, lwpsinfo_t *lwpsinfo, void *arg)
594 {
595 	id_t pid = psinfo->pr_pid;
596 	id_t lwpid = lwpsinfo->pr_lwpid;
597 	psetid_t *cpuid = arg;
598 	psetid_t binding = lwpsinfo->pr_bindpset;
599 
600 	if (psinfo->pr_nlwp == 1)
601 		lwpid = -1;	/* report process bindings if only 1 lwp */
602 	if ((cpuid != NULL && *cpuid == binding) ||
603 	    (cpuid == NULL && binding != PBIND_NONE))
604 		query_out(pid, lwpid, binding);
605 	return (0);
606 }
607 
608 void
609 exec_cmd(psetid_t pset, char **argv)
610 {
611 	if (pset_bind(pset, P_PID, P_MYID, NULL) != 0) {
612 		warn(gettext("cannot exec in processor set %d"), pset);
613 		return;
614 	}
615 
616 	(void) execvp(argv[0], argv);
617 	warn(gettext("cannot exec command %s"), argv[0]);
618 }
619 
620 int
621 usage(void)
622 {
623 	(void) fprintf(stderr, gettext(
624 		"usage: \n"
625 		"\t%1$s -c [-F] [processor_id ...]\n"
626 		"\t%1$s -d processor_set_id ...\n"
627 		"\t%1$s -n processor_set_id\n"
628 		"\t%1$s -f processor_set_id\n"
629 		"\t%1$s -e processor_set_id command [argument(s)...]\n"
630 		"\t%1$s -a [-F] processor_set_id processor_id ...\n"
631 		"\t%1$s -r [-F] processor_id ...\n"
632 		"\t%1$s -p [processorid ...]\n"
633 		"\t%1$s -b processor_set_id pid[/lwpids] ...\n"
634 		"\t%1$s -u pid[/lwpids] ...\n"
635 		"\t%1$s -q [pid[/lwpids] ...]\n"
636 		"\t%1$s -U [processor_set_id] ...\n"
637 		"\t%1$s -Q [processor_set_id] ...\n"
638 		"\t%1$s [-i] [processor_set_id ...]\n"),
639 		progname);
640 	return (ERR_USAGE);
641 }
642 
643 /*
644  * Query, set, or clear bindings for the range of LWPs in the given process.
645  */
646 static int
647 do_lwps(id_t pid, const char *range, psetid_t pset)
648 {
649 	char procfile[MAX_PROCFS_PATH];
650 	struct ps_prochandle *Pr;
651 	struct prheader header;
652 	struct lwpsinfo *lwp;
653 	char *lpsinfo, *ptr;
654 	psetid_t binding;
655 	int nent, size;
656 	int i, fd, found;
657 
658 	/*
659 	 * Report bindings for LWPs in process 'pid'.
660 	 */
661 	(void) snprintf(procfile, MAX_PROCFS_PATH,
662 	    "/proc/%d/lpsinfo", (int)pid);
663 	if ((fd = open(procfile, O_RDONLY)) < 0) {
664 		if (errno == ENOENT)
665 			errno = ESRCH;
666 		bind_err(pset, pid, -1, errno);
667 		return (ERR_FAIL);
668 	}
669 	if (pread(fd, &header, sizeof (header), 0) != sizeof (header)) {
670 		(void) close(fd);
671 		bind_err(pset, pid, -1, errno);
672 		return (ERR_FAIL);
673 	}
674 	nent = header.pr_nent;
675 	size = header.pr_entsize * nent;
676 	ptr = lpsinfo = malloc(size);
677 	if (lpsinfo == NULL) {
678 		bind_err(pset, pid, -1, errno);
679 		return (ERR_FAIL);
680 	}
681 	if (pread(fd, lpsinfo, size, sizeof (header)) != size) {
682 		bind_err(pset, pid, -1, errno);
683 		free(lpsinfo);
684 		(void) close(fd);
685 		return (ERR_FAIL);
686 	}
687 
688 	if ((bflag || uflag) && (Pr = grab_proc(pid)) == NULL) {
689 		free(lpsinfo);
690 		(void) close(fd);
691 		return (ERR_FAIL);
692 	}
693 	found = 0;
694 	for (i = 0; i < nent; i++, ptr += header.pr_entsize) {
695 		/*LINTED ALIGNMENT*/
696 		lwp = (lwpsinfo_t *)ptr;
697 		binding = lwp->pr_bindpset;
698 		if (!proc_lwp_in_set(range, lwp->pr_lwpid))
699 			continue;
700 		found++;
701 		if (bflag || uflag)
702 			bind_lwp(Pr, pid, lwp->pr_lwpid, pset);
703 		else if (binding != PBIND_NONE)
704 			query_out(pid, lwp->pr_lwpid, binding);
705 	}
706 	if (bflag || uflag)
707 		rele_proc(Pr);
708 	free(lpsinfo);
709 	(void) close(fd);
710 	if (found == 0) {
711 		warn(gettext("cannot %s lwpid %d/%s: "
712 		    "No matching LWPs found\n"),
713 		    bflag ? "bind" : "query", pid, range);
714 		return (ERR_FAIL);
715 	}
716 	return (ERR_OK);
717 }
718 
719 int
720 main(int argc, char *argv[])
721 {
722 	extern int optind;
723 	int	c;
724 	id_t	pid;
725 	processorid_t	cpu;
726 	psetid_t	pset, old_pset;
727 	char	*errptr;
728 
729 	progname = argv[0];	/* put actual command name in messages */
730 
731 	(void) setlocale(LC_ALL, "");	/* setup localization */
732 	(void) textdomain(TEXT_DOMAIN);
733 
734 	while ((c = getopt(argc, argv, "cdFarpibqQuUnfe")) != EOF) {
735 		switch (c) {
736 		case 'c':
737 			cflag = 1;
738 			break;
739 		case 'd':
740 			dflag = 1;
741 			break;
742 		case 'e':
743 			eflag = 1;
744 			break;
745 		case 'a':
746 			aflag = 1;
747 			break;
748 		case 'r':
749 			rflag = 1;
750 			pset = PS_NONE;
751 			break;
752 		case 'p':
753 			pflag = 1;
754 			pset = PS_QUERY;
755 			break;
756 		case 'i':
757 			iflag = 1;
758 			break;
759 		case 'b':
760 			bflag = 1;
761 			break;
762 		case 'u':
763 			uflag = 1;
764 			pset = PS_NONE;
765 			break;
766 		case 'U':
767 			Uflag = 1;
768 			break;
769 		case 'q':
770 			qflag = 1;
771 			pset = PS_QUERY;
772 			break;
773 		case 'Q':
774 			Qflag = 1;
775 			break;
776 		case 'f':
777 			fflag = 1;
778 			break;
779 		case 'F':
780 			Fflag = 1;
781 			break;
782 		case 'n':
783 			nflag = 1;
784 			break;
785 		default:
786 			return (usage());
787 		}
788 	}
789 
790 	/*
791 	 * Make sure that at most one of the options was specified.
792 	 */
793 	c = cflag + dflag + aflag + rflag + pflag +
794 	    iflag + bflag + uflag + Uflag +
795 	    qflag + Qflag + fflag + nflag + eflag;
796 	if (c < 1) {				/* nothing specified */
797 		iflag = 1;			/* default is to get info */
798 	} else if (c > 1) {
799 		warn(gettext("options are mutually exclusive\n"));
800 		return (usage());
801 	}
802 
803 	if (Fflag && (cflag + aflag + rflag == 0))
804 		return (usage());
805 
806 	errors = 0;
807 	argc -= optind;
808 	argv += optind;
809 
810 	if (argc == 0) {
811 		/*
812 		 * Handle single option cases.
813 		 */
814 		if (qflag) {
815 			(void) proc_walk(query_all_proc, NULL, PR_WALK_PROC);
816 			return (errors);
817 		}
818 		if (Qflag) {
819 			(void) proc_walk(query_all_lwp, NULL, PR_WALK_LWP);
820 			return (errors);
821 		}
822 		if (Uflag) {
823 			if (pset_bind(PS_NONE, P_ALL, 0, &old_pset) != 0)
824 				die(gettext("failed to unbind all LWPs"));
825 		}
826 		if (pflag)
827 			return (print_all());
828 		if (iflag)
829 			return (info_all());
830 	}
831 
832 	/*
833 	 * Get processor set id.
834 	 */
835 	if (aflag || bflag || fflag || nflag || eflag) {
836 		if (argc < 1) {
837 			/* must specify processor set */
838 			warn(gettext("must specify processor set\n"));
839 			return (usage());
840 		}
841 		pset = strtol(*argv, &errptr, 10);
842 		if (errptr != NULL && *errptr != '\0' || pset < 0) {
843 			warn(gettext("invalid processor set ID %s\n"), *argv);
844 			return (ERR_FAIL);
845 		}
846 		argv++;
847 		argc--;
848 	}
849 
850 	if (cflag) {
851 		if (pset_create(&pset) != 0) {
852 			warn(gettext("could not create processor set"));
853 			return (ERR_FAIL);
854 		} else {
855 			create_out(pset);
856 			if (argc == 0)
857 				return (ERR_OK);
858 		}
859 	} else if (iflag || dflag) {
860 		if (argc == 0) {
861 			warn(gettext("must specify at least one "
862 			    "processor set\n"));
863 			return (usage());
864 		}
865 		/*
866 		 * Go through listed processor sets.
867 		 */
868 		for (; argc > 0; argv++, argc--) {
869 			pset = (psetid_t)strtol(*argv, &errptr, 10);
870 			if (errptr != NULL && *errptr != '\0') {
871 				warn(gettext("invalid processor set ID %s\n"),
872 				    *argv);
873 				errors = ERR_FAIL;
874 				continue;
875 			}
876 			if (iflag) {
877 				errors = do_info(pset);
878 			} else {
879 				errors = do_destroy(pset);
880 			}
881 		}
882 	} else if (nflag) {
883 		errors = do_intr(pset, P_ONLINE);
884 	} else if (fflag) {
885 		errors = do_intr(pset, P_NOINTR);
886 	} else if (eflag) {
887 		if (argc == 0) {
888 			warn(gettext("must specify command\n"));
889 			return (usage());
890 		}
891 		exec_cmd(pset, argv);
892 		/* if returning, must have had an error */
893 		return (ERR_USAGE);
894 	}
895 
896 	if (cflag || aflag || rflag || pflag) {
897 		/*
898 		 * Perform function for each processor specified.
899 		 */
900 		if (argc == 0) {
901 			warn(gettext("must specify at least one processor\n"));
902 			return (usage());
903 		}
904 
905 		/*
906 		 * Go through listed processors.
907 		 */
908 		for (; argc > 0; argv++, argc--) {
909 			if (strchr(*argv, '-') == NULL) {
910 				/* individual processor id */
911 				cpu = (processorid_t)strtol(*argv, &errptr, 10);
912 				if (errptr != NULL && *errptr != '\0') {
913 					warn(gettext("invalid processor "
914 					    "ID %s\n"), *argv);
915 					errors = ERR_FAIL;
916 					continue;
917 				}
918 				if (do_cpu(pset, cpu, pflag, 1))
919 					errors = ERR_FAIL;
920 			} else {
921 				/* range of processors */
922 				processorid_t first, last;
923 
924 				first = (processorid_t)
925 				    strtol(*argv, &errptr, 10);
926 				if (*errptr++ != '-') {
927 					warn(gettext(
928 					    "invalid processor range %s\n"),
929 					    *argv);
930 					errors = ERR_USAGE;
931 					continue;
932 				}
933 				last = (processorid_t)
934 				    strtol(errptr, &errptr, 10);
935 				if ((errptr != NULL && *errptr != '\0') ||
936 				    last < first || first < 0) {
937 					warn(gettext(
938 					    "invalid processor range %s\n"),
939 					    *argv);
940 					errors = ERR_USAGE;
941 					continue;
942 				}
943 				if (do_range(pset, first, last, pflag))
944 					errors = ERR_FAIL;
945 			}
946 		}
947 	} else if (bflag || uflag || qflag) {
948 		/*
949 		 * Perform function for each pid/lwpid specified.
950 		 */
951 		if (argc == 0) {
952 			warn(gettext("must specify at least one pid\n"));
953 			return (usage());
954 		}
955 
956 		/*
957 		 * Go through listed processes/lwp_ranges.
958 		 */
959 		for (; argc > 0; argv++, argc--) {
960 			pid = (id_t)strtol(*argv, &errptr, 10);
961 			if (errno != 0 ||
962 			    (errptr != NULL && *errptr != '\0' &&
963 				*errptr != '/')) {
964 				warn(gettext("invalid process ID: %s\n"),
965 				    *argv);
966 				continue;
967 			}
968 			if (errptr != NULL && *errptr == '/') {
969 				int ret;
970 				/*
971 				 * Handle lwp range case
972 				 */
973 				const char *lwps = (const char *)(++errptr);
974 				if (*lwps == '\0' ||
975 				    proc_lwp_range_valid(lwps) != 0) {
976 					warn(gettext("invalid lwp range "
977 					    "for pid %d\n"), (int)pid);
978 					errors = ERR_FAIL;
979 					continue;
980 				}
981 				if (!qflag)
982 					(void) proc_initstdio();
983 				ret = do_lwps(pid, lwps, pset);
984 				if (!qflag)
985 					(void) proc_finistdio();
986 				if (ret != ERR_OK)
987 					errors = ret;
988 			} else {
989 				/*
990 				 * Handle whole process case.
991 				 */
992 				if (pset_bind(pset, P_PID, pid,
993 				    &old_pset) < 0) {
994 					bind_err(pset, pid, -1, errno);
995 					errors = ERR_FAIL;
996 					continue;
997 				}
998 				if (qflag)
999 					query_out(pid, -1, old_pset);
1000 				else
1001 					bind_out(pid, -1, old_pset, pset);
1002 			}
1003 		}
1004 	}
1005 
1006 	if (Qflag || Uflag) {
1007 		/*
1008 		 * Go through listed processor set IDs.
1009 		 */
1010 		for (; argc > 0; argv++, argc--) {
1011 			errno = 0;
1012 			pset = (id_t)strtol(*argv, &errptr, 10);
1013 			if (errno != 0 ||
1014 			    (errptr != NULL && *errptr != '\0')) {
1015 				warn(gettext("invalid processor set ID\n"));
1016 				continue;
1017 			}
1018 			if (Qflag) {
1019 				(void) proc_walk(query_all_lwp,
1020 				    &pset, PR_WALK_LWP);
1021 				continue;
1022 			}
1023 			if (Uflag) {
1024 				if (pset_bind(PS_NONE, P_PSETID, pset,
1025 				    &old_pset) != 0) {
1026 					warn(gettext("failed to unbind from "
1027 					    "processor set %d"), (int)pset);
1028 					errors = ERR_FAIL;
1029 				}
1030 				continue;
1031 			}
1032 		}
1033 	}
1034 
1035 	return (errors);
1036 }
1037