summaryrefslogtreecommitdiff
path: root/usr/src/cmd/priocntl/subr.c
diff options
context:
space:
mode:
authorstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
committerstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
commit7c478bd95313f5f23a4c958a745db2134aa03244 (patch)
treec871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/cmd/priocntl/subr.c
downloadillumos-joyent-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz
OpenSolaris Launch
Diffstat (limited to 'usr/src/cmd/priocntl/subr.c')
-rw-r--r--usr/src/cmd/priocntl/subr.c729
1 files changed, 729 insertions, 0 deletions
diff --git a/usr/src/cmd/priocntl/subr.c b/usr/src/cmd/priocntl/subr.c
new file mode 100644
index 0000000000..a9caf5fff5
--- /dev/null
+++ b/usr/src/cmd/priocntl/subr.c
@@ -0,0 +1,729 @@
+/*
+ * 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 <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <limits.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <sys/time.h>
+#include <sys/procset.h>
+#include <sys/priocntl.h>
+#include <sys/task.h>
+#include <procfs.h>
+#include <project.h>
+#include <errno.h>
+#include <zone.h>
+#include <libcontract_priv.h>
+
+#include "priocntl.h"
+
+/*LINTLIBRARY*/
+
+/*
+ * Utility functions for priocntl command.
+ */
+
+static char *procdir = "/proc";
+
+/*PRINTFLIKE1*/
+void
+fatalerr(format, a1, a2, a3, a4, a5)
+char *format;
+int a1, a2, a3, a4, a5;
+{
+ (void) fprintf(stderr, format, a1, a2, a3, a4, a5);
+ exit(1);
+}
+
+
+/*
+ * Structure defining idtypes known to the priocntl command
+ * along with the corresponding names and a liberal guess
+ * of the max number of procs sharing any given ID of that type.
+ * The idtype values themselves are defined in <sys/procset.h>.
+ */
+static struct idtypes {
+ idtype_t idtype;
+ char *idtypnm;
+} idtypes [] = {
+ { P_PID, "pid" },
+ { P_PPID, "ppid" },
+ { P_PGID, "pgid" },
+ { P_SID, "sid" },
+ { P_CID, "class" },
+ { P_UID, "uid" },
+ { P_GID, "gid" },
+ { P_PROJID, "projid" },
+ { P_TASKID, "taskid" },
+ { P_ZONEID, "zoneid" },
+ { P_CTID, "ctid" },
+ { P_ALL, "all" }
+};
+
+#define IDCNT (sizeof (idtypes) / sizeof (struct idtypes))
+
+
+int
+str2idtyp(idtypnm, idtypep)
+char *idtypnm;
+idtype_t *idtypep;
+{
+ register struct idtypes *curp;
+ register struct idtypes *endp;
+
+ for (curp = idtypes, endp = &idtypes[IDCNT]; curp < endp; curp++) {
+ if (strcmp(curp->idtypnm, idtypnm) == 0) {
+ *idtypep = curp->idtype;
+ return (0);
+ }
+ }
+ return (-1);
+}
+
+
+int
+idtyp2str(idtype, idtypnm)
+idtype_t idtype;
+char *idtypnm;
+{
+ register struct idtypes *curp;
+ register struct idtypes *endp;
+
+ for (curp = idtypes, endp = &idtypes[IDCNT]; curp < endp; curp++) {
+ if (idtype == curp->idtype) {
+ (void) strncpy(idtypnm, curp->idtypnm, PC_IDTYPNMSZ);
+ return (0);
+ }
+ }
+ return (-1);
+}
+
+
+/*
+ * Compare two IDs for equality.
+ */
+int
+idcompar(id1p, id2p)
+id_t *id1p;
+id_t *id2p;
+{
+ if (*id1p == *id2p)
+ return (0);
+ else
+ return (-1);
+}
+
+
+id_t
+clname2cid(clname)
+char *clname;
+{
+ pcinfo_t pcinfo;
+
+ (void) strncpy(pcinfo.pc_clname, clname, PC_CLNMSZ);
+ if (priocntl(0, 0, PC_GETCID, (caddr_t)&pcinfo) == -1)
+ return ((id_t)-1);
+ return (pcinfo.pc_cid);
+}
+
+
+int
+getmyid(idtype, idptr)
+idtype_t idtype;
+id_t *idptr;
+{
+ pcinfo_t pcinfo;
+
+ switch (idtype) {
+
+ case P_PID:
+ *idptr = (id_t)getpid();
+ break;
+
+ case P_PPID:
+ *idptr = (id_t)getppid();
+ break;
+
+ case P_PGID:
+ *idptr = (id_t)getpgrp();
+ break;
+
+ case P_SID:
+ *idptr = (id_t)getsid(getpid());
+ break;
+
+ case P_CID:
+ if (priocntl(P_PID, P_MYID, PC_GETXPARMS, NULL,
+ PC_KY_CLNAME, pcinfo.pc_clname, 0) == -1 ||
+ priocntl(0, 0, PC_GETCID, (caddr_t)&pcinfo) == -1)
+ return (-1);
+
+ *idptr = pcinfo.pc_cid;
+ break;
+
+ case P_UID:
+ *idptr = (id_t)getuid();
+ break;
+
+ case P_GID:
+ *idptr = (id_t)getgid();
+ break;
+
+ case P_PROJID:
+ *idptr = (id_t)getprojid();
+ break;
+
+ case P_TASKID:
+ *idptr = (id_t)gettaskid();
+ break;
+
+ case P_ZONEID:
+ *idptr = (id_t)getzoneid();
+ break;
+
+ case P_CTID: {
+ ctid_t id = getctid();
+ if (id == -1)
+ return (-1);
+ *idptr = id;
+ break;
+ }
+
+ default:
+ return (-1);
+ }
+ return (0);
+}
+
+
+int
+getmyidstr(idtype, idstr)
+idtype_t idtype;
+char *idstr;
+{
+ char clname[PC_CLNMSZ];
+
+ switch (idtype) {
+
+ case P_PID:
+ itoa((long)getpid(), idstr);
+ break;
+
+ case P_PPID:
+ itoa((long)getppid(), idstr);
+ break;
+
+ case P_PGID:
+ itoa((long)getpgrp(), idstr);
+ break;
+ case P_SID:
+ itoa((long)getsid(getpid()), idstr);
+ break;
+
+ case P_CID:
+ if (priocntl(P_PID, P_MYID, PC_GETXPARMS, NULL,
+ PC_KY_CLNAME, clname, 0) == -1)
+ return (-1);
+ (void) strncpy(idstr, clname, PC_CLNMSZ);
+ break;
+
+ case P_UID:
+ itoa((long)getuid(), idstr);
+ break;
+
+ case P_GID:
+ itoa((long)getgid(), idstr);
+ break;
+
+ case P_PROJID:
+ itoa((long)getprojid(), idstr);
+ break;
+
+ case P_TASKID:
+ itoa((long)gettaskid(), idstr);
+ break;
+
+ case P_ZONEID:
+ itoa((long)getzoneid(), idstr);
+ break;
+
+ case P_CTID: {
+ id_t id;
+ if ((id = getctid()) == -1)
+ return (-1);
+ itoa((long)id, idstr);
+ break;
+ }
+
+ default:
+ return (-1);
+ }
+ return (0);
+}
+
+/*
+ * Look for pids with "upri > uprilim" in the set specified by idtype/id.
+ * If upri exceeds uprilim then print a warning.
+ */
+int
+verifyupri(idtype_t idtype, id_t id, char *clname, int key,
+ pri_t upri, char *basenm)
+{
+ psinfo_t prinfo;
+ prcred_t prcred;
+ DIR *dirp;
+ struct dirent *dentp;
+ char pname[MAXNAMLEN];
+ char *fname;
+ int procfd;
+ int saverr;
+ pri_t uprilim;
+ int verify;
+ int error = 0;
+
+ if (idtype == P_PID) {
+ if (priocntl(P_PID, id, PC_GETXPARMS, clname, key,
+ &uprilim, 0) == -1)
+ error = -1;
+ else if (upri > uprilim)
+ (void) fprintf(stderr,
+ "%s: Specified user priority %d exceeds"
+ " limit %d; set to %d (pid %d)\n",
+ basenm, upri, uprilim, uprilim, (int)id);
+
+ return (error);
+ }
+
+ /*
+ * Look for the processes in the set specified by idtype/id.
+ * We read the /proc/<pid>/psinfo file to get the necessary
+ * process information.
+ */
+
+ if ((dirp = opendir(procdir)) == NULL)
+ fatalerr("%s: Can't open PROC directory %s\n",
+ basenm, procdir);
+
+ while ((dentp = readdir(dirp)) != NULL) {
+ if (dentp->d_name[0] == '.') /* skip . and .. */
+ continue;
+
+ (void) snprintf(pname, MAXNAMLEN, "%s/%s/",
+ procdir, dentp->d_name);
+ fname = pname + strlen(pname);
+retry:
+ (void) strncpy(fname, "psinfo", strlen("psinfo") + 1);
+ 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;
+ continue;
+ }
+ (void) close(procfd);
+
+ if (idtype == P_UID || idtype == P_GID) {
+ (void) strncpy(fname, "cred", strlen("cred") + 1);
+ if ((procfd = open(pname, O_RDONLY)) < 0 ||
+ read(procfd, &prcred, sizeof (prcred)) !=
+ sizeof (prcred)) {
+ saverr = errno;
+ (void) close(procfd);
+ if (saverr == EAGAIN)
+ goto retry;
+ continue;
+ }
+ (void) close(procfd);
+ }
+
+ if (prinfo.pr_lwp.pr_state == 0 || prinfo.pr_nlwp == 0)
+ continue;
+
+ /*
+ * The lwp must be in the correct class.
+ */
+ if (strncmp(clname, prinfo.pr_lwp.pr_clname, PC_CLNMSZ) != 0)
+ continue;
+
+ verify = 0;
+ switch (idtype) {
+
+ case P_PPID:
+ if (id == (id_t)prinfo.pr_ppid)
+ verify++;
+ break;
+
+ case P_PGID:
+ if (id == (id_t)prinfo.pr_pgid)
+ verify++;
+ break;
+
+ case P_SID:
+ if (id == (id_t)prinfo.pr_sid)
+ verify++;
+ break;
+
+ case P_UID:
+ if (id == (id_t)prcred.pr_euid)
+ verify++;
+ break;
+
+ case P_GID:
+ if (id == (id_t)prcred.pr_egid)
+ verify++;
+ break;
+
+ case P_PROJID:
+ if (id == (id_t)prinfo.pr_projid)
+ verify++;
+ break;
+
+ case P_TASKID:
+ if (id == (id_t)prinfo.pr_taskid)
+ verify++;
+ break;
+
+ case P_ZONEID:
+ if (id == (id_t)prinfo.pr_zoneid)
+ verify++;
+ break;
+
+ case P_CTID:
+ if (id == (id_t)prinfo.pr_contract)
+ verify++;
+ break;
+
+ case P_CID:
+ case P_ALL:
+ verify++;
+ break;
+
+ default:
+ fatalerr("%s: Bad idtype %d in verifyupri()\n",
+ basenm, idtype);
+ }
+
+ if (verify) {
+ if (priocntl(P_PID, prinfo.pr_pid, PC_GETXPARMS,
+ clname, key, &uprilim, 0) == -1)
+ error = -1;
+ else if (upri > uprilim)
+ (void) fprintf(stderr,
+ "%s: Specified user priority %d exceeds"
+ " limit %d; set to %d (pid %d)\n",
+ basenm, upri, uprilim, uprilim,
+ (int)prinfo.pr_pid);
+ }
+ }
+ (void) closedir(dirp);
+
+ return (error);
+}
+
+
+/*
+ * Read a list of pids from a stream.
+ */
+pid_t *
+read_pidlist(size_t *npidsp, FILE *filep)
+{
+ size_t nitems;
+ pid_t *pidlist = NULL;
+
+ *npidsp = 0;
+
+ do {
+ if ((pidlist = (pid_t *)realloc(pidlist,
+ (*npidsp + NPIDS) * sizeof (pid_t))) == NULL)
+ return (NULL);
+
+ nitems = fread(pidlist + *npidsp, sizeof (pid_t), NPIDS, filep);
+ if (ferror(filep))
+ return (NULL);
+
+ *npidsp += nitems;
+ } while (nitems == NPIDS);
+
+ return (pidlist);
+}
+
+
+void
+free_pidlist(pid_t *pidlist)
+{
+ free(pidlist);
+}
+
+
+long
+str2num(char *p, long min, long max)
+{
+ long val;
+ char *q;
+ errno = 0;
+
+ val = strtol(p, &q, 10);
+ if (errno != 0 || q == p || *q != '\0' || val < min || val > max)
+ errno = EINVAL;
+
+ return (val);
+}
+
+
+/*
+ * itoa() and reverse() taken almost verbatim from K & R Chapter 3.
+ */
+static void reverse();
+
+/*
+ * itoa(): Convert n to characters in s.
+ */
+void
+itoa(n, s)
+long n;
+char *s;
+{
+ long i, sign;
+
+ if ((sign = n) < 0) /* record sign */
+ n = -n; /* make sign positive */
+ i = 0;
+ do { /* generate digits in reverse order */
+ s[i++] = n % 10 + '0'; /* get next digit */
+ } while ((n /= 10) > 0); /* delete it */
+ if (sign < 0)
+ s[i++] = '-';
+ s[i] = '\0';
+ reverse(s);
+}
+
+
+/*
+ * reverse(): Reverse string s in place.
+ */
+static void
+reverse(s)
+char *s;
+{
+ int c, i, j;
+
+ for (i = 0, j = strlen(s) - 1; i < j; i++, j--) {
+ c = s[i];
+ s[i] = s[j];
+ s[j] = (char)c;
+ }
+}
+
+
+/*
+ * The following routine was removed from libc (libc/port/gen/hrtnewres.c).
+ * It has also been added to disadmin, so if you fix it here, you should
+ * also probably fix it there. In the long term, this should be recoded to
+ * not be hrt'ish.
+ */
+
+/*
+ * Convert interval expressed in htp->hrt_res to new_res.
+ *
+ * Calculate: (interval * new_res) / htp->hrt_res rounding off as
+ * specified by round.
+ *
+ * Note: All args are assumed to be positive. If
+ * the last divide results in something bigger than
+ * a long, then -1 is returned instead.
+ */
+
+int
+_hrtnewres(htp, new_res, round)
+register hrtimer_t *htp;
+register ulong_t new_res;
+long round;
+{
+ register long interval;
+ longlong_t dint;
+ longlong_t dto_res;
+ longlong_t drem;
+ longlong_t dfrom_res;
+ longlong_t prod;
+ longlong_t quot;
+ register long numerator;
+ register long result;
+ ulong_t modulus;
+ ulong_t twomodulus;
+ long temp;
+
+ if (new_res > NANOSEC || htp->hrt_rem < 0)
+ return (-1);
+
+ if (htp->hrt_rem >= htp->hrt_res) {
+ htp->hrt_secs += htp->hrt_rem / htp->hrt_res;
+ htp->hrt_rem = htp->hrt_rem % htp->hrt_res;
+ }
+
+ interval = htp->hrt_rem;
+ if (interval == 0) {
+ htp->hrt_res = new_res;
+ return (0);
+ }
+
+ /*
+ * Try to do the calculations in single precision first
+ * (for speed). If they overflow, use double precision.
+ * What we want to compute is:
+ *
+ * (interval * new_res) / hrt->hrt_res
+ */
+
+ numerator = interval * new_res;
+
+ if (numerator / new_res == interval) {
+
+ /*
+ * The above multiply didn't give overflow since
+ * the division got back the original number. Go
+ * ahead and compute the result.
+ */
+
+ result = numerator / htp->hrt_res;
+
+ /*
+ * For HRT_RND, compute the value of:
+ *
+ * (interval * new_res) % htp->hrt_res
+ *
+ * If it is greater than half of the htp->hrt_res,
+ * then rounding increases the result by 1.
+ *
+ * For HRT_RNDUP, we increase the result by 1 if:
+ *
+ * result * htp->hrt_res != numerator
+ *
+ * because this tells us we truncated when calculating
+ * result above.
+ *
+ * We also check for overflow when incrementing result
+ * although this is extremely rare.
+ */
+
+ if (round == HRT_RND) {
+ modulus = numerator - result * htp->hrt_res;
+ if ((twomodulus = 2 * modulus) / 2 == modulus) {
+
+ /*
+ * No overflow (if we overflow in calculation
+ * of twomodulus we fall through and use
+ * double precision).
+ */
+ if (twomodulus >= htp->hrt_res) {
+ temp = result + 1;
+ if (temp - 1 == result)
+ result++;
+ else
+ return (-1);
+ }
+ htp->hrt_res = new_res;
+ htp->hrt_rem = result;
+ return (0);
+ }
+ } else if (round == HRT_RNDUP) {
+ if (result * htp->hrt_res != numerator) {
+ temp = result + 1;
+ if (temp - 1 == result)
+ result++;
+ else
+ return (-1);
+ }
+ htp->hrt_res = new_res;
+ htp->hrt_rem = result;
+ return (0);
+ } else { /* round == HRT_TRUNC */
+ htp->hrt_res = new_res;
+ htp->hrt_rem = result;
+ return (0);
+ }
+ }
+
+ /*
+ * We would get overflow doing the calculation is
+ * single precision so do it the slow but careful way.
+ *
+ * Compute the interval times the resolution we are
+ * going to.
+ */
+
+ dint = interval;
+ dto_res = new_res;
+ prod = dint * dto_res;
+
+ /*
+ * For HRT_RND the result will be equal to:
+ *
+ * ((interval * new_res) + htp->hrt_res / 2) / htp->hrt_res
+ *
+ * and for HRT_RNDUP we use:
+ *
+ * ((interval * new_res) + htp->hrt_res - 1) / htp->hrt_res
+ *
+ * This is a different but equivalent way of rounding.
+ */
+
+ if (round == HRT_RND) {
+ drem = htp->hrt_res / 2;
+ prod = prod + drem;
+ } else if (round == HRT_RNDUP) {
+ drem = htp->hrt_res - 1;
+ prod = prod + drem;
+ }
+
+ dfrom_res = htp->hrt_res;
+ quot = prod / dfrom_res;
+
+ /*
+ * If the quotient won't fit in a long, then we have
+ * overflow. Otherwise, return the result.
+ */
+
+ if (quot > UINT_MAX) {
+ return (-1);
+ } else {
+ htp->hrt_res = new_res;
+ htp->hrt_rem = (int)quot;
+ return (0);
+ }
+}