OpenSolaris_b135/cmd/priocntl/priocntl.c

Compare this file to the similar file:
Show the results in this format:

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
/*	  All Rights Reserved  	*/


#include	<stdio.h>
#include	<stdlib.h>
#include	<string.h>
#include	<wait.h>
#include	<search.h>
#include	<unistd.h>
#include	<sys/types.h>
#include	<dirent.h>
#include	<fcntl.h>
#include	<sys/param.h>
#include	<sys/procset.h>
#include	<sys/priocntl.h>
#include	<procfs.h>
#include	<macros.h>
#include	<libgen.h>
#include	<limits.h>
#include	<errno.h>

#include	"priocntl.h"

/*
 * This file contains the code implementing the class independent part
 * of the priocntl command.  Most of the useful work for the priocntl
 * command is done by the class specific sub-commands, the code for
 * which is elsewhere.  The class independent part of the command is
 * responsible for executing the appropriate class specific sub-commands
 * and providing any necessary input to the sub-commands.
 * Code in this file should never assume any knowledge of any specific
 * scheduler class (other than the SYS class).
 */

#define	CLASSPATH	"/usr/lib/class"

typedef struct classpids {
	char	clp_clname[PC_CLNMSZ];
	pid_t	*clp_pidlist;
	int	clp_pidlistsz;
	int	clp_npids;
} classpids_t;

static char usage[] =
"usage:	priocntl -l\n\
	priocntl -d [-i idtype] [idlist]\n\
	priocntl -s [-c class] [c.s.o.] [-i idtype] [idlist]\n\
	priocntl -e [-c class] [c.s.o.] command [argument(s)]\n";

static char	basenm[BASENMSZ];
static char	cmdpath[MAXPATHLEN];

static char	*procdir = "/proc";

static int	print_classlist(void);
static void	set_procs(char *, idtype_t, int, char **, char **);
static void	exec_cmd(char *, char **);
static int	print_procs(idtype_t, int, char *[]);
static void	ids2pids(idtype_t, id_t *, int, classpids_t *, int);
static void	add_pid_tolist(classpids_t *, int, char *, pid_t);
static void	increase_pidlist(classpids_t *);
static boolean_t	idmatch(char *, char *, int, char **);

/*
 * These variables are defined to be used in prio_getopt() below.
 */
static	int	prio_getopt();
/* LINTED static unused */
static	int	prio_optopt = 0;
static	char	*prio_optarg = 0;
static	int	prio_optind = 1;
static	int	prio_sp = 1;

int
main(int argc, char *argv[])
{
	int		c;
	int		lflag, dflag, sflag, eflag, cflag, iflag, csoptsflag;
	char		*clname;
	char		*idtypnm;
	idtype_t	idtype;
	int		idargc;
	char		**idargv;

	(void) strlcpy(cmdpath, argv[0], MAXPATHLEN);
	(void) strlcpy(basenm, basename(argv[0]), BASENMSZ);
	lflag = dflag = sflag = eflag = cflag = iflag = csoptsflag = 0;
	while ((c = prio_getopt(argc, argv, "ldsec:i:")) != -1) {

		switch (c) {

		case 'l':
			lflag++;
			break;

		case 'd':
			dflag++;
			break;

		case 's':
			sflag++;
			break;

		case 'e':
			eflag++;
			break;

		case 'c':
			cflag++;
			clname = prio_optarg;
			break;

		case 'i':
			iflag++;
			idtypnm = prio_optarg;
			break;

		case '?':
			if (strcmp(argv[prio_optind - 1], "-c") == 0 ||
			    strcmp(argv[prio_optind - 1], "-i") == 0) {

				/*
				 * getopt() will return ? if either
				 * of these appear without an argument.
				 */
				fatalerr(usage);
			}

			/*
			 * We assume for now that any option that
			 * getopt() doesn't recognize (with the
			 * exception of c and i) is intended for a
			 * class specific subcommand.  For now we also
			 * require that all class specific options
			 * take an argument (until we can get smarter
			 * about parsing our options).
			 */
			csoptsflag++;
			prio_optind++;
			prio_sp = 1;
			break;

		default:
			break;
		}
	}

	if (lflag) {
		if (dflag || sflag || eflag || cflag || iflag || csoptsflag)
			fatalerr(usage);

		return (print_classlist());

	} else if (dflag) {
		if (lflag || sflag || eflag || cflag || csoptsflag)
			fatalerr(usage);
		if (iflag) {
			if (str2idtyp(idtypnm, &idtype) == -1)
				fatalerr("%s: bad idtype %s\n", cmdpath,
				    idtypnm);
		} else {
			idtype = P_PID;
		}

		if (prio_optind < argc) {
			idargc = argc - prio_optind;
			idargv = &argv[prio_optind];
		} else {
			idargc = 0;
		}

		return (print_procs(idtype, idargc, idargv));

	} else if (sflag) {
		if (lflag || dflag || eflag)
			fatalerr(usage);
		if (iflag) {
			if (str2idtyp(idtypnm, &idtype) == -1)
				fatalerr("%s: bad idtype %s\n", cmdpath,
				    idtypnm);
		} else {
			idtype = P_PID;
		}

		if (cflag == 0)
			clname = NULL;

		if (prio_optind < argc) {
			idargc = argc - prio_optind;
			idargv = &argv[prio_optind];
		} else {
			idargc = 0;
		}

		set_procs(clname, idtype, idargc, idargv, argv);

	} else if (eflag) {
		if (lflag || dflag || sflag || iflag)
			fatalerr(usage);

		if (cflag == 0)
			clname = NULL;

		if (prio_optind >= argc)
			fatalerr(usage);

		exec_cmd(clname, argv);

	} else {
		fatalerr(usage);
	}

	return (0);
}


/*
 * Print the heading for the class list and execute the class
 * specific sub-command with the -l option for each configured class.
 */
static int
print_classlist(void)
{
	id_t		cid;
	int		nclass;
	pcinfo_t	pcinfo;
	static char	subcmdpath[128];
	int		status;
	pid_t		pid;
	int		error = 0;

	/*
	 * No special privileges required for this operation.
	 * Set the effective UID back to the real UID.
	 */
	if (setuid(getuid()) == -1)
		fatalerr("%s: Can't set effective UID back to real UID\n",
		    cmdpath);

	if ((nclass = priocntl(0, 0, PC_GETCLINFO, NULL)) == -1)
		fatalerr("%s: Can't get number of configured classes, priocntl"
		    " system call failed with errno %d\n", cmdpath, errno);

	(void) printf("CONFIGURED CLASSES\n==================\n\n");
	(void) printf("SYS (System Class)\n");
	for (cid = 1; cid < nclass; cid++) {
		(void) printf("\n");
		(void) fflush(stdout);
		pcinfo.pc_cid = cid;
		if (priocntl(0, 0, PC_GETCLINFO, (caddr_t)&pcinfo) == -1)
			fatalerr("%s: can't get class name (class ID = %ld)\n",
			    cmdpath, cid);
		if (snprintf(subcmdpath, sizeof (subcmdpath), "%s/%s/%s%s",
		    CLASSPATH, pcinfo.pc_clname, pcinfo.pc_clname, basenm) >=
		    sizeof (subcmdpath))
			fatalerr("%s: can't generate %s specific subcommand\n",
			    cmdpath, pcinfo.pc_clname);
		if ((pid = fork()) == 0) {
			(void) execl(subcmdpath, subcmdpath, "-l", (char *)0);
			(void) printf("%s\n", pcinfo.pc_clname);
			fatalerr("\tCan't execute %s specific subcommand\n",
			    pcinfo.pc_clname);
		} else if (pid == (pid_t)-1) {
			(void) printf("%s\n", pcinfo.pc_clname);
			(void) fprintf(stderr,
			    "Can't execute %s specific subcommand)\n",
			    pcinfo.pc_clname);
			error = 1;
		} else {
			(void) wait(&status);
			if (status)
				error = 1;
		}
	}

	return (error);
}


/*
 * For each class represented within the set of processes specified by
 * idtype/idargv, print_procs() executes the class specific sub-command
 * with the -d option.  We pipe to each sub-command a list of pids in
 * the set belonging to that class.
 */
static int
print_procs(idtype_t idtype, int idargc, char *idargv[])
{
	int		i;
	id_t		id;
	id_t		idlist[NIDS];
	int		nids;
	classpids_t	*clpids;
	int		nclass;
	id_t		cid;
	pcinfo_t	pcinfo;
	int		pidexists;
	FILE		*pipe_to_subcmd;
	char		subcmd[128];
	int		error = 0;


	/*
	 * Build a list of ids eliminating any duplicates in idargv.
	 */
	if (idtype == P_ALL) {
		/*
		 * No idlist should be specified. If one is specified,
		 * it is ignored.
		 */
		nids = 0;
	} else if (idargc == 0) {

		/*
		 * No ids supplied by user; use current id.
		 */
		if (getmyid(idtype, &idlist[0]) == -1)
			fatalerr("%s: Can't get ID for current process,"
			    " idtype = %d\n", cmdpath, idtype);
		nids = 1;
	} else {
		nids = 0;
		for (i = 0; i < idargc && nids < NIDS; i++) {
			if (idtype == P_CID) {
				if ((id = clname2cid(idargv[i])) == -1) {
					(void) fprintf(stderr, "%s: Invalid or"
					    " unconfigured class %s in idlist"
					    " - ignored\n", cmdpath, idargv[i]);
					error = 1;
				}
			} else {
				id = (id_t)str2num(idargv[i], INT_MIN, INT_MAX);
				if (errno) {
					(void) fprintf(stderr,
					    "%s: Invalid id \"%s\"\n",
					    cmdpath, idargv[i]);
					error = 1;
					id = BADPID;
				}
			}

			/*
			 * lsearch(3C) adds ids to the idlist,
			 * eliminating duplicates.
			 */
			(void) lsearch((void *)&id, (void *)idlist,
			    (size_t *)&nids, sizeof (id), (int (*)())idcompar);
		}
	}

	if ((nclass = priocntl(0, 0, PC_GETCLINFO, NULL)) == -1)
		fatalerr("%s: Can't get number of configured classes, priocntl"
		    " system call failed with errno %d\n", cmdpath, errno);

	if ((clpids = (classpids_t *)malloc(sizeof (classpids_t) * nclass)) ==
	    NULL)
		fatalerr("%s: Can't allocate memory for clpids.\n", cmdpath);

	for (cid = 1; cid < nclass; cid++) {
		pcinfo.pc_cid = cid;
		if (priocntl(0, 0, PC_GETCLINFO, (caddr_t)&pcinfo) == -1)
			fatalerr("%s: Can't get class name, cid = %ld\n",
			    cmdpath, cid);

		(void) strncpy(clpids[cid].clp_clname, pcinfo.pc_clname,
		    PC_CLNMSZ);

		/*
		 * The memory allocation for the pidlist uses realloc().
		 * A realloc() call is required, when "clp_npids" is
		 * equal to "clp_pidlistsz".
		 */
		clpids[cid].clp_pidlist = (pid_t *)NULL;
		clpids[cid].clp_pidlistsz = 0;
		clpids[cid].clp_npids = 0;
	}

	/*
	 * Build the pidlist.
	 */
	ids2pids(idtype, idlist, nids, clpids, nclass);

	/*
	 * No need for special privileges any more.
	 * Set the effective UID back to the real UID.
	 */
	if (setuid(getuid()) == -1)
		fatalerr("%s: Can't set effective UID back to real UID\n",
		    cmdpath);

	pidexists = 0;
	for (cid = 1; cid < nclass; cid++) {
		if (clpids[cid].clp_npids == 0)
			continue;

		pidexists = 1;
		if (snprintf(subcmd, sizeof (subcmd), "%s/%s/%s%s -d",
		    CLASSPATH, clpids[cid].clp_clname, clpids[cid].clp_clname,
		    basenm) >= sizeof (subcmd)) {
			(void) fprintf(stderr,
			    "Can't generate %s specific subcommand\n",
			    clpids[cid].clp_clname);
			error = 1;
			free(clpids[cid].clp_pidlist);
			continue;
		}
		if ((pipe_to_subcmd = popen(subcmd, "w")) == NULL) {
			(void) printf("%s\n", clpids[cid].clp_clname);
			(void) fprintf(stderr,
			    "Can't execute %s specific subcommand\n",
			    clpids[cid].clp_clname);
			error = 1;
			free(clpids[cid].clp_pidlist);
			continue;
		}
		(void) fwrite(clpids[cid].clp_pidlist, sizeof (pid_t),
		    clpids[cid].clp_npids, pipe_to_subcmd);
		if (pclose(pipe_to_subcmd))
			error = 1;

		free(clpids[cid].clp_pidlist);
	}

	free(clpids);

	if (pidexists == 0)
		fatalerr("%s: Process(es) not found.\n", cmdpath);

	return (error);
}


/*
 * Execute the appropriate class specific sub-command with the arguments
 * pointed to by subcmdargv.  If the user specified a class we simply
 * exec the sub-command for that class.  If no class was specified we
 * verify that the processes in the set specified by idtype/idargv are
 * all in the same class and then execute the sub-command for that class.
 */
static void
set_procs(clname, idtype, idargc, idargv, subcmdargv)
char		*clname;
idtype_t	idtype;
int		idargc;
char		**idargv;
char		**subcmdargv;
{
	char			idstr[PC_IDTYPNMSZ];
	char			myidstr[PC_IDTYPNMSZ];
	char			clnmbuf[PC_CLNMSZ];
	pcinfo_t		pcinfo;
	static psinfo_t		prinfo;
	static prcred_t		prcred;
	DIR			*dirp;
	struct dirent		*dentp;
	static char		pname[100];
	char			*fname;
	int			procfd;
	int			saverr;
	static char		subcmdpath[128];
	boolean_t		procinset;
	id_t			id;
	size_t			len;

	if (clname == NULL && idtype == P_PID && idargc <= 1) {

		/*
		 * No class specified by user but only one process
		 * in specified set.  Get the class the easy way.
		 */
		if (idargc == 0) {
			if (priocntl(P_PID, P_MYID, PC_GETXPARMS, NULL,
			    PC_KY_CLNAME, clnmbuf, 0) == -1)
				if (errno == ESRCH)
					fatalerr("%s: Process not found.\n",
					    cmdpath);
				else
					fatalerr("%s: Can't get class of"
					    " current process\npriocntl"
					    " system call failed with"
					    " errno %d\n", cmdpath, errno);
		} else {
			/* idargc == 1 */
			id = (id_t)str2num(idargv[0], INT_MIN, INT_MAX);
			if (errno)
				fatalerr("%s: Invalid id \"%s\"\n", cmdpath,
				    idargv[0]);

			if (priocntl(P_PID, id, PC_GETXPARMS,
			    NULL, PC_KY_CLNAME, clnmbuf, 0) == -1)
				if (errno == ESRCH)
					fatalerr("%s: Process not found.\n",
					    cmdpath);
				else
					fatalerr("%s: Can't get class of "
					    " specified  process\npriocntl"
					    " system call failed with"
					    " errno %d\n", cmdpath, errno);
		}

		clname = clnmbuf;
	} else if (clname == NULL) {

		/*
		 * No class specified by user and potentially more
		 * than one process in specified set.  Verify that
		 * all procs in set are in the same class.
		 */
		if (idargc == 0 && idtype != P_ALL) {

			/*
			 * No ids supplied by user; use current id.
			 */
			if (getmyidstr(idtype, myidstr) == -1)
				fatalerr("%s: Can't get ID string for current"
				    " process, idtype = %d\n", cmdpath, idtype);
		}
		if ((dirp = opendir(procdir)) == NULL)
			fatalerr("%s: Can't open PROC directory %s\n",
			    cmdpath, procdir);

		while ((dentp = readdir(dirp)) != NULL) {
			if (dentp->d_name[0] == '.')	/* skip . and .. */
				continue;

			len = snprintf(pname, sizeof (pname), "%s/%s/",
			    procdir, dentp->d_name);
			/* Really max(sizeof ("psinfo"), sizeof ("cred")) */
			if (len + sizeof ("psinfo") > sizeof (pname)) {
				(void) fprintf(stderr,
				    "%s: skipping %s, name too long.\n",
				    cmdpath, dentp->d_name);
				continue;
			}
			fname = pname + len;
retry:
			(void) strcpy(fname, "psinfo");
			if ((procfd = open(pname, O_RDONLY)) < 0)
				continue;

			if (read(procfd, &prinfo, sizeof (prinfo)) !=
			    sizeof (prinfo)) {
				saverr = errno;
				(void) close(procfd);
				if (saverr == EAGAIN)
					goto retry;
				if (saverr != ENOENT) {
					(void) fprintf(stderr,
					    "%s: Can't get process info for"
					    " %s\n", cmdpath, pname);
				}
				continue;
			}
			(void) close(procfd);

			if (idtype == P_UID || idtype == P_GID) {
				(void) strcpy(fname, "cred");
				if ((procfd = open(pname, O_RDONLY)) < 0 ||
				    read(procfd, &prcred, sizeof (prcred)) !=
				    sizeof (prcred)) {
					saverr = errno;
					if (procfd >= 0)
						(void) close(procfd);
					if (saverr == EAGAIN)
						goto retry;
					if (saverr != ENOENT) {
						(void) fprintf(stderr,
						    "%s: Can't get process"
						    " credentials for %s\n",
						    cmdpath, pname);
					}
					continue;
				}
				(void) close(procfd);
			}

			if (prinfo.pr_lwp.pr_state == 0 || prinfo.pr_nlwp == 0)
				continue;


			switch (idtype) {

			case P_PID:
				itoa((long)prinfo.pr_pid, idstr);
				procinset = idmatch(idstr, myidstr,
				    idargc, idargv);
				break;

			case P_PPID:
				itoa((long)prinfo.pr_ppid, idstr);
				procinset = idmatch(idstr, myidstr,
				    idargc, idargv);
				break;

			case P_PGID:
				itoa((long)prinfo.pr_pgid, idstr);
				procinset = idmatch(idstr, myidstr,
				    idargc, idargv);
				break;

			case P_SID:
				itoa((long)prinfo.pr_sid, idstr);
				procinset = idmatch(idstr, myidstr,
				    idargc, idargv);
				break;

			case P_CID:
				procinset = idmatch(prinfo.pr_lwp.pr_clname,
				    myidstr, idargc, idargv);
				break;

			case P_UID:
				itoa((long)prcred.pr_euid, idstr);
				procinset = idmatch(idstr, myidstr,
				    idargc, idargv);
				break;

			case P_GID:
				itoa((long)prcred.pr_egid, idstr);
				procinset = idmatch(idstr, myidstr,
				    idargc, idargv);
				break;

			case P_PROJID:
				itoa((long)prinfo.pr_projid, idstr);
				procinset = idmatch(idstr, myidstr,
				    idargc, idargv);
				break;

			case P_TASKID:
				itoa((long)prinfo.pr_taskid, idstr);
				procinset = idmatch(idstr, myidstr,
				    idargc, idargv);
				break;

			case P_ZONEID:
				itoa((long)prinfo.pr_zoneid, idstr);
				procinset = idmatch(idstr, myidstr,
				    idargc, idargv);
				break;

			case P_CTID:
				itoa((long)prinfo.pr_contract, idstr);
				procinset = idmatch(idstr, myidstr,
				    idargc, idargv);
				break;

			case P_ALL:
				procinset = B_TRUE;
				break;

			default:
				fatalerr("%s: Bad idtype %d in set_procs()\n",
				    cmdpath, idtype);
			}
			if (procinset == B_TRUE) {
				if (clname == NULL) {

					/*
					 * First proc found in set.
					 */
					(void) strcpy(clnmbuf,
					    prinfo.pr_lwp.pr_clname);
					clname = clnmbuf;
				} else if (strcmp(clname,
				    prinfo.pr_lwp.pr_clname) != 0) {
					fatalerr("%s: Specified processes"
					    " from different classes.\n",
					    cmdpath);
				}
			}
		}
		(void) closedir(dirp);
		if (clname == NULL)
			fatalerr("%s: Process(es) not found.\n", cmdpath);
	} else {

		/*
		 * User specified class. Check it for validity.
		 */
		(void) strcpy(pcinfo.pc_clname, clname);
		if (priocntl(0, 0, PC_GETCID, (caddr_t)&pcinfo) == -1)
			fatalerr("%s: Invalid or unconfigured class %s\n",
			    cmdpath, clname);
	}

	/*
	 * No need for special privileges any more.
	 * Set the effective UID back to the real UID.
	 */
	if (setuid(getuid()) == -1)
		fatalerr("%s: Can't set effective UID back to real UID\n",
		    cmdpath);

	if (snprintf(subcmdpath, sizeof (subcmdpath), "%s/%s/%s%s",
	    CLASSPATH, clname, clname, basenm) >= sizeof (subcmdpath))
		fatalerr("%s: can't generate %s specific subcommand\n",
		    cmdpath, clname);

	subcmdargv[0] = subcmdpath;
	(void) execv(subcmdpath, subcmdargv);
	fatalerr("%s: Can't execute %s sub-command\n", cmdpath, clname);
}


/*
 * Execute the appropriate class specific sub-command with the arguments
 * pointed to by subcmdargv.  If the user specified a class we simply
 * exec the sub-command for that class.  If no class was specified we
 * execute the sub-command for our own current class.
 */
static void
exec_cmd(clname, subcmdargv)
char	*clname;
char	**subcmdargv;
{
	pcinfo_t	pcinfo;
	char		clnmbuf[PC_CLNMSZ];
	char		subcmdpath[128];

	/*
	 * No special privileges required for this operation.
	 * Set the effective UID back to the real UID.
	 */
	if (setuid(getuid()) == -1)
		fatalerr("%s: Can't set effective UID back to real UID\n",
		    cmdpath);

	if (clname == NULL) {
		if (priocntl(P_PID, P_MYID, PC_GETXPARMS, NULL,
		    PC_KY_CLNAME, clnmbuf, 0) == -1)
			fatalerr("%s: Can't get class name of current process\n"
			    "priocntl system call failed with errno %d\n",
			    cmdpath, errno);

		clname = clnmbuf;
	} else {

		/*
		 * User specified class. Check it for validity.
		 */
		(void) strcpy(pcinfo.pc_clname, clname);
		if (priocntl(0, 0, PC_GETCID, (caddr_t)&pcinfo) == -1)
			fatalerr("%s: Invalid or unconfigured class %s\n",
			    cmdpath, clname);
	}

	if (snprintf(subcmdpath, sizeof (subcmdpath), "%s/%s/%s%s",
	    CLASSPATH, clname, clname, basenm) >= sizeof (subcmdpath))
		fatalerr("%s: can't generate %s specific subcommand\n",
		    cmdpath, clname);
	subcmdargv[0] = subcmdpath;
	(void) execv(subcmdpath, subcmdargv);
	fatalerr("%s: Can't execute %s sub-command\n", cmdpath, clname);
}


/*
 * Fill in the classpids structures in the array pointed to by clpids
 * with pids for the processes in the set specified by idtype/idlist.
 * We read the /proc/<pid>/psinfo file to get the necessary process
 * information.
 */
static void
ids2pids(idtype, idlist, nids, clpids, nclass)
idtype_t	idtype;
id_t		*idlist;
int		nids;
classpids_t	*clpids;
int		nclass;
{
	static psinfo_t		prinfo;
	static prcred_t		prcred;
	DIR			*dirp;
	struct dirent		*dentp;
	char			pname[100];
	char			*fname;
	int			procfd;
	int			saverr;
	int			i;
	char			*clname;
	size_t			len;

	if ((dirp = opendir(procdir)) == NULL)
		fatalerr("%s: Can't open PROC directory %s\n",
		    cmdpath, procdir);

	while ((dentp = readdir(dirp)) != NULL) {
		if (dentp->d_name[0] == '.')	/* skip . and .. */
			continue;

		len = snprintf(pname, sizeof (pname), "%s/%s/",
		    procdir, dentp->d_name);
		/* Really max(sizeof ("psinfo"), sizeof ("cred")) */
		if (len + sizeof ("psinfo") > sizeof (pname)) {
			(void) fprintf(stderr,
			    "%s: skipping %s, name too long.\n",
			    cmdpath, dentp->d_name);
			continue;
		}
		fname = pname + len;
retry:
		(void) strcpy(fname, "psinfo");
		if ((procfd = open(pname, O_RDONLY)) < 0)
			continue;
		if (read(procfd, &prinfo, sizeof (prinfo)) != sizeof (prinfo)) {
			saverr = errno;
			(void) close(procfd);
			if (saverr == EAGAIN)
				goto retry;
			if (saverr != ENOENT) {
				(void) fprintf(stderr,
				    "%s: Can't get process info for %s\n",
				    cmdpath, pname);
			}
			continue;
		}
		(void) close(procfd);

		if (idtype == P_UID || idtype == P_GID) {
			(void) strcpy(fname, "cred");
			if ((procfd = open(pname, O_RDONLY)) < 0 ||
			    read(procfd, &prcred, sizeof (prcred)) !=
			    sizeof (prcred)) {
				saverr = errno;
				(void) close(procfd);
				if (saverr == EAGAIN)
					goto retry;
				if (saverr != ENOENT) {
					(void) fprintf(stderr,
					    "%s: Can't get process credentials"
					    " for %s\n",
					    cmdpath, pname);
				}
				continue;
			}
			(void) close(procfd);
		}

		if (prinfo.pr_lwp.pr_state == 0 || prinfo.pr_nlwp == 0)
			continue;

		switch (idtype) {

		case P_PID:
			for (i = 0; i < nids; i++) {
				if (idlist[i] == (id_t)prinfo.pr_pid)
					add_pid_tolist(clpids, nclass,
					    prinfo.pr_lwp.pr_clname,
					    prinfo.pr_pid);
			}
			break;

		case P_PPID:
			for (i = 0; i < nids; i++) {
				if (idlist[i] == (id_t)prinfo.pr_ppid)
					add_pid_tolist(clpids, nclass,
					    prinfo.pr_lwp.pr_clname,
					    prinfo.pr_pid);
			}
			break;

		case P_PGID:
			for (i = 0; i < nids; i++) {
				if (idlist[i] == (id_t)prinfo.pr_pgid)
					add_pid_tolist(clpids, nclass,
					    prinfo.pr_lwp.pr_clname,
					    prinfo.pr_pid);
			}
			break;

		case P_SID:
			for (i = 0; i < nids; i++) {
				if (idlist[i] == (id_t)prinfo.pr_sid)
					add_pid_tolist(clpids, nclass,
					    prinfo.pr_lwp.pr_clname,
					    prinfo.pr_pid);
			}
			break;

		case P_CID:
			for (i = 0; i < nids; i++) {
				clname = clpids[idlist[i]].clp_clname;
				if (strcmp(clname,
				    prinfo.pr_lwp.pr_clname) == 0)
					add_pid_tolist(clpids, nclass,
					    prinfo.pr_lwp.pr_clname,
					    prinfo.pr_pid);
			}
			break;

		case P_UID:
			for (i = 0; i < nids; i++) {
				if (idlist[i] == (id_t)prcred.pr_euid)
					add_pid_tolist(clpids, nclass,
					    prinfo.pr_lwp.pr_clname,
					    prinfo.pr_pid);
			}
			break;

		case P_GID:
			for (i = 0; i < nids; i++) {
				if (idlist[i] == (id_t)prcred.pr_egid)
					add_pid_tolist(clpids, nclass,
					    prinfo.pr_lwp.pr_clname,
					    prinfo.pr_pid);
			}
			break;

		case P_PROJID:
			for (i = 0; i < nids; i++) {
				if (idlist[i] == (id_t)prinfo.pr_projid)
					add_pid_tolist(clpids, nclass,
					    prinfo.pr_lwp.pr_clname,
					    prinfo.pr_pid);
			}
			break;

		case P_TASKID:
			for (i = 0; i < nids; i++) {
				if (idlist[i] == (id_t)prinfo.pr_taskid)
					add_pid_tolist(clpids, nclass,
					    prinfo.pr_lwp.pr_clname,
					    prinfo.pr_pid);
			}
		break;

		case P_ZONEID:
			for (i = 0; i < nids; i++) {
				if (idlist[i] == (id_t)prinfo.pr_zoneid)
					add_pid_tolist(clpids, nclass,
					    prinfo.pr_lwp.pr_clname,
					    prinfo.pr_pid);
			}
			break;

		case P_CTID:
			for (i = 0; i < nids; i++) {
				if (idlist[i] == (id_t)prinfo.pr_contract)
					add_pid_tolist(clpids, nclass,
					    prinfo.pr_lwp.pr_clname,
					    prinfo.pr_pid);
			}
			break;

		case P_ALL:
			add_pid_tolist(clpids, nclass, prinfo.pr_lwp.pr_clname,
			    prinfo.pr_pid);
			break;

		default:
			fatalerr("%s: Bad idtype %d in ids2pids()\n",
			    cmdpath, idtype);
		}
	}
	(void) closedir(dirp);
}


/*
 * Search the array pointed to by clpids for the classpids
 * structure corresponding to clname and add pid to its
 * pidlist.
 */
static void
add_pid_tolist(clpids, nclass, clname, pid)
classpids_t	*clpids;
int		nclass;
char		*clname;
pid_t		pid;
{
	classpids_t	*clp;

	for (clp = clpids; clp != &clpids[nclass]; clp++) {
		if (strcmp(clp->clp_clname, clname) == 0) {
			if (clp->clp_npids == clp->clp_pidlistsz)
				increase_pidlist(clp);

			(clp->clp_pidlist)[clp->clp_npids] = pid;
			clp->clp_npids++;
			return;
		}
	}
}


static void
increase_pidlist(classpids_t *clp)
{
	if ((clp->clp_pidlist = realloc(clp->clp_pidlist,
	    (clp->clp_pidlistsz + NPIDS) * sizeof (pid_t))) == NULL)
		/*
		 * The pidlist is filled up and we cannot increase the size.
		 */
		fatalerr("%s: Can't allocate memory for pidlist.\n", cmdpath);

	clp->clp_pidlistsz += NPIDS;
}


/*
 * Compare id strings for equality.  If idargv contains ids
 * (idargc > 0) compare idstr to each id in idargv, otherwise
 * just compare to curidstr.
 */
static boolean_t
idmatch(idstr, curidstr, idargc, idargv)
char	*idstr;
char	*curidstr;
int	idargc;
char	**idargv;
{
	int	i;

	if (idargc == 0) {
		if (strcmp(curidstr, idstr) == 0)
			return (B_TRUE);
	} else {
		for (i = 0; i < idargc; i++) {
			if (strcmp(idargv[i], idstr) == 0)
				return (B_TRUE);
		}
	}
	return (B_FALSE);
}

/*
 * This is a copy of the getopt() function found in libc:getopt.c. A separate
 * copy is required to fix the bug id #1114636. To fix the problem we need to
 * reset the _sp to 1. Since _sp in libc:getopt() is not exposed, a copy of
 * the getopt() is kept so that prio_sp can be reset to 1.
 */

static int
prio_getopt(argc, argv, opts)
int	argc;
#ifdef __STDC__
char	*const *argv, *opts;
#else
char	**argv, *opts;
#endif
{
	register char c;
	register char *cp;

	if (prio_sp == 1)
		if (prio_optind >= argc ||
		    argv[prio_optind][0] != '-' || argv[prio_optind][1] == '\0')
			return (EOF);
		else if (strcmp(argv[prio_optind], "--") == NULL) {
			prio_optind++;
			return (EOF);
		}
	prio_optopt = c = (unsigned char)argv[prio_optind][prio_sp];
	if (c == ':' || (cp = strchr(opts, c)) == NULL) {
		if (argv[prio_optind][++prio_sp] == '\0') {
			prio_optind++;
			prio_sp = 1;
		}
		return ('?');
	}
	if (*++cp == ':') {
		if (argv[prio_optind][prio_sp+1] != '\0')
			prio_optarg = &argv[prio_optind++][prio_sp+1];
		else if (++prio_optind >= argc) {
			prio_sp = 1;
			return ('?');
		} else
			prio_optarg = argv[prio_optind++];
		prio_sp = 1;
	} else {
		if (argv[prio_optind][++prio_sp] == '\0') {
			prio_sp = 1;
			prio_optind++;
		}
		prio_optarg = NULL;
	}
	return (c);
}