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