xref: /illumos-gate/usr/src/cmd/priocntl/priocntl.c (revision d1419d5a02eaedd520f925cd465fd62f3950ae43)
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 2004 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 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
30 /*	  All Rights Reserved  	*/
31 
32 
33 #include	<stdio.h>
34 #include	<stdlib.h>
35 #include	<string.h>
36 #include	<wait.h>
37 #include	<search.h>
38 #include	<unistd.h>
39 #include	<sys/types.h>
40 #include	<dirent.h>
41 #include	<fcntl.h>
42 #include	<sys/param.h>
43 #include	<sys/procset.h>
44 #include	<sys/priocntl.h>
45 #include	<procfs.h>
46 #include	<macros.h>
47 #include	<libgen.h>
48 #include	<limits.h>
49 #include	<errno.h>
50 
51 #include	"priocntl.h"
52 
53 /*
54  * This file contains the code implementing the class independent part
55  * of the priocntl command.  Most of the useful work for the priocntl
56  * command is done by the class specific sub-commands, the code for
57  * which is elsewhere.  The class independent part of the command is
58  * responsible for executing the appropriate class specific sub-commands
59  * and providing any necessary input to the sub-commands.
60  * Code in this file should never assume any knowledge of any specific
61  * scheduler class (other than the SYS class).
62  */
63 
64 #define	CLASSPATH	"/usr/lib/class"
65 
66 typedef struct classpids {
67 	char	clp_clname[PC_CLNMSZ];
68 	pid_t	*clp_pidlist;
69 	int	clp_pidlistsz;
70 	int	clp_npids;
71 } classpids_t;
72 
73 static char usage[] =
74 "usage:	priocntl -l\n\
75 	priocntl -d [-i idtype] [idlist]\n\
76 	priocntl -s [-c class] [c.s.o.] [-i idtype] [idlist]\n\
77 	priocntl -e [-c class] [c.s.o.] command [argument(s)]\n";
78 
79 static char	basenm[BASENMSZ];
80 static char	cmdpath[MAXPATHLEN];
81 
82 static char	*procdir = "/proc";
83 
84 static int	print_classlist(void);
85 static void	set_procs(char *, idtype_t, int, char **, char **);
86 static void	exec_cmd(char *, char **);
87 static int	print_procs(idtype_t, int, char *[]);
88 static void	ids2pids(idtype_t, id_t *, int, classpids_t *, int);
89 static void	add_pid_tolist(classpids_t *, int, char *, pid_t);
90 static void	increase_pidlist(classpids_t *);
91 static boolean_t	idmatch(char *, char *, int, char **);
92 
93 /*
94  * These variables are defined to be used in prio_getopt() below.
95  */
96 static	int	prio_getopt();
97 /* LINTED static unused */
98 static	int	prio_optopt = 0;
99 static	char	*prio_optarg = 0;
100 static	int	prio_optind = 1;
101 static	int	prio_sp = 1;
102 
103 int
104 main(int argc, char *argv[])
105 {
106 	int		c;
107 	int		lflag, dflag, sflag, eflag, cflag, iflag, csoptsflag;
108 	char		*clname;
109 	char		*idtypnm;
110 	idtype_t	idtype;
111 	int		idargc;
112 	char		**idargv;
113 
114 	(void) strlcpy(cmdpath, argv[0], MAXPATHLEN);
115 	(void) strlcpy(basenm, basename(argv[0]), BASENMSZ);
116 	lflag = dflag = sflag = eflag = cflag = iflag = csoptsflag = 0;
117 	while ((c = prio_getopt(argc, argv, "ldsec:i:")) != -1) {
118 
119 		switch (c) {
120 
121 		case 'l':
122 			lflag++;
123 			break;
124 
125 		case 'd':
126 			dflag++;
127 			break;
128 
129 		case 's':
130 			sflag++;
131 			break;
132 
133 		case 'e':
134 			eflag++;
135 			break;
136 
137 		case 'c':
138 			cflag++;
139 			clname = prio_optarg;
140 			break;
141 
142 		case 'i':
143 			iflag++;
144 			idtypnm = prio_optarg;
145 			break;
146 
147 		case '?':
148 			if (strcmp(argv[prio_optind - 1], "-c") == 0 ||
149 			    strcmp(argv[prio_optind - 1], "-i") == 0) {
150 
151 				/*
152 				 * getopt() will return ? if either
153 				 * of these appear without an argument.
154 				 */
155 				fatalerr(usage);
156 			}
157 
158 			/*
159 			 * We assume for now that any option that
160 			 * getopt() doesn't recognize (with the
161 			 * exception of c and i) is intended for a
162 			 * class specific subcommand.  For now we also
163 			 * require that all class specific options
164 			 * take an argument (until we can get smarter
165 			 * about parsing our options).
166 			 */
167 			csoptsflag++;
168 			prio_optind++;
169 			prio_sp = 1;
170 			break;
171 
172 		default:
173 			break;
174 		}
175 	}
176 
177 	if (lflag) {
178 		if (dflag || sflag || eflag || cflag || iflag || csoptsflag)
179 			fatalerr(usage);
180 
181 		return (print_classlist());
182 
183 	} else if (dflag) {
184 		if (lflag || sflag || eflag || cflag || csoptsflag)
185 			fatalerr(usage);
186 		if (iflag) {
187 			if (str2idtyp(idtypnm, &idtype) == -1)
188 				fatalerr("%s: bad idtype %s\n", cmdpath,
189 				    idtypnm);
190 		} else {
191 			idtype = P_PID;
192 		}
193 
194 		if (prio_optind < argc) {
195 			idargc = argc - prio_optind;
196 			idargv = &argv[prio_optind];
197 		} else {
198 			idargc = 0;
199 		}
200 
201 		return (print_procs(idtype, idargc, idargv));
202 
203 	} else if (sflag) {
204 		if (lflag || dflag || eflag)
205 			fatalerr(usage);
206 		if (iflag) {
207 			if (str2idtyp(idtypnm, &idtype) == -1)
208 				fatalerr("%s: bad idtype %s\n", cmdpath,
209 				    idtypnm);
210 		} else {
211 			idtype = P_PID;
212 		}
213 
214 		if (cflag == 0)
215 			clname = NULL;
216 
217 		if (prio_optind < argc) {
218 			idargc = argc - prio_optind;
219 			idargv = &argv[prio_optind];
220 		} else {
221 			idargc = 0;
222 		}
223 
224 		set_procs(clname, idtype, idargc, idargv, argv);
225 
226 	} else if (eflag) {
227 		if (lflag || dflag || sflag || iflag)
228 			fatalerr(usage);
229 
230 		if (cflag == 0)
231 			clname = NULL;
232 
233 		if (prio_optind >= argc)
234 			fatalerr(usage);
235 
236 		exec_cmd(clname, argv);
237 
238 	} else {
239 		fatalerr(usage);
240 	}
241 
242 	return (0);
243 }
244 
245 
246 /*
247  * Print the heading for the class list and execute the class
248  * specific sub-command with the -l option for each configured class.
249  */
250 static int
251 print_classlist(void)
252 {
253 	id_t		cid;
254 	int		nclass;
255 	pcinfo_t	pcinfo;
256 	static char	subcmdpath[128];
257 	int		status;
258 	pid_t		pid;
259 	int		error = 0;
260 
261 	/*
262 	 * No special privileges required for this operation.
263 	 * Set the effective UID back to the real UID.
264 	 */
265 	if (setuid(getuid()) == -1)
266 		fatalerr("%s: Can't set effective UID back to real UID\n",
267 		    cmdpath);
268 
269 	if ((nclass = priocntl(0, 0, PC_GETCLINFO, NULL)) == -1)
270 		fatalerr("%s: Can't get number of configured classes, priocntl"
271 		    " system call failed with errno %d\n", cmdpath, errno);
272 
273 	(void) printf("CONFIGURED CLASSES\n==================\n\n");
274 	(void) printf("SYS (System Class)\n");
275 	for (cid = 1; cid < nclass; cid++) {
276 		(void) printf("\n");
277 		(void) fflush(stdout);
278 		pcinfo.pc_cid = cid;
279 		if (priocntl(0, 0, PC_GETCLINFO, (caddr_t)&pcinfo) == -1)
280 			fatalerr("%s: can't get class name (class ID = %ld)\n",
281 			    cmdpath, cid);
282 		if (snprintf(subcmdpath, sizeof (subcmdpath), "%s/%s/%s%s",
283 		    CLASSPATH, pcinfo.pc_clname, pcinfo.pc_clname, basenm) >=
284 		    sizeof (subcmdpath))
285 			fatalerr("%s: can't generate %s specific subcommand\n",
286 			    cmdpath, pcinfo.pc_clname);
287 		if ((pid = fork()) == 0) {
288 			(void) execl(subcmdpath, subcmdpath, "-l", (char *)0);
289 			(void) printf("%s\n", pcinfo.pc_clname);
290 			fatalerr("\tCan't execute %s specific subcommand\n",
291 			    pcinfo.pc_clname);
292 		} else if (pid == (pid_t)-1) {
293 			(void) printf("%s\n", pcinfo.pc_clname);
294 			(void) fprintf(stderr,
295 			    "Can't execute %s specific subcommand)\n",
296 			    pcinfo.pc_clname);
297 			error = 1;
298 		} else {
299 			(void) wait(&status);
300 			if (status)
301 				error = 1;
302 		}
303 	}
304 
305 	return (error);
306 }
307 
308 
309 /*
310  * For each class represented within the set of processes specified by
311  * idtype/idargv, print_procs() executes the class specific sub-command
312  * with the -d option.  We pipe to each sub-command a list of pids in
313  * the set belonging to that class.
314  */
315 static int
316 print_procs(idtype_t idtype, int idargc, char *idargv[])
317 {
318 	int		i;
319 	id_t		id;
320 	id_t		idlist[NIDS];
321 	int		nids;
322 	classpids_t	*clpids;
323 	int		nclass;
324 	id_t		cid;
325 	pcinfo_t	pcinfo;
326 	int		pidexists;
327 	FILE		*pipe_to_subcmd;
328 	char		subcmd[128];
329 	int		error = 0;
330 
331 
332 	/*
333 	 * Build a list of ids eliminating any duplicates in idargv.
334 	 */
335 	if (idtype == P_ALL) {
336 		/*
337 		 * No idlist should be specified. If one is specified,
338 		 * it is ignored.
339 		 */
340 		nids = 0;
341 	} else if (idargc == 0) {
342 
343 		/*
344 		 * No ids supplied by user; use current id.
345 		 */
346 		if (getmyid(idtype, &idlist[0]) == -1)
347 			fatalerr("%s: Can't get ID for current process,"
348 			    " idtype = %d\n", cmdpath, idtype);
349 		nids = 1;
350 	} else {
351 		nids = 0;
352 		for (i = 0; i < idargc && nids < NIDS; i++) {
353 			if (idtype == P_CID) {
354 				if ((id = clname2cid(idargv[i])) == -1) {
355 					(void) fprintf(stderr, "%s: Invalid or"
356 					    " unconfigured class %s in idlist"
357 					    " - ignored\n", cmdpath, idargv[i]);
358 					error = 1;
359 				}
360 			} else {
361 				id = (id_t)str2num(idargv[i], INT_MIN, INT_MAX);
362 				if (errno) {
363 					(void) fprintf(stderr,
364 					    "%s: Invalid id \"%s\"\n",
365 					    cmdpath, idargv[i]);
366 					error = 1;
367 					id = BADPID;
368 				}
369 			}
370 
371 			/*
372 			 * lsearch(3C) adds ids to the idlist,
373 			 * eliminating duplicates.
374 			 */
375 			(void) lsearch((void *)&id, (void *)idlist,
376 			    (size_t *)&nids, sizeof (id), (int (*)())idcompar);
377 		}
378 	}
379 
380 	if ((nclass = priocntl(0, 0, PC_GETCLINFO, NULL)) == -1)
381 		fatalerr("%s: Can't get number of configured classes, priocntl"
382 		    " system call failed with errno %d\n", cmdpath, errno);
383 
384 	if ((clpids = (classpids_t *)malloc(sizeof (classpids_t) * nclass)) ==
385 	    NULL)
386 		fatalerr("%s: Can't allocate memory for clpids.\n", cmdpath);
387 
388 	for (cid = 1; cid < nclass; cid++) {
389 		pcinfo.pc_cid = cid;
390 		if (priocntl(0, 0, PC_GETCLINFO, (caddr_t)&pcinfo) == -1)
391 			fatalerr("%s: Can't get class name, cid = %ld\n",
392 			    cmdpath, cid);
393 
394 		(void) strncpy(clpids[cid].clp_clname, pcinfo.pc_clname,
395 		    PC_CLNMSZ);
396 
397 		/*
398 		 * The memory allocation for the pidlist uses realloc().
399 		 * A realloc() call is required, when "clp_npids" is
400 		 * equal to "clp_pidlistsz".
401 		 */
402 		clpids[cid].clp_pidlist = (pid_t *)NULL;
403 		clpids[cid].clp_pidlistsz = 0;
404 		clpids[cid].clp_npids = 0;
405 	}
406 
407 	/*
408 	 * Build the pidlist.
409 	 */
410 	ids2pids(idtype, idlist, nids, clpids, nclass);
411 
412 	/*
413 	 * No need for special privileges any more.
414 	 * Set the effective UID back to the real UID.
415 	 */
416 	if (setuid(getuid()) == -1)
417 		fatalerr("%s: Can't set effective UID back to real UID\n",
418 		    cmdpath);
419 
420 	pidexists = 0;
421 	for (cid = 1; cid < nclass; cid++) {
422 		if (clpids[cid].clp_npids == 0)
423 			continue;
424 
425 		pidexists = 1;
426 		if (snprintf(subcmd, sizeof (subcmd), "%s/%s/%s%s -d",
427 		    CLASSPATH, clpids[cid].clp_clname, clpids[cid].clp_clname,
428 		    basenm) >= sizeof (subcmd)) {
429 			(void) fprintf(stderr,
430 			    "Can't generate %s specific subcommand\n",
431 			    clpids[cid].clp_clname);
432 			error = 1;
433 			free(clpids[cid].clp_pidlist);
434 			continue;
435 		}
436 		if ((pipe_to_subcmd = popen(subcmd, "w")) == NULL) {
437 			(void) printf("%s\n", clpids[cid].clp_clname);
438 			(void) fprintf(stderr,
439 			    "Can't execute %s specific subcommand\n",
440 			    clpids[cid].clp_clname);
441 			error = 1;
442 			free(clpids[cid].clp_pidlist);
443 			continue;
444 		}
445 		(void) fwrite(clpids[cid].clp_pidlist, sizeof (pid_t),
446 		    clpids[cid].clp_npids, pipe_to_subcmd);
447 		if (pclose(pipe_to_subcmd))
448 			error = 1;
449 
450 		free(clpids[cid].clp_pidlist);
451 	}
452 
453 	free(clpids);
454 
455 	if (pidexists == 0)
456 		fatalerr("%s: Process(es) not found.\n", cmdpath);
457 
458 	return (error);
459 }
460 
461 
462 /*
463  * Execute the appropriate class specific sub-command with the arguments
464  * pointed to by subcmdargv.  If the user specified a class we simply
465  * exec the sub-command for that class.  If no class was specified we
466  * verify that the processes in the set specified by idtype/idargv are
467  * all in the same class and then execute the sub-command for that class.
468  */
469 static void
470 set_procs(clname, idtype, idargc, idargv, subcmdargv)
471 char		*clname;
472 idtype_t	idtype;
473 int		idargc;
474 char		**idargv;
475 char		**subcmdargv;
476 {
477 	char			idstr[PC_IDTYPNMSZ];
478 	char			myidstr[PC_IDTYPNMSZ];
479 	char			clnmbuf[PC_CLNMSZ];
480 	pcinfo_t		pcinfo;
481 	static psinfo_t		prinfo;
482 	static prcred_t		prcred;
483 	DIR			*dirp;
484 	struct dirent		*dentp;
485 	static char		pname[100];
486 	char			*fname;
487 	int			procfd;
488 	int			saverr;
489 	static char		subcmdpath[128];
490 	boolean_t		procinset;
491 	id_t			id;
492 	size_t			len;
493 
494 	if (clname == NULL && idtype == P_PID && idargc <= 1) {
495 
496 		/*
497 		 * No class specified by user but only one process
498 		 * in specified set.  Get the class the easy way.
499 		 */
500 		if (idargc == 0) {
501 			if (priocntl(P_PID, P_MYID, PC_GETXPARMS, NULL,
502 			    PC_KY_CLNAME, clnmbuf, 0) == -1)
503 				if (errno == ESRCH)
504 					fatalerr("%s: Process not found.\n",
505 					    cmdpath);
506 				else
507 					fatalerr("%s: Can't get class of"
508 					    " current process\npriocntl"
509 					    " system call failed with"
510 					    " errno %d\n", cmdpath, errno);
511 		} else {
512 			/* idargc == 1 */
513 			id = (id_t)str2num(idargv[0], INT_MIN, INT_MAX);
514 			if (errno)
515 				fatalerr("%s: Invalid id \"%s\"\n", cmdpath,
516 				    idargv[0]);
517 
518 			if (priocntl(P_PID, id, PC_GETXPARMS,
519 			    NULL, PC_KY_CLNAME, clnmbuf, 0) == -1)
520 				if (errno == ESRCH)
521 					fatalerr("%s: Process not found.\n",
522 					    cmdpath);
523 				else
524 					fatalerr("%s: Can't get class of "
525 					    " specified  process\npriocntl"
526 					    " system call failed with"
527 					    " errno %d\n", cmdpath, errno);
528 		}
529 
530 		clname = clnmbuf;
531 	} else if (clname == NULL) {
532 
533 		/*
534 		 * No class specified by user and potentially more
535 		 * than one process in specified set.  Verify that
536 		 * all procs in set are in the same class.
537 		 */
538 		if (idargc == 0 && idtype != P_ALL) {
539 
540 			/*
541 			 * No ids supplied by user; use current id.
542 			 */
543 			if (getmyidstr(idtype, myidstr) == -1)
544 				fatalerr("%s: Can't get ID string for current"
545 				    " process, idtype = %d\n", cmdpath, idtype);
546 		}
547 		if ((dirp = opendir(procdir)) == NULL)
548 			fatalerr("%s: Can't open PROC directory %s\n",
549 			    cmdpath, procdir);
550 
551 		while ((dentp = readdir(dirp)) != NULL) {
552 			if (dentp->d_name[0] == '.')	/* skip . and .. */
553 				continue;
554 
555 			len = snprintf(pname, sizeof (pname), "%s/%s/",
556 			    procdir, dentp->d_name);
557 			/* Really max(sizeof ("psinfo"), sizeof ("cred")) */
558 			if (len + sizeof ("psinfo") > sizeof (pname)) {
559 				(void) fprintf(stderr,
560 				    "%s: skipping %s, name too long.\n",
561 				    cmdpath, dentp->d_name);
562 				continue;
563 			}
564 			fname = pname + len;
565 retry:
566 			(void) strcpy(fname, "psinfo");
567 			if ((procfd = open(pname, O_RDONLY)) < 0)
568 				continue;
569 
570 			if (read(procfd, &prinfo, sizeof (prinfo)) !=
571 			    sizeof (prinfo)) {
572 				saverr = errno;
573 				(void) close(procfd);
574 				if (saverr == EAGAIN)
575 					goto retry;
576 				if (saverr != ENOENT) {
577 					(void) fprintf(stderr,
578 					    "%s: Can't get process info for"
579 					    " %s\n", cmdpath, pname);
580 				}
581 				continue;
582 			}
583 			(void) close(procfd);
584 
585 			if (idtype == P_UID || idtype == P_GID) {
586 				(void) strcpy(fname, "cred");
587 				if ((procfd = open(pname, O_RDONLY)) < 0 ||
588 				    read(procfd, &prcred, sizeof (prcred)) !=
589 				    sizeof (prcred)) {
590 					saverr = errno;
591 					if (procfd >= 0)
592 						(void) close(procfd);
593 					if (saverr == EAGAIN)
594 						goto retry;
595 					if (saverr != ENOENT) {
596 						(void) fprintf(stderr,
597 						    "%s: Can't get process"
598 						    " credentials for %s\n",
599 						    cmdpath, pname);
600 					}
601 					continue;
602 				}
603 				(void) close(procfd);
604 			}
605 
606 			if (prinfo.pr_lwp.pr_state == 0 || prinfo.pr_nlwp == 0)
607 				continue;
608 
609 
610 			switch (idtype) {
611 
612 			case P_PID:
613 				itoa((long)prinfo.pr_pid, idstr);
614 				procinset = idmatch(idstr, myidstr,
615 				    idargc, idargv);
616 				break;
617 
618 			case P_PPID:
619 				itoa((long)prinfo.pr_ppid, idstr);
620 				procinset = idmatch(idstr, myidstr,
621 				    idargc, idargv);
622 				break;
623 
624 			case P_PGID:
625 				itoa((long)prinfo.pr_pgid, idstr);
626 				procinset = idmatch(idstr, myidstr,
627 				    idargc, idargv);
628 				break;
629 
630 			case P_SID:
631 				itoa((long)prinfo.pr_sid, idstr);
632 				procinset = idmatch(idstr, myidstr,
633 				    idargc, idargv);
634 				break;
635 
636 			case P_CID:
637 				procinset = idmatch(prinfo.pr_lwp.pr_clname,
638 				    myidstr, idargc, idargv);
639 				break;
640 
641 			case P_UID:
642 				itoa((long)prcred.pr_euid, idstr);
643 				procinset = idmatch(idstr, myidstr,
644 				    idargc, idargv);
645 				break;
646 
647 			case P_GID:
648 				itoa((long)prcred.pr_egid, idstr);
649 				procinset = idmatch(idstr, myidstr,
650 				    idargc, idargv);
651 				break;
652 
653 			case P_PROJID:
654 				itoa((long)prinfo.pr_projid, idstr);
655 				procinset = idmatch(idstr, myidstr,
656 				    idargc, idargv);
657 				break;
658 
659 			case P_TASKID:
660 				itoa((long)prinfo.pr_taskid, idstr);
661 				procinset = idmatch(idstr, myidstr,
662 				    idargc, idargv);
663 				break;
664 
665 			case P_ZONEID:
666 				itoa((long)prinfo.pr_zoneid, idstr);
667 				procinset = idmatch(idstr, myidstr,
668 				    idargc, idargv);
669 				break;
670 
671 			case P_CTID:
672 				itoa((long)prinfo.pr_contract, idstr);
673 				procinset = idmatch(idstr, myidstr,
674 				    idargc, idargv);
675 				break;
676 
677 			case P_ALL:
678 				procinset = B_TRUE;
679 				break;
680 
681 			default:
682 				fatalerr("%s: Bad idtype %d in set_procs()\n",
683 				    cmdpath, idtype);
684 			}
685 			if (procinset == B_TRUE) {
686 				if (clname == NULL) {
687 
688 					/*
689 					 * First proc found in set.
690 					 */
691 					(void) strcpy(clnmbuf,
692 					    prinfo.pr_lwp.pr_clname);
693 					clname = clnmbuf;
694 				} else if (strcmp(clname,
695 				    prinfo.pr_lwp.pr_clname) != 0) {
696 					fatalerr("%s: Specified processes"
697 					    " from different classes.\n",
698 					    cmdpath);
699 				}
700 			}
701 		}
702 		(void) closedir(dirp);
703 		if (clname == NULL)
704 			fatalerr("%s: Process(es) not found.\n", cmdpath);
705 	} else {
706 
707 		/*
708 		 * User specified class. Check it for validity.
709 		 */
710 		(void) strcpy(pcinfo.pc_clname, clname);
711 		if (priocntl(0, 0, PC_GETCID, (caddr_t)&pcinfo) == -1)
712 			fatalerr("%s: Invalid or unconfigured class %s\n",
713 			    cmdpath, clname);
714 	}
715 
716 	/*
717 	 * No need for special privileges any more.
718 	 * Set the effective UID back to the real UID.
719 	 */
720 	if (setuid(getuid()) == -1)
721 		fatalerr("%s: Can't set effective UID back to real UID\n",
722 		    cmdpath);
723 
724 	if (snprintf(subcmdpath, sizeof (subcmdpath), "%s/%s/%s%s",
725 	    CLASSPATH, clname, clname, basenm) >= sizeof (subcmdpath))
726 		fatalerr("%s: can't generate %s specific subcommand\n",
727 		    cmdpath, clname);
728 
729 	subcmdargv[0] = subcmdpath;
730 	(void) execv(subcmdpath, subcmdargv);
731 	fatalerr("%s: Can't execute %s sub-command\n", cmdpath, clname);
732 }
733 
734 
735 /*
736  * Execute the appropriate class specific sub-command with the arguments
737  * pointed to by subcmdargv.  If the user specified a class we simply
738  * exec the sub-command for that class.  If no class was specified we
739  * execute the sub-command for our own current class.
740  */
741 static void
742 exec_cmd(clname, subcmdargv)
743 char	*clname;
744 char	**subcmdargv;
745 {
746 	pcinfo_t	pcinfo;
747 	char		clnmbuf[PC_CLNMSZ];
748 	char		subcmdpath[128];
749 
750 	/*
751 	 * No special privileges required for this operation.
752 	 * Set the effective UID back to the real UID.
753 	 */
754 	if (setuid(getuid()) == -1)
755 		fatalerr("%s: Can't set effective UID back to real UID\n",
756 		    cmdpath);
757 
758 	if (clname == NULL) {
759 		if (priocntl(P_PID, P_MYID, PC_GETXPARMS, NULL,
760 		    PC_KY_CLNAME, clnmbuf, 0) == -1)
761 			fatalerr("%s: Can't get class name of current process\n"
762 			    "priocntl system call failed with errno %d\n",
763 			    cmdpath, errno);
764 
765 		clname = clnmbuf;
766 	} else {
767 
768 		/*
769 		 * User specified class. Check it for validity.
770 		 */
771 		(void) strcpy(pcinfo.pc_clname, clname);
772 		if (priocntl(0, 0, PC_GETCID, (caddr_t)&pcinfo) == -1)
773 			fatalerr("%s: Invalid or unconfigured class %s\n",
774 			    cmdpath, clname);
775 	}
776 
777 	if (snprintf(subcmdpath, sizeof (subcmdpath), "%s/%s/%s%s",
778 	    CLASSPATH, clname, clname, basenm) >= sizeof (subcmdpath))
779 		fatalerr("%s: can't generate %s specific subcommand\n",
780 		    cmdpath, clname);
781 	subcmdargv[0] = subcmdpath;
782 	(void) execv(subcmdpath, subcmdargv);
783 	fatalerr("%s: Can't execute %s sub-command\n", cmdpath, clname);
784 }
785 
786 
787 /*
788  * Fill in the classpids structures in the array pointed to by clpids
789  * with pids for the processes in the set specified by idtype/idlist.
790  * We read the /proc/<pid>/psinfo file to get the necessary process
791  * information.
792  */
793 static void
794 ids2pids(idtype, idlist, nids, clpids, nclass)
795 idtype_t	idtype;
796 id_t		*idlist;
797 int		nids;
798 classpids_t	*clpids;
799 int		nclass;
800 {
801 	static psinfo_t		prinfo;
802 	static prcred_t		prcred;
803 	DIR			*dirp;
804 	struct dirent		*dentp;
805 	char			pname[100];
806 	char			*fname;
807 	int			procfd;
808 	int			saverr;
809 	int			i;
810 	char			*clname;
811 	size_t			len;
812 
813 	if ((dirp = opendir(procdir)) == NULL)
814 		fatalerr("%s: Can't open PROC directory %s\n",
815 		    cmdpath, procdir);
816 
817 	while ((dentp = readdir(dirp)) != NULL) {
818 		if (dentp->d_name[0] == '.')	/* skip . and .. */
819 			continue;
820 
821 		len = snprintf(pname, sizeof (pname), "%s/%s/",
822 		    procdir, dentp->d_name);
823 		/* Really max(sizeof ("psinfo"), sizeof ("cred")) */
824 		if (len + sizeof ("psinfo") > sizeof (pname)) {
825 			(void) fprintf(stderr,
826 			    "%s: skipping %s, name too long.\n",
827 			    cmdpath, dentp->d_name);
828 			continue;
829 		}
830 		fname = pname + len;
831 retry:
832 		(void) strcpy(fname, "psinfo");
833 		if ((procfd = open(pname, O_RDONLY)) < 0)
834 			continue;
835 		if (read(procfd, &prinfo, sizeof (prinfo)) != sizeof (prinfo)) {
836 			saverr = errno;
837 			(void) close(procfd);
838 			if (saverr == EAGAIN)
839 				goto retry;
840 			if (saverr != ENOENT) {
841 				(void) fprintf(stderr,
842 				    "%s: Can't get process info for %s\n",
843 				    cmdpath, pname);
844 			}
845 			continue;
846 		}
847 		(void) close(procfd);
848 
849 		if (idtype == P_UID || idtype == P_GID) {
850 			(void) strcpy(fname, "cred");
851 			if ((procfd = open(pname, O_RDONLY)) < 0 ||
852 			    read(procfd, &prcred, sizeof (prcred)) !=
853 			    sizeof (prcred)) {
854 				saverr = errno;
855 				(void) close(procfd);
856 				if (saverr == EAGAIN)
857 					goto retry;
858 				if (saverr != ENOENT) {
859 					(void) fprintf(stderr,
860 					    "%s: Can't get process credentials"
861 					    " for %s\n",
862 					    cmdpath, pname);
863 				}
864 				continue;
865 			}
866 			(void) close(procfd);
867 		}
868 
869 		if (prinfo.pr_lwp.pr_state == 0 || prinfo.pr_nlwp == 0)
870 			continue;
871 
872 		switch (idtype) {
873 
874 		case P_PID:
875 			for (i = 0; i < nids; i++) {
876 				if (idlist[i] == (id_t)prinfo.pr_pid)
877 					add_pid_tolist(clpids, nclass,
878 					    prinfo.pr_lwp.pr_clname,
879 					    prinfo.pr_pid);
880 			}
881 			break;
882 
883 		case P_PPID:
884 			for (i = 0; i < nids; i++) {
885 				if (idlist[i] == (id_t)prinfo.pr_ppid)
886 					add_pid_tolist(clpids, nclass,
887 					    prinfo.pr_lwp.pr_clname,
888 					    prinfo.pr_pid);
889 			}
890 			break;
891 
892 		case P_PGID:
893 			for (i = 0; i < nids; i++) {
894 				if (idlist[i] == (id_t)prinfo.pr_pgid)
895 					add_pid_tolist(clpids, nclass,
896 					    prinfo.pr_lwp.pr_clname,
897 					    prinfo.pr_pid);
898 			}
899 			break;
900 
901 		case P_SID:
902 			for (i = 0; i < nids; i++) {
903 				if (idlist[i] == (id_t)prinfo.pr_sid)
904 					add_pid_tolist(clpids, nclass,
905 					    prinfo.pr_lwp.pr_clname,
906 					    prinfo.pr_pid);
907 			}
908 			break;
909 
910 		case P_CID:
911 			for (i = 0; i < nids; i++) {
912 				clname = clpids[idlist[i]].clp_clname;
913 				if (strcmp(clname,
914 				    prinfo.pr_lwp.pr_clname) == 0)
915 					add_pid_tolist(clpids, nclass,
916 					    prinfo.pr_lwp.pr_clname,
917 					    prinfo.pr_pid);
918 			}
919 			break;
920 
921 		case P_UID:
922 			for (i = 0; i < nids; i++) {
923 				if (idlist[i] == (id_t)prcred.pr_euid)
924 					add_pid_tolist(clpids, nclass,
925 					    prinfo.pr_lwp.pr_clname,
926 					    prinfo.pr_pid);
927 			}
928 			break;
929 
930 		case P_GID:
931 			for (i = 0; i < nids; i++) {
932 				if (idlist[i] == (id_t)prcred.pr_egid)
933 					add_pid_tolist(clpids, nclass,
934 					    prinfo.pr_lwp.pr_clname,
935 					    prinfo.pr_pid);
936 			}
937 			break;
938 
939 		case P_PROJID:
940 			for (i = 0; i < nids; i++) {
941 				if (idlist[i] == (id_t)prinfo.pr_projid)
942 					add_pid_tolist(clpids, nclass,
943 					    prinfo.pr_lwp.pr_clname,
944 					    prinfo.pr_pid);
945 			}
946 			break;
947 
948 		case P_TASKID:
949 			for (i = 0; i < nids; i++) {
950 				if (idlist[i] == (id_t)prinfo.pr_taskid)
951 					add_pid_tolist(clpids, nclass,
952 					    prinfo.pr_lwp.pr_clname,
953 					    prinfo.pr_pid);
954 			}
955 		break;
956 
957 		case P_ZONEID:
958 			for (i = 0; i < nids; i++) {
959 				if (idlist[i] == (id_t)prinfo.pr_zoneid)
960 					add_pid_tolist(clpids, nclass,
961 					    prinfo.pr_lwp.pr_clname,
962 					    prinfo.pr_pid);
963 			}
964 			break;
965 
966 		case P_CTID:
967 			for (i = 0; i < nids; i++) {
968 				if (idlist[i] == (id_t)prinfo.pr_contract)
969 					add_pid_tolist(clpids, nclass,
970 					    prinfo.pr_lwp.pr_clname,
971 					    prinfo.pr_pid);
972 			}
973 			break;
974 
975 		case P_ALL:
976 			add_pid_tolist(clpids, nclass, prinfo.pr_lwp.pr_clname,
977 			    prinfo.pr_pid);
978 			break;
979 
980 		default:
981 			fatalerr("%s: Bad idtype %d in ids2pids()\n",
982 			    cmdpath, idtype);
983 		}
984 	}
985 	(void) closedir(dirp);
986 }
987 
988 
989 /*
990  * Search the array pointed to by clpids for the classpids
991  * structure corresponding to clname and add pid to its
992  * pidlist.
993  */
994 static void
995 add_pid_tolist(clpids, nclass, clname, pid)
996 classpids_t	*clpids;
997 int		nclass;
998 char		*clname;
999 pid_t		pid;
1000 {
1001 	classpids_t	*clp;
1002 
1003 	for (clp = clpids; clp != &clpids[nclass]; clp++) {
1004 		if (strcmp(clp->clp_clname, clname) == 0) {
1005 			if (clp->clp_npids == clp->clp_pidlistsz)
1006 				increase_pidlist(clp);
1007 
1008 			(clp->clp_pidlist)[clp->clp_npids] = pid;
1009 			clp->clp_npids++;
1010 			return;
1011 		}
1012 	}
1013 }
1014 
1015 
1016 static void
1017 increase_pidlist(classpids_t *clp)
1018 {
1019 	if ((clp->clp_pidlist = realloc(clp->clp_pidlist,
1020 	    (clp->clp_pidlistsz + NPIDS) * sizeof (pid_t))) == NULL)
1021 		/*
1022 		 * The pidlist is filled up and we cannot increase the size.
1023 		 */
1024 		fatalerr("%s: Can't allocate memory for pidlist.\n", cmdpath);
1025 
1026 	clp->clp_pidlistsz += NPIDS;
1027 }
1028 
1029 
1030 /*
1031  * Compare id strings for equality.  If idargv contains ids
1032  * (idargc > 0) compare idstr to each id in idargv, otherwise
1033  * just compare to curidstr.
1034  */
1035 static boolean_t
1036 idmatch(idstr, curidstr, idargc, idargv)
1037 char	*idstr;
1038 char	*curidstr;
1039 int	idargc;
1040 char	**idargv;
1041 {
1042 	int	i;
1043 
1044 	if (idargc == 0) {
1045 		if (strcmp(curidstr, idstr) == 0)
1046 			return (B_TRUE);
1047 	} else {
1048 		for (i = 0; i < idargc; i++) {
1049 			if (strcmp(idargv[i], idstr) == 0)
1050 				return (B_TRUE);
1051 		}
1052 	}
1053 	return (B_FALSE);
1054 }
1055 
1056 /*
1057  * This is a copy of the getopt() function found in libc:getopt.c. A separate
1058  * copy is required to fix the bug id #1114636. To fix the problem we need to
1059  * reset the _sp to 1. Since _sp in libc:getopt() is not exposed, a copy of
1060  * the getopt() is kept so that prio_sp can be reset to 1.
1061  */
1062 
1063 static int
1064 prio_getopt(argc, argv, opts)
1065 int	argc;
1066 #ifdef __STDC__
1067 char	*const *argv, *opts;
1068 #else
1069 char	**argv, *opts;
1070 #endif
1071 {
1072 	register char c;
1073 	register char *cp;
1074 
1075 	if (prio_sp == 1)
1076 		if (prio_optind >= argc ||
1077 		    argv[prio_optind][0] != '-' || argv[prio_optind][1] == '\0')
1078 			return (EOF);
1079 		else if (strcmp(argv[prio_optind], "--") == NULL) {
1080 			prio_optind++;
1081 			return (EOF);
1082 		}
1083 	prio_optopt = c = (unsigned char)argv[prio_optind][prio_sp];
1084 	if (c == ':' || (cp = strchr(opts, c)) == NULL) {
1085 		if (argv[prio_optind][++prio_sp] == '\0') {
1086 			prio_optind++;
1087 			prio_sp = 1;
1088 		}
1089 		return ('?');
1090 	}
1091 	if (*++cp == ':') {
1092 		if (argv[prio_optind][prio_sp+1] != '\0')
1093 			prio_optarg = &argv[prio_optind++][prio_sp+1];
1094 		else if (++prio_optind >= argc) {
1095 			prio_sp = 1;
1096 			return ('?');
1097 		} else
1098 			prio_optarg = argv[prio_optind++];
1099 		prio_sp = 1;
1100 	} else {
1101 		if (argv[prio_optind][++prio_sp] == '\0') {
1102 			prio_sp = 1;
1103 			prio_optind++;
1104 		}
1105 		prio_optarg = NULL;
1106 	}
1107 	return (c);
1108 }
1109