diff options
| author | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
|---|---|---|
| committer | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
| commit | 7c478bd95313f5f23a4c958a745db2134aa03244 (patch) | |
| tree | c871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/cmd/priocntl | |
| download | illumos-joyent-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz | |
OpenSolaris Launch
Diffstat (limited to 'usr/src/cmd/priocntl')
| -rw-r--r-- | usr/src/cmd/priocntl/Makefile | 136 | ||||
| -rw-r--r-- | usr/src/cmd/priocntl/fsspriocntl.c | 461 | ||||
| -rw-r--r-- | usr/src/cmd/priocntl/fxpriocntl.c | 550 | ||||
| -rw-r--r-- | usr/src/cmd/priocntl/iapriocntl.c | 509 | ||||
| -rw-r--r-- | usr/src/cmd/priocntl/priocntl.c | 1108 | ||||
| -rw-r--r-- | usr/src/cmd/priocntl/priocntl.h | 100 | ||||
| -rw-r--r-- | usr/src/cmd/priocntl/rtpriocntl.c | 524 | ||||
| -rw-r--r-- | usr/src/cmd/priocntl/subr.c | 729 | ||||
| -rw-r--r-- | usr/src/cmd/priocntl/tspriocntl.c | 477 |
9 files changed, 4594 insertions, 0 deletions
diff --git a/usr/src/cmd/priocntl/Makefile b/usr/src/cmd/priocntl/Makefile new file mode 100644 index 0000000000..a947c89073 --- /dev/null +++ b/usr/src/cmd/priocntl/Makefile @@ -0,0 +1,136 @@ +# +# 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. +# +# ident "%Z%%M% %I% %E% SMI" +# + +PROG= priocntl + +PRIOCNTLOBJ= $(PROG).o subr.o + +PRIOCNTLSRC= $(PRIOCNTLOBJ:%.o=%.c) +OBJS= $(PRIOCNTLOBJ) rt$(PROG).o ts$(PROG).o ia$(PROG).o fss$(PROG).o \ + fx$(PROG).o +SRCS= $(OBJ:%.o=%.c) + +include ../Makefile.cmd + +LDLIBS += -lcontract + +CLASSD = $(ROOTLIB)/class +RTD = $(CLASSD)/RT +TSD = $(CLASSD)/TS +IAD = $(CLASSD)/IA +FSSD = $(CLASSD)/FSS +FXD = $(CLASSD)/FX +DIRS = $(CLASSD) $(RTD) $(TSD) $(IAD) $(FSSD) $(FXD) + +RTPROG = RT$(PROG) +TSPROG = TS$(PROG) +IAPROG = IA$(PROG) +FSSPROG = FSS$(PROG) +FXPROG = FX$(PROG) + +ROOTRTPROG = $(RTD)/$(RTPROG) +ROOTTSPROG = $(TSD)/$(TSPROG) +ROOTIAPROG = $(IAD)/$(IAPROG) +ROOTFSSPROG = $(FSSD)/$(FSSPROG) +ROOTFXPROG = $(FXD)/$(FXPROG) + +$(ROOTUSRSBINPROG) := FILEMODE = 04555 +$(ROOTUSRSBINPROG) := OWNER= root +$(ROOTUSRSBINPROG) := GROUP= root +$(DIRS) := FILEMODE = 0775 +$(DIRS) := OWNER = root +$(DIRS) := GROUP = bin +CLOBBERFILES += $(RTPROG) $(TSPROG) $(IAPROG) $(FSSPROG) $(FXPROG) + +# installation rules +$(RTD)/% : % + $(INS.file) + +$(TSD)/% : % + $(INS.file) + +$(IAD)/% : % + $(INS.file) + +$(FSSD)/% : % + $(INS.file) + +$(FXD)/% : % + $(INS.file) + +.KEEP_STATE: + +all: $(PROG) $(RTPROG) $(TSPROG) $(IAPROG) $(FSSPROG) $(FXPROG) + +$(PROG): $(PRIOCNTLOBJ) + $(LINK.c) $(PRIOCNTLOBJ) -o $@ $(LDLIBS) + $(POST_PROCESS) + +$(RTPROG): rt$(PRIOCNTLOBJ) + $(LINK.c) rt$(PRIOCNTLOBJ) -o $@ $(LDLIBS) + $(POST_PROCESS) + +$(TSPROG): ts$(PRIOCNTLOBJ) + $(LINK.c) ts$(PRIOCNTLOBJ) -o $@ $(LDLIBS) + $(POST_PROCESS) + +$(IAPROG): ia$(PRIOCNTLOBJ) + $(LINK.c) ia$(PRIOCNTLOBJ) -o $@ $(LDLIBS) + $(POST_PROCESS) + +$(FSSPROG): fss$(PRIOCNTLOBJ) + $(LINK.c) fss$(PRIOCNTLOBJ) -o $@ $(LDLIBS) + $(POST_PROCESS) + +$(FXPROG): fx$(PRIOCNTLOBJ) + $(LINK.c) fx$(PRIOCNTLOBJ) -o $@ $(LDLIBS) + $(POST_PROCESS) + +install: all $(DIRS) \ + $(ROOTPROG) \ + $(ROOTRTPROG) \ + $(ROOTTSPROG) \ + $(ROOTIAPROG) \ + $(ROOTFSSPROG) \ + $(ROOTFXPROG) + +$(DIRS): + $(INS.dir) + +clean: + $(RM) $(OBJS) + +lint: + $(LINT.c) $(PRIOCNTLSRC) $(LDLIBS) + $(LINT.c) rt$(PRIOCNTLSRC) $(LDLIBS) + $(LINT.c) ts$(PRIOCNTLSRC) $(LDLIBS) + $(LINT.c) ia$(PRIOCNTLSRC) $(LDLIBS) + $(LINT.c) fss$(PRIOCNTLSRC) $(LDLIBS) + $(LINT.c) fx$(PRIOCNTLSRC) $(LDLIBS) + +include ../Makefile.targ diff --git a/usr/src/cmd/priocntl/fsspriocntl.c b/usr/src/cmd/priocntl/fsspriocntl.c new file mode 100644 index 0000000000..d37c165592 --- /dev/null +++ b/usr/src/cmd/priocntl/fsspriocntl.c @@ -0,0 +1,461 @@ +/* + * 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. + */ + +/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ +/* All Rights Reserved */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/types.h> +#include <sys/procset.h> +#include <sys/priocntl.h> +#include <sys/fsspriocntl.h> +#include <string.h> +#include <stdio.h> +#include <unistd.h> +#include <libgen.h> +#include <limits.h> +#include <errno.h> +#include "priocntl.h" + +/* + * This file contains the class specific code implementing the fair-share + * scheduler priocntl sub-command. + */ +#define ADDKEYVAL(p, k, v) { (p[0]) = (k); (p[1]) = (v); p += 2; } +#define FSS_KEYCNT 2 /* max number of (key, value) pairs */ + +/* + * Control flags + */ +#define FSS_DOUPRILIM 0x01 /* user priority limit */ +#define FSS_DOUPRI 0x02 /* user priority */ + +static void print_fssinfo(void); +static int print_fssprocs(void); +static int fss_priocntl(idtype_t, id_t, int, char *, uintptr_t *); +static int set_fssprocs(idtype_t, int, char **, uint_t, pri_t, pri_t); +static void exec_fsscmd(char **, uint_t, pri_t, pri_t); + +static char usage[] = +"usage: priocntl -l\n" +" priocntl -d [-d idtype] [idlist]\n" +" priocntl -s [-c FSS] [-m fssuprilim] [-p fssupri] [-i idtype] " +"[idlist]\n" +" priocntl -e [-c FSS] [-m fssuprilim] [-p fssupri] command [argument(s)]" +"\n"; + +static char cmdpath[MAXPATHLEN]; +static char basenm[BASENMSZ]; + + +int +main(int argc, char *argv[]) +{ + int c; + int lflag, dflag, sflag, mflag, pflag, eflag, iflag; + pri_t fssuprilim; + pri_t fssupri; + char *idtypnm; + idtype_t idtype; + int idargc; + uint_t cflags; + + (void) strlcpy(cmdpath, argv[0], MAXPATHLEN); + (void) strlcpy(basenm, basename(argv[0]), BASENMSZ); + lflag = dflag = sflag = mflag = pflag = eflag = iflag = 0; + while ((c = getopt(argc, argv, "ldsm:p:ec:i:")) != -1) { + switch (c) { + case 'l': + lflag++; + break; + case 'd': + dflag++; + break; + case 's': + sflag++; + break; + case 'm': + mflag++; + fssuprilim = (pri_t)str2num(optarg, SHRT_MIN, SHRT_MAX); + if (errno) + fatalerr("%s: Specified user priority limit %s " + "out of configured range\n", + basenm, optarg); + break; + case 'p': + pflag++; + fssupri = (pri_t)str2num(optarg, SHRT_MIN, SHRT_MAX); + if (errno) + fatalerr("%s: Specified user priority %s " + "out of configured range\n", + basenm, optarg); + break; + case 'e': + eflag++; + break; + case 'c': + if (strcmp(optarg, "FSS") != 0) + fatalerr("error: %s executed for %s class, %s " + "is actually sub-command for FSS class\n", + cmdpath, optarg, cmdpath); + break; + case 'i': + iflag++; + idtypnm = optarg; + break; + case '?': + fatalerr(usage); + default: + break; + } + } + + if (lflag) { + if (dflag || sflag || mflag || pflag || eflag || iflag) + fatalerr(usage); + + print_fssinfo(); + + } else if (dflag) { + if (lflag || sflag || mflag || pflag || eflag) + fatalerr(usage); + + return (print_fssprocs()); + + } else if (sflag) { + if (lflag || dflag || eflag) + fatalerr(usage); + + if (iflag) { + if (str2idtyp(idtypnm, &idtype) == -1) + fatalerr("%s: Bad idtype %s\n", basenm, + idtypnm); + } else { + idtype = P_PID; + } + + cflags = (pflag ? FSS_DOUPRI : 0); + + if (mflag) + cflags |= FSS_DOUPRILIM; + + if (optind < argc) + idargc = argc - optind; + else + idargc = 0; + + return (set_fssprocs(idtype, idargc, &argv[optind], cflags, + fssuprilim, fssupri)); + + } else if (eflag) { + if (lflag || dflag || sflag || iflag) + fatalerr(usage); + + cflags = (pflag ? FSS_DOUPRI : 0); + + if (mflag) + cflags |= FSS_DOUPRILIM; + + exec_fsscmd(&argv[optind], cflags, fssuprilim, fssupri); + + } else { + fatalerr(usage); + } + + return (0); +} + +/* + * Print our class name and the configured user priority range. + */ +static void +print_fssinfo(void) +{ + pcinfo_t pcinfo; + + (void) strcpy(pcinfo.pc_clname, "FSS"); + + (void) printf("FSS (Fair Share)\n"); + + if (priocntl(0, 0, PC_GETCID, (caddr_t)&pcinfo) == -1) + fatalerr("\tCan't get configured FSS user priority range\n"); + + (void) printf("\tConfigured FSS User Priority Range: -%d through %d\n", + ((fssinfo_t *)pcinfo.pc_clinfo)->fss_maxupri, + ((fssinfo_t *)pcinfo.pc_clinfo)->fss_maxupri); +} + +/* + * Read a list of pids from stdin and print the user priority and user + * priority limit for each of the corresponding processes. + */ +static int +print_fssprocs(void) +{ + pid_t *pidlist; + size_t numread; + int i; + char clname[PC_CLNMSZ]; + pri_t fssuprilim; + pri_t fssupri; + int error = 0; + + /* + * Read a list of pids from stdin. + */ + if ((pidlist = read_pidlist(&numread, stdin)) == NULL) + fatalerr("%s: Can't read pidlist.\n", basenm); + + (void) printf("FAIR SHARING PROCESSES:\n" + " PID FSSUPRILIM FSSUPRI\n"); + + if (numread == 0) + fatalerr("%s: No pids on input\n", basenm); + + for (i = 0; i < numread; i++) { + (void) printf("%7ld", pidlist[i]); + if (priocntl(P_PID, pidlist[i], PC_GETXPARMS, "FSS", + FSS_KY_UPRI, &fssupri, + FSS_KY_UPRILIM, &fssuprilim, 0) != -1) { + (void) printf(" %5d %5d\n", + fssuprilim, fssupri); + } else { + error = 1; + + if (priocntl(P_PID, pidlist[i], PC_GETXPARMS, NULL, + PC_KY_CLNAME, clname, 0) == -1 && + strcmp(clname, "FSS")) + /* + * Process from some class other than fair + * sharing. It has probably changed class while + * priocntl command was executing (otherwise + * we wouldn't have been passed its pid). + * Print the little we know about it. + */ + (void) printf("\tChanged to class %s while" + " priocntl command executing\n", clname); + else + (void) printf("\tCan't get FSS user priority" + "\n"); + } + } + + free_pidlist(pidlist); + return (error); +} + +/* + * Call priocntl() with command codes PC_SETXPARMS or PC_GETXPARMS. The + * first parameter behind the command code is always the class name. + * Each parameter is headed by a key, which detemines the meanin of the + * following value. There is maximum FSS_KEYCNT == 2 of (key, value) pairs. + */ +static int +fss_priocntl(idtype_t idtype, id_t id, int cmd, char *clname, uintptr_t *argsp) +{ + return (priocntl(idtype, id, cmd, clname, argsp[0], argsp[1], + argsp[2], argsp[3], 0)); +} + +/* + * Set all processes in the set specified by idtype/idargv to fair-sharing + * (if they aren't already fair-sharing) and set their user priority limit + * and user priority to those specified by fssuprilim and fssupri. + */ +static int +set_fssprocs(idtype_t idtype, int idargc, char **idargv, uint_t cflags, + short fssuprilim, short fssupri) +{ + pcinfo_t pcinfo; + uintptr_t args[2 * FSS_KEYCNT + 1]; + uintptr_t *argsp = &args[0]; + pri_t maxupri; + char idtypnm[PC_IDTYPNMSZ]; + int i; + int error = 0; + id_t id; + + /* + * Get the fair sharing class ID and max configured user priority. + */ + (void) strcpy(pcinfo.pc_clname, "FSS"); + if (priocntl(0, 0, PC_GETCID, (caddr_t)&pcinfo) == -1) + fatalerr("%s: Can't get FSS class ID, priocntl system call " + "failed (%s)\n", basenm, strerror(errno)); + maxupri = ((fssinfo_t *)pcinfo.pc_clinfo)->fss_maxupri; + + /* + * Validate the fssuprilim and fssupri arguments. + */ + if ((cflags & FSS_DOUPRILIM) != 0) { + if (fssuprilim > maxupri || fssuprilim < -maxupri) + fatalerr("%s: Specified user priority limit %d out of " + "configured range\n", basenm, fssuprilim); + ADDKEYVAL(argsp, FSS_KY_UPRILIM, fssuprilim); + } + + if ((cflags & FSS_DOUPRI) != 0) { + if (fssupri > maxupri || fssupri < -maxupri) + fatalerr("%s: Specified user priority %d out of " + "configured range\n", basenm, fssupri); + ADDKEYVAL(argsp, FSS_KY_UPRI, fssupri); + } + *argsp = 0; + + if (idtype == P_ALL) { + if (fss_priocntl(P_ALL, 0, PC_SETXPARMS, "FSS", args) == -1) { + if (errno == EPERM) { + (void) fprintf(stderr, "Permissions error " + "encountered on one or more processes.\n"); + error = 1; + } else { + fatalerr("%s: Can't reset fair sharing " + "parameters\npriocntl system call failed " + "(%s)\n", basenm, strerror(errno)); + } + } else if ((cflags & (FSS_DOUPRILIM|FSS_DOUPRI)) == + FSS_DOUPRI) { + (void) verifyupri(idtype, 0, "FSS", FSS_KY_UPRILIM, + fssupri, basenm); + } + } else if (idargc == 0) { + if (fss_priocntl(idtype, P_MYID, PC_SETXPARMS, "FSS", + args) == -1) { + if (errno == EPERM) { + (void) idtyp2str(idtype, idtypnm); + (void) fprintf(stderr, "Permissions error " + "encountered on current %s.\n", idtypnm); + error = 1; + } else { + fatalerr("%s: Can't reset fair sharing " + "parameters\npriocntl system call failed " + "(%s)\n", basenm, strerror(errno)); + } + } else if ((cflags & (FSS_DOUPRILIM|FSS_DOUPRI)) == + FSS_DOUPRI && getmyid(idtype, &id) != -1) { + (void) verifyupri(idtype, id, "FSS", FSS_KY_UPRILIM, + fssupri, basenm); + } + } else { + (void) idtyp2str(idtype, idtypnm); + for (i = 0; i < idargc; i++) { + if (idtype == P_CID) { + (void) strcpy(pcinfo.pc_clname, idargv[i]); + if (priocntl(0, 0, PC_GETCID, + (caddr_t)&pcinfo) == -1) + fatalerr("%s: Invalid or unconfigured " + "class %s, priocntl system call " + "failed (%s)\n", + basenm, pcinfo.pc_clname, + strerror(errno)); + id = pcinfo.pc_cid; + } else { + id = (id_t)str2num(idargv[i], INT_MIN, INT_MAX); + if (errno) + fatalerr("%s: Invalid id \"%s\"\n", + basenm, idargv[i]); + } + + if (fss_priocntl(idtype, id, PC_SETXPARMS, "FSS", + args) == -1) { + if (errno == EPERM) { + (void) fprintf(stderr, "Permissions " + "error encountered on %s %s.\n", + idtypnm, idargv[i]); + error = 1; + } else { + fatalerr("%s: Can't reset fair sharing" + " parameters\npriocntl system call" + " failed (%s)\n", + basenm, strerror(errno)); + } + } else if ((cflags & (FSS_DOUPRILIM|FSS_DOUPRI)) == + FSS_DOUPRI) { + (void) verifyupri(idtype, id, "FSS", + FSS_KY_UPRILIM, fssupri, basenm); + } + } + } + + return (error); +} + +/* + * Execute the command pointed to by cmdargv as a fair-sharing process + * with the user priority limit given by fssuprilim and user priority fssupri. + */ +static void +exec_fsscmd(char **cmdargv, uint_t cflags, pri_t fssuprilim, pri_t fssupri) +{ + pcinfo_t pcinfo; + uintptr_t args[2 * FSS_KEYCNT + 1]; + uintptr_t *argsp = &args[0]; + pri_t maxupri; + pri_t uprilim; + + /* + * Get the fair sharing class ID and max configured user priority. + */ + (void) strcpy(pcinfo.pc_clname, "FSS"); + if (priocntl(0, 0, PC_GETCID, (caddr_t)&pcinfo) == -1) + fatalerr("%s: Can't get FSS class ID, priocntl system call " + "failed (%s)\n", basenm, strerror(errno)); + maxupri = ((fssinfo_t *)pcinfo.pc_clinfo)->fss_maxupri; + + if ((cflags & FSS_DOUPRILIM) != 0) { + if (fssuprilim > maxupri || fssuprilim < -maxupri) + fatalerr("%s: Specified user priority limit %d out of " + "configured range\n", basenm, fssuprilim); + ADDKEYVAL(argsp, FSS_KY_UPRILIM, fssuprilim); + } + + if ((cflags & FSS_DOUPRI) != 0) { + if (fssupri > maxupri || fssupri < -maxupri) + fatalerr("%s: Specified user priority %d out of " + "configured range\n", basenm, fssupri); + ADDKEYVAL(argsp, FSS_KY_UPRI, fssupri); + } + *argsp = 0; + + if (fss_priocntl(P_PID, P_MYID, PC_SETXPARMS, "FSS", args) == -1) + fatalerr("%s: Can't reset fair sharing parameters\n" + "priocntl system call failed (%s)\n", + basenm, strerror(errno)); + + if ((cflags & (FSS_DOUPRILIM|FSS_DOUPRI)) == FSS_DOUPRI) { + if (priocntl(P_PID, P_MYID, PC_GETXPARMS, "FSS", + FSS_KY_UPRILIM, &uprilim, 0) != -1 && fssupri > uprilim) + (void) fprintf(stderr, + "%s: Specified user priority %d exceeds" + " limit %d; set to %d (pid %d)\n", + basenm, fssupri, uprilim, uprilim, (int)getpid()); + } + + (void) execvp(cmdargv[0], cmdargv); + fatalerr("%s: Can't execute %s, exec failed (%s)\n", + basenm, cmdargv[0], strerror(errno)); +} diff --git a/usr/src/cmd/priocntl/fxpriocntl.c b/usr/src/cmd/priocntl/fxpriocntl.c new file mode 100644 index 0000000000..118762b3f7 --- /dev/null +++ b/usr/src/cmd/priocntl/fxpriocntl.c @@ -0,0 +1,550 @@ +/* + * 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 2001-2002 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ +/* All Rights Reserved */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdio.h> +#include <string.h> +#include <libgen.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/procset.h> +#include <sys/priocntl.h> +#include <sys/fxpriocntl.h> +#include <limits.h> +#include <errno.h> + +#include "priocntl.h" + +/* + * This file contains the class specific code implementing + * the fixed-priority priocntl sub-command. + */ + +#define ADDKEYVAL(p, k, v) { (p[0]) = (k); (p[1]) = (v); p += 2; } + +#define FX_KEYCNT 4 /* maximal number of (key, value) pairs */ + +/* + * control flags + */ +#define FX_DOUPRILIM 0x01 /* user priority limit */ +#define FX_DOUPRI 0x02 /* user priority */ +#define FX_DOTQ 0x04 /* time quantum */ + + + +static void print_fxinfo(); +static int print_fxprocs(); +static int set_fxprocs(idtype_t, int, char **, uint_t, pri_t, pri_t, long, + long); +static void exec_fxcmd(char **, uint_t, pri_t, pri_t, long, long); +static int fx_priocntl(idtype_t, id_t, int, char *, uintptr_t *); + +static char usage[] = +"usage: priocntl -l\n\ + priocntl -d [-d idtype] [idlist]\n\ + priocntl -s [-c FX] [-m fxuprilim] [-p fxupri] [-t tqntm [-r res]] \ +[-i idtype] [idlist]\n\ + priocntl -e [-c FX] [-m fxuprilim] [-p fxupri] [-t tqntm [-r res]] \ +command [argument(s)]\n"; + +static char cmdpath[MAXPATHLEN]; +static char basenm[BASENMSZ]; + +int +main(int argc, char *argv[]) +{ + extern char *optarg; + extern int optind; + + int c; + int lflag, dflag, sflag, mflag, pflag, eflag, iflag, tflag; + int rflag; + pri_t fxuprilim; + pri_t fxupri; + char *idtypnm; + idtype_t idtype; + int idargc; + long res; + long tqntm; + uint_t cflags; + + (void) strlcpy(cmdpath, argv[0], MAXPATHLEN); + (void) strlcpy(basenm, basename(argv[0]), BASENMSZ); + lflag = dflag = sflag = mflag = pflag = eflag = iflag = tflag = 0; + rflag = 0; + while ((c = getopt(argc, argv, "ldsm:p:ec:i:t:r:")) != -1) { + switch (c) { + + case 'l': + lflag++; + break; + + case 'd': + dflag++; + break; + + case 's': + sflag++; + break; + + case 'm': + mflag++; + fxuprilim = (pri_t)str2num(optarg, SHRT_MIN, SHRT_MAX); + if (errno) + fatalerr("%s: Specified user priority limit %s" + " out of configured range\n", + basenm, optarg); + break; + + case 'p': + pflag++; + fxupri = (pri_t)str2num(optarg, SHRT_MIN, SHRT_MAX); + if (errno) + fatalerr("%s: Specified user priority %s out of" + " configured range\n", basenm, optarg); + break; + + case 'e': + eflag++; + break; + + case 'c': + if (strcmp(optarg, "FX") != 0) + fatalerr("error: %s executed for %s class, %s" + " is actually sub-command for FX class\n", + cmdpath, optarg, cmdpath); + break; + + case 'i': + iflag++; + idtypnm = optarg; + break; + case 't': + tflag++; + tqntm = str2num(optarg, 1, INT_MAX); + if (errno) + fatalerr("%s: Invalid time quantum specified;" + " time quantum must be positive\n", basenm); + break; + + case 'r': + rflag++; + res = str2num(optarg, 1, 1000000000); + if (errno) + fatalerr("%s: Invalid resolution specified;" + " resolution must be between" + " 1 and 1,000,000,000\n", basenm); + + break; + + case '?': + fatalerr(usage); + + default: + break; + } + } + + if (lflag) { + if (dflag || sflag || mflag || pflag || eflag || iflag || + tflag || rflag) + fatalerr(usage); + + print_fxinfo(); + + } else if (dflag) { + if (sflag || mflag || pflag || eflag) + fatalerr(usage); + + return (print_fxprocs()); + + } else if (sflag) { + if (eflag) + fatalerr(usage); + + if (iflag) { + if (str2idtyp(idtypnm, &idtype) == -1) + fatalerr("%s: Bad idtype %s\n", basenm, + idtypnm); + } else + idtype = P_PID; + + cflags = (pflag ? FX_DOUPRI : 0); + + if (mflag) + cflags |= FX_DOUPRILIM; + + if (tflag) + cflags |= FX_DOTQ; + + if (rflag == 0) + res = 1000; + + if (optind < argc) + idargc = argc - optind; + else + idargc = 0; + + return (set_fxprocs(idtype, idargc, &argv[optind], cflags, + fxuprilim, fxupri, tqntm, res)); + + } else if (eflag) { + if (iflag) + fatalerr(usage); + + cflags = (pflag ? FX_DOUPRI : 0); + + if (mflag) + cflags |= FX_DOUPRILIM; + + if (tflag) + cflags |= FX_DOTQ; + + if (rflag == 0) + res = 1000; + + exec_fxcmd(&argv[optind], cflags, fxuprilim, fxupri, tqntm, + res); + + } else { + fatalerr(usage); + } + return (0); +} + + +/* + * Print our class name and the configured user priority range. + */ +static void +print_fxinfo() +{ + pcinfo_t pcinfo; + + (void) strcpy(pcinfo.pc_clname, "FX"); + + (void) printf("FX (Fixed priority)\n"); + + if (priocntl(0, 0, PC_GETCID, (caddr_t)&pcinfo) == -1) + fatalerr("\tCan't get configured FX user priority range\n"); + + (void) printf("\tConfigured FX User Priority Range: 0 through %d\n", + ((fxinfo_t *)pcinfo.pc_clinfo)->fx_maxupri); +} + + +/* + * Read a list of pids from stdin and print the user priority and user + * priority limit for each of the corresponding processes. + */ +static int +print_fxprocs() +{ + pid_t *pidlist; + size_t numread; + int i; + char clname[PC_CLNMSZ]; + uint_t fx_tqsecs; + int fx_tqnsecs; + pri_t fx_uprilim; + pri_t fx_upri; + int error = 0; + + + /* + * Read a list of pids from stdin. + */ + if ((pidlist = read_pidlist(&numread, stdin)) == NULL) + fatalerr("%s: Can't read pidlist.\n", basenm); + + (void) printf("FIXED PRIORITY PROCESSES:\n PID FXUPRILIM " + "FXUPRI FXTQNTM\n"); + + if (numread == 0) + fatalerr("%s: No pids on input\n", basenm); + + + for (i = 0; i < numread; i++) { + (void) printf("%7ld", pidlist[i]); + if (priocntl(P_PID, pidlist[i], PC_GETXPARMS, "FX", + FX_KY_UPRI, &fx_upri, FX_KY_UPRILIM, &fx_uprilim, + FX_KY_TQSECS, &fx_tqsecs, FX_KY_TQNSECS, + &fx_tqnsecs, 0) != -1) { + (void) printf(" %5d %5d", fx_uprilim, fx_upri); + + if (fx_tqnsecs == FX_TQINF) + (void) printf(" FX_TQINF\n"); + else + (void) printf(" %11lld\n", + (longlong_t)fx_tqsecs * 1000 + + fx_tqnsecs / 1000000); + + } else { + error = 1; + + if (priocntl(P_PID, pidlist[i], PC_GETXPARMS, NULL, + PC_KY_CLNAME, clname, 0) != -1 && + strcmp(clname, "FX")) { + /* + * Process from some class other than fixed priority. + * It has probably changed class while priocntl + * command was executing (otherwise we wouldn't + * have been passed its pid). Print the little + * we know about it. + */ + (void) printf("\tChanged to class %s while priocntl" + " command executing\n", clname); + } else { + (void) printf("\tCan't get FX user priority\n"); + } + } + } + + free_pidlist(pidlist); + return (error); +} + +/* + * Call priocntl() with command codes PC_SETXPARMS or PC_GETXPARMS. + * The first parameter behind the command code is always the class name. + * Each parameter is headed by a key, which determines the meaning of the + * following value. There are maximal FX_KEYCNT = 4 (key, value) pairs. + */ +static int +fx_priocntl(idtype_t idtype, id_t id, int cmd, char *clname, uintptr_t *argsp) +{ + return (priocntl(idtype, id, cmd, clname, argsp[0], argsp[1], + argsp[2], argsp[3], argsp[4], argsp[5], argsp[6], argsp[7], 0)); +} + +/* + * Set all processes in the set specified by idtype/idargv to fixed-priority + * (if they aren't already fixed-priority) and set their user priority limit + * and user priority to those specified by fxuprilim and fxupri. + */ +static int +set_fxprocs(idtype_t idtype, int idargc, char **idargv, uint_t cflags, + pri_t fxuprilim, pri_t fxupri, long tqntm, long res) +{ + pcinfo_t pcinfo; + uintptr_t args[2*FX_KEYCNT+1]; + uintptr_t *argsp = &args[0]; + pri_t maxupri; + char idtypnm[PC_IDTYPNMSZ]; + int i; + id_t id; + hrtimer_t hrtime; + int error = 0; + + /* + * Get the fixed priority class ID and max configured user priority. + */ + (void) strcpy(pcinfo.pc_clname, "FX"); + if (priocntl(0, 0, PC_GETCID, (caddr_t)&pcinfo) == -1) + fatalerr("%s: Can't get FX class ID, priocntl system call" + " failed with errno %d\n", basenm, errno); + maxupri = ((fxinfo_t *)pcinfo.pc_clinfo)->fx_maxupri; + + /* + * Validate the fxuprilim and fxupri arguments. + */ + if ((cflags & FX_DOUPRILIM) != 0) { + if (fxuprilim > maxupri || fxuprilim < 0) { + fatalerr("%s: Specified user priority limit %d out of" + " configured range\n", basenm, fxuprilim); + } + ADDKEYVAL(argsp, FX_KY_UPRILIM, fxuprilim); + } + + if ((cflags & FX_DOUPRI) != 0) { + if (fxupri > maxupri || fxupri < 0) + fatalerr("%s: Specified user priority %d out of " + "configured range\n", basenm, fxupri); + ADDKEYVAL(argsp, FX_KY_UPRI, fxupri); + + } + + if (cflags & FX_DOTQ) { + hrtime.hrt_secs = 0; + hrtime.hrt_rem = tqntm; + hrtime.hrt_res = res; + if (_hrtnewres(&hrtime, NANOSEC, HRT_RNDUP) == -1) + fatalerr("%s: Can't convert resolution.\n", basenm); + ADDKEYVAL(argsp, FX_KY_TQSECS, hrtime.hrt_secs); + ADDKEYVAL(argsp, FX_KY_TQNSECS, hrtime.hrt_rem); + } + + *argsp = 0; + + if (idtype == P_ALL) { + if (fx_priocntl(P_ALL, 0, PC_SETXPARMS, "FX", args) == -1) { + if (errno == EPERM) { + (void) fprintf(stderr, + "Permissions error encountered " + "on one or more processes.\n"); + error = 1; + } else { + fatalerr("%s: Can't reset fixed priority" + " parameters\npriocntl system call failed " + " with errno %d\n", basenm, errno); + } + } else if ((cflags & (FX_DOUPRILIM|FX_DOUPRI)) == FX_DOUPRI) { + (void) verifyupri(idtype, 0, "FX", FX_KY_UPRILIM, + fxupri, basenm); + } + } else if (idargc == 0) { + if (fx_priocntl(idtype, P_MYID, PC_SETXPARMS, "FX", + args) == -1) { + if (errno == EPERM) { + (void) idtyp2str(idtype, idtypnm); + (void) fprintf(stderr, "Permissions error" + " encountered on current %s.\n", idtypnm); + error = 1; + } else { + fatalerr("%s: Can't reset fixed priority" + " parameters\npriocntl system call failed" + " with errno %d\n", basenm, errno); + } + } else if ((cflags & (FX_DOUPRILIM|FX_DOUPRI)) == FX_DOUPRI && + getmyid(idtype, &id) != -1) { + (void) verifyupri(idtype, id, "FX", FX_KY_UPRILIM, + fxupri, basenm); + } + } else { + (void) idtyp2str(idtype, idtypnm); + for (i = 0; i < idargc; i++) { + if (idtype == P_CID) { + (void) strcpy(pcinfo.pc_clname, idargv[i]); + if (priocntl(0, 0, PC_GETCID, + (caddr_t)&pcinfo) == -1) + fatalerr("%s: Invalid or unconfigured" + " class %s, priocntl system call" + " failed with errno %d\n", + basenm, pcinfo.pc_clname, errno); + id = pcinfo.pc_cid; + } else { + id = (id_t)str2num(idargv[i], INT_MIN, INT_MAX); + } + + if (fx_priocntl(idtype, id, PC_SETXPARMS, "FX", args) + == -1) { + if (errno == EPERM) { + (void) fprintf(stderr, + "Permissions error encountered on" + " %s %s.\n", idtypnm, idargv[i]); + error = 1; + } else { + fatalerr("%s: Can't reset fixed " + "priority" + " parameters\npriocntl system call" + " failed with errno %d\n", + basenm, errno); + } + } else if ((cflags & (FX_DOUPRILIM|FX_DOUPRI)) == + FX_DOUPRI) { + (void) verifyupri(idtype, id, "FX", + FX_KY_UPRILIM, fxupri, basenm); + } + } + } + return (error); + +} + + +/* + * Execute the command pointed to by cmdargv as a fixed-priority process + * with the user priority limit given by fxuprilim and user priority fxupri. + */ +static void +exec_fxcmd(char **cmdargv, uint_t cflags, pri_t fxuprilim, pri_t fxupri, + long tqntm, long res) +{ + pcinfo_t pcinfo; + uintptr_t args[2*FX_KEYCNT+1]; + uintptr_t *argsp = &args[0]; + + pri_t maxupri; + pri_t uprilim; + hrtimer_t hrtime; + + /* + * Get the fixed priority class ID and max configured user priority. + */ + (void) strcpy(pcinfo.pc_clname, "FX"); + if (priocntl(0, 0, PC_GETCID, (caddr_t)&pcinfo) == -1) + fatalerr("%s: Can't get FX class ID, priocntl system call" + " failed with errno %d\n", basenm, errno); + maxupri = ((fxinfo_t *)pcinfo.pc_clinfo)->fx_maxupri; + + if ((cflags & FX_DOUPRILIM) != 0) { + if (fxuprilim > maxupri || fxuprilim < 0) + fatalerr("%s: Specified user priority limit %d out of" + " configured range\n", basenm, fxuprilim); + ADDKEYVAL(argsp, FX_KY_UPRILIM, fxuprilim); + } + + if ((cflags & FX_DOUPRI) != 0) { + if (fxupri > maxupri || fxupri < 0) + fatalerr("%s: Specified user priority %d out of" + " configured range\n", basenm, fxupri); + ADDKEYVAL(argsp, FX_KY_UPRI, fxupri); + } + + if ((cflags & FX_DOTQ) != 0) { + hrtime.hrt_secs = 0; + hrtime.hrt_rem = tqntm; + hrtime.hrt_res = res; + if (_hrtnewres(&hrtime, NANOSEC, HRT_RNDUP) == -1) + fatalerr("%s: Can't convert resolution.\n", basenm); + ADDKEYVAL(argsp, FX_KY_TQSECS, hrtime.hrt_secs); + ADDKEYVAL(argsp, FX_KY_TQNSECS, hrtime.hrt_rem); + + } + *argsp = 0; + if (fx_priocntl(P_PID, P_MYID, PC_SETXPARMS, "FX", args) == -1) + fatalerr("%s: Can't reset fixed priority parameters\n" + " priocntl system call failed with errno %d\n", + basenm, errno); + + if ((cflags & (FX_DOUPRILIM|FX_DOUPRI)) == FX_DOUPRI) { + if (priocntl(P_PID, P_MYID, PC_GETXPARMS, "FX", + FX_KY_UPRILIM, &uprilim, 0) != -1 && fxupri > uprilim) + (void) fprintf(stderr, + "%s: Specified user priority %d exceeds" + " limit %d; set to %d (pid %d)\n", + basenm, fxupri, uprilim, uprilim, (int)getpid()); + } + + (void) execvp(cmdargv[0], cmdargv); + fatalerr("%s: Can't execute %s, exec failed with errno %d\n", + basenm, cmdargv[0], errno); +} diff --git a/usr/src/cmd/priocntl/iapriocntl.c b/usr/src/cmd/priocntl/iapriocntl.c new file mode 100644 index 0000000000..d353415982 --- /dev/null +++ b/usr/src/cmd/priocntl/iapriocntl.c @@ -0,0 +1,509 @@ +/* + * 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 <stdlib.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/procset.h> +#include <sys/priocntl.h> +#include <sys/iapriocntl.h> +#include <libgen.h> +#include <limits.h> +#include <errno.h> + +#include "priocntl.h" + +/* + * This file contains the class specific code implementing + * the interactive class priocntl sub-command. + */ + +#define ADDKEYVAL(p, k, v) { (p[0]) = (k); (p[1]) = (v); p += 2; } + +#define IA_KEYCNT 3 /* maximal number of (key, value) pairs */ + +/* + * control flags + */ +#define IA_DOUPRILIM 0x01 /* user priority limit */ +#define IA_DOUPRI 0x02 /* user priority */ +#define IA_DOMODE 0x04 /* interactive on/off */ + +static void print_iainfo(void); +static int print_iaprocs(void); +static int ia_priocntl(idtype_t, id_t, int, char *, uintptr_t *); +static int set_iaprocs(idtype_t, int, char **, uint_t, pri_t, pri_t, int); +static void exec_iacmd(char **, uint_t, pri_t, pri_t, int); + +static char usage[] = +"usage: priocntl -l\n\ + priocntl -d [-i idtype] [idlist]\n\ + priocntl -s [-c IA] [-m iauprilim] [-p iaupri] [-t iamode]\n\ + [-i idtype] [idlist]\n\ + priocntl -e [-c IA] [-m iauprilim] [-p iaupri] [-t iamode]\n\ + command [argument(s)]\n"; + +static char cmdpath[MAXPATHLEN]; +static char basenm[BASENMSZ]; + + +int +main(int argc, char *argv[]) +{ + int c; + int lflag, dflag, sflag, mflag, pflag, eflag, iflag, tflag; + int iamode; + pri_t iauprilim; + pri_t iaupri; + char *idtypnm; + idtype_t idtype; + int idargc; + uint_t cflags; + + (void) strlcpy(cmdpath, argv[0], MAXPATHLEN); + (void) strlcpy(basenm, basename(argv[0]), BASENMSZ); + lflag = dflag = sflag = mflag = pflag = eflag = iflag = tflag = 0; + while ((c = getopt(argc, argv, "ldsm:p:t:ec:i:")) != -1) { + switch (c) { + + case 'l': + lflag++; + break; + + case 'd': + dflag++; + break; + + case 's': + sflag++; + break; + + case 'm': + mflag++; + iauprilim = (pri_t)str2num(optarg, SHRT_MIN, SHRT_MAX); + if (errno) + fatalerr("%s: Specified user priority limit %s" + " out of configured range\n", + basenm, optarg); + break; + + case 'p': + pflag++; + iaupri = (pri_t)str2num(optarg, SHRT_MIN, SHRT_MAX); + if (errno) + fatalerr("%s: Specified user priority %s out of" + " configured range\n", basenm, optarg); + break; + + case 't': + tflag++; + iamode = (int)str2num(optarg, INT_MIN, INT_MAX); + if (errno || (iamode != IA_INTERACTIVE_OFF && + iamode != IA_SET_INTERACTIVE)) + fatalerr("%s: Specified illegal mode %s\n", + basenm, optarg); + break; + + case 'e': + eflag++; + break; + + case 'c': + if (strcmp(optarg, "IA") != 0) + fatalerr("error: %s executed for %s class, %s" + " is actually sub-command for IA class\n", + cmdpath, optarg, cmdpath); + break; + + case 'i': + iflag++; + idtypnm = optarg; + break; + + case '?': + fatalerr(usage); + + default: + break; + } + } + + if (lflag) { + if (dflag || sflag || mflag || pflag || tflag || eflag || iflag) + fatalerr(usage); + + print_iainfo(); + + } else if (dflag) { + if (lflag || sflag || mflag || pflag || tflag || eflag) + fatalerr(usage); + + return (print_iaprocs()); + + } else if (sflag) { + if (lflag || dflag || eflag) + fatalerr(usage); + + if (iflag) { + if (str2idtyp(idtypnm, &idtype) == -1) + fatalerr("%s: Bad idtype %s\n", basenm, + idtypnm); + } else { + idtype = P_PID; + } + + cflags = (pflag ? IA_DOUPRI : 0); + + if (mflag) + cflags |= IA_DOUPRILIM; + + if (tflag) + cflags |= IA_DOMODE; + + if (optind < argc) + idargc = argc - optind; + else + idargc = 0; + + return (set_iaprocs(idtype, idargc, &argv[optind], cflags, + iauprilim, iaupri, iamode)); + + } else if (eflag) { + if (lflag || dflag || sflag || iflag) + fatalerr(usage); + + cflags = (pflag ? IA_DOUPRI : 0); + + if (mflag) + cflags |= IA_DOUPRILIM; + + if (tflag) + cflags |= IA_DOMODE; + + exec_iacmd(&argv[optind], cflags, iauprilim, iaupri, iamode); + + } else { + fatalerr(usage); + } + + return (0); +} + + +/* + * Print our class name and the configured user priority range. + */ +static void +print_iainfo(void) +{ + pcinfo_t pcinfo; + + (void) strcpy(pcinfo.pc_clname, "IA"); + + (void) printf("IA (Interactive)\n"); + + if (priocntl(0, 0, PC_GETCID, (caddr_t)&pcinfo) == -1) + fatalerr("\tCan't get configured IA user priority range\n"); + + (void) printf("\tConfigured IA User Priority Range: -%d through %d\n", + ((iainfo_t *)pcinfo.pc_clinfo)->ia_maxupri, + ((iainfo_t *)pcinfo.pc_clinfo)->ia_maxupri); +} + + +/* + * Read a list of pids from stdin and print the user priority and user + * priority limit for each of the corresponding processes. + * print their interactive mode and nice values + */ +static int +print_iaprocs(void) +{ + pid_t *pidlist; + size_t numread; + int i; + char clname[PC_CLNMSZ]; + pri_t ia_uprilim; + pri_t ia_upri; + int ia_mode; + int error = 0; + + /* + * Read a list of pids from stdin. + */ + if ((pidlist = read_pidlist(&numread, stdin)) == NULL) + fatalerr("%s: Can't read pidlist.\n", basenm); + + (void) printf("INTERACTIVE CLASS PROCESSES:"); + (void) printf("\n PID IAUPRILIM IAUPRI IAMODE\n"); + + if (numread == 0) + fatalerr("%s: No pids on input\n", basenm); + + for (i = 0; i < numread; i++) { + (void) printf("%7ld", pidlist[i]); + if (priocntl(P_PID, pidlist[i], PC_GETXPARMS, "IA", + IA_KY_UPRI, &ia_upri, IA_KY_UPRILIM, &ia_uprilim, + IA_KY_MODE, &ia_mode, 0) != -1) { + (void) printf(" %5d %5d %5d\n", + ia_uprilim, ia_upri, ia_mode); + } else { + error = 1; + + if (priocntl(P_PID, pidlist[i], PC_GETXPARMS, NULL, + PC_KY_CLNAME, clname, 0) != -1 && + strcmp(clname, "IA")) + /* + * Process from some class other than + * interactive. It has probably changed class + * while priocntl command was executing + * (otherwise we wouldn't have been passed its + * pid). Print the little we know about it. + */ + (void) printf("\tChanged to class %s while" + " priocntl command executing\n", clname); + else + (void) printf("\tCan't get IA user priority\n"); + } + } + + free_pidlist(pidlist); + return (error); +} + + +/* + * Call priocntl() with command codes PC_SETXPARMS or PC_GETXPARMS. + * The first parameter behind the command code is always the class name. + * Each parameter is headed by a key, which determines the meaning of the + * following value. There are maximal IA_KEYCNT = 3 (key, value) pairs. + */ +static int +ia_priocntl(idtype_t idtype, id_t id, int cmd, char *clname, uintptr_t *argsp) +{ + return (priocntl(idtype, id, cmd, clname, argsp[0], argsp[1], + argsp[2], argsp[3], argsp[4], argsp[5], 0)); +} + + +/* + * Set all processes in the set specified by idtype/idargv to interactive + * (if they aren't already interactive ) and set their user priority limit + * and user priority to those specified by iauprilim and iaupri. + */ +static int +set_iaprocs(idtype_t idtype, int idargc, char **idargv, uint_t cflags, + pri_t iauprilim, pri_t iaupri, int iamode) +{ + pcinfo_t pcinfo; + uintptr_t args[2*IA_KEYCNT+1]; + uintptr_t *argsp = &args[0]; + int maxupri; + char idtypnm[PC_IDTYPNMSZ]; + int i; + int error = 0; + id_t id; + + /* + * Get the interactive class ID and max configured user priority. + */ + (void) strcpy(pcinfo.pc_clname, "IA"); + if (priocntl(0, 0, PC_GETCID, (caddr_t)&pcinfo) == -1) + fatalerr("%s: Can't get IA class ID, priocntl system call" + " failed with errno %d\n", basenm, errno); + maxupri = ((iainfo_t *)pcinfo.pc_clinfo)->ia_maxupri; + + /* + * Validate the iauprilim and iaupri arguments. + */ + if ((cflags & IA_DOUPRILIM) != 0) { + if (iauprilim > maxupri || iauprilim < -maxupri) + fatalerr("%s: Specified user priority limit %d out of" + " configured range\n", basenm, iauprilim); + ADDKEYVAL(argsp, IA_KY_UPRILIM, iauprilim); + } + + if ((cflags & IA_DOUPRI) != 0) { + if (iaupri > maxupri || iaupri < -maxupri) + fatalerr("%s: Specified user priority %d out of" + " configured range\n", basenm, iaupri); + ADDKEYVAL(argsp, IA_KY_UPRI, iaupri); + } + + if ((cflags & IA_DOMODE) != 0) + ADDKEYVAL(argsp, IA_KY_MODE, iamode); + *argsp = 0; + + if (idtype == P_ALL) { + if (ia_priocntl(P_ALL, 0, PC_SETXPARMS, "IA", args) == -1) { + if (errno == EPERM) { + (void) fprintf(stderr, + "Permissions error encountered" + " on one or more processes.\n"); + error = 1; + } else { + fatalerr("%s: Can't reset interactive" + " parameters\npriocntl system call failed" + " with errno %d\n", basenm, errno); + } + } else if ((cflags & (IA_DOUPRILIM|IA_DOUPRI)) == IA_DOUPRI) { + (void) verifyupri(idtype, 0, "IA", IA_KY_UPRILIM, + iaupri, basenm); + } + } else if (idargc == 0) { + if (ia_priocntl(idtype, P_MYID, PC_SETXPARMS, "IA", + args) == -1) { + if (errno == EPERM) { + (void) idtyp2str(idtype, idtypnm); + (void) fprintf(stderr, + "Permissions error encountered" + " on current %s.\n", idtypnm); + error = 1; + } else { + fatalerr("%s: Can't reset interactive" + " parameters\npriocntl system call failed" + " with errno %d\n", basenm, errno); + } + } else if ((cflags & (IA_DOUPRILIM|IA_DOUPRI)) == IA_DOUPRI && + getmyid(idtype, &id) != -1) { + (void) verifyupri(idtype, id, "IA", IA_KY_UPRILIM, + iaupri, basenm); + } + } else { + (void) idtyp2str(idtype, idtypnm); + for (i = 0; i < idargc; i++) { + if (idtype == P_CID) { + (void) strcpy(pcinfo.pc_clname, idargv[i]); + if (priocntl(0, 0, PC_GETCID, + (caddr_t)&pcinfo) == -1) + fatalerr("%s: Invalid or unconfigured" + " class %s, priocntl system call" + " failed with errno %d\n", + basenm, pcinfo.pc_clname, errno); + id = pcinfo.pc_cid; + } else { + id = (id_t)str2num(idargv[i], INT_MIN, INT_MAX); + if (errno) + fatalerr("%s: Invalid id \"%s\"\n", + basenm, idargv[i]); + } + + if (ia_priocntl(idtype, id, PC_SETXPARMS, "IA", + args) == -1) { + if (errno == EPERM) { + (void) fprintf(stderr, + "Permissions error" + " encountered on %s %s.\n", + idtypnm, idargv[i]); + error = 1; + } else { + fatalerr("%s: Can't reset interactive" + " parameters\npriocntl system call" + " failed with errno %d\n", + basenm, errno); + } + } else if ((cflags & (IA_DOUPRILIM|IA_DOUPRI)) == + IA_DOUPRI) { + (void) verifyupri(idtype, id, "IA", + IA_KY_UPRILIM, iaupri, basenm); + } + } + } + + return (error); +} + + +/* + * Execute the command pointed to by cmdargv as a interactive process + * with the user priority limit given by iauprilim and user priority iaupri. + */ +static void +exec_iacmd(char **cmdargv, uint_t cflags, pri_t iauprilim, pri_t iaupri, + int iamode) +{ + pcinfo_t pcinfo; + uintptr_t args[2*IA_KEYCNT+1]; + uintptr_t *argsp = &args[0]; + pri_t maxupri; + pri_t uprilim; + + /* + * Get the time sharing class ID and max configured user priority. + */ + (void) strcpy(pcinfo.pc_clname, "IA"); + if (priocntl(0, 0, PC_GETCID, (caddr_t)&pcinfo) == -1) + fatalerr("%s: Can't get IA class ID, priocntl system call" + " failed with errno %d\n", basenm, errno); + maxupri = ((iainfo_t *)pcinfo.pc_clinfo)->ia_maxupri; + + /* + * Validate the iauprilim and iaupri arguments. + */ + if ((cflags & IA_DOUPRILIM) != 0) { + if (iauprilim > maxupri || iauprilim < -maxupri) + fatalerr("%s: Specified user priority limit %d out of" + " configured range\n", basenm, iauprilim); + ADDKEYVAL(argsp, IA_KY_UPRILIM, iauprilim); + } + + if ((cflags & IA_DOUPRI) != 0) { + if (iaupri > maxupri || iaupri < -maxupri) + fatalerr("%s: Specified user priority %d out of" + " configured range\n", basenm, iaupri); + ADDKEYVAL(argsp, IA_KY_UPRI, iaupri); + } + + if ((cflags & IA_DOMODE) != 0) + ADDKEYVAL(argsp, IA_KY_MODE, iamode); + *argsp = 0; + + if (ia_priocntl(P_PID, P_MYID, PC_SETXPARMS, "IA", args) == -1) + fatalerr("%s: Can't reset interactive parameters\n" + "priocntl system call failed with errno %d\n", + basenm, errno); + + if ((cflags & (IA_DOUPRILIM|IA_DOUPRI)) == IA_DOUPRI) { + if (priocntl(P_PID, P_MYID, PC_GETXPARMS, "IA", + IA_KY_UPRILIM, &uprilim, 0) != -1 && iaupri > uprilim) + (void) fprintf(stderr, + "%s: Specified user priority %d exceeds" + " limit %d; set to %d (pid %d)\n", + basenm, iaupri, uprilim, uprilim, (int)getpid()); + } + + (void) execvp(cmdargv[0], cmdargv); + fatalerr("%s: Can't execute %s, exec failed with errno %d\n", + basenm, cmdargv[0], errno); +} diff --git a/usr/src/cmd/priocntl/priocntl.c b/usr/src/cmd/priocntl/priocntl.c new file mode 100644 index 0000000000..f599c18dad --- /dev/null +++ b/usr/src/cmd/priocntl/priocntl.c @@ -0,0 +1,1108 @@ +/* + * 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); +} diff --git a/usr/src/cmd/priocntl/priocntl.h b/usr/src/cmd/priocntl/priocntl.h new file mode 100644 index 0000000000..cd0f3e0ac4 --- /dev/null +++ b/usr/src/cmd/priocntl/priocntl.h @@ -0,0 +1,100 @@ +/* + * 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 (c) 1984, 1986, 1987, 1988, 1989 AT&T */ +/* All Rights Reserved */ + + +/* + * Copyright 2001-2002 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +#ifndef _PRIOCNTL_H +#define _PRIOCNTL_H + +#pragma ident "%Z%%M% %I% %E% SMI" /* SVr4.0 1.3 */ + +#ifdef __cplusplus +extern "C" { +#endif + +#define NPIDS 1024 /* number of additional pids allocated by realloc(). */ +#define NIDS 1024 /* max number of id arguments we handle */ + +#define BASENMSZ 16 +#define CSOPTSLN 128 /* max length of class specific opts string */ +#define PC_IDTYPNMSZ 12 /* max length of an idtype name */ + +/* + * The command string for the sub-command must be big enough for the + * path, the class specific options, and plenty of space for arguments. + */ +#define SUBCMDSZ 512 + +#define BADPID (-2) + +extern void fatalerr(); +extern int str2idtyp(char *, idtype_t *); +extern int idtyp2str(idtype_t, char *); +extern int idcompar(id_t *, id_t *); +extern id_t clname2cid(char *); +extern int getmyid(idtype_t, id_t *); +extern int getmyidstr(idtype_t, char *); +extern int verifyupri(idtype_t, id_t, char *, int, pri_t, char *); +extern pid_t *read_pidlist(size_t *, FILE *); +extern void free_pidlist(pid_t *); +extern long str2num(char *, long, long); +extern void itoa(long, char *); + +/* + * The following is an excerpt from <sys/hrtcntl.h>. HRT timers are not + * supported by SunOS (which will support the POSIX definition). Priocntl + * uses the hrt routine _hrtnewres because it coincidentally does the + * right thing. These defines allow this routine to be locally included + * in priocntl (rather than exported in libc). This should be improved in + * the long term. + */ + +#define HRT_TRUNC 0 /* Round results down. */ +#define HRT_RND 1 /* Round results (rnd up if fractional */ + /* part >= .5 otherwise round down). */ +#define HRT_RNDUP 2 /* Always round results up. */ + +/* + * Structure used to represent a high-resolution time-of-day + * or interval. + */ + +typedef struct hrtimer { + ulong_t hrt_secs; /* Seconds. */ + long hrt_rem; /* A value less than a second. */ + ulong_t hrt_res; /* The resolution of hrt_rem. */ +} hrtimer_t; + +extern int _hrtnewres(hrtimer_t *, ulong_t, long); + +#ifdef __cplusplus +} +#endif + +#endif /* _PRIOCNTL_H */ diff --git a/usr/src/cmd/priocntl/rtpriocntl.c b/usr/src/cmd/priocntl/rtpriocntl.c new file mode 100644 index 0000000000..8a7340589f --- /dev/null +++ b/usr/src/cmd/priocntl/rtpriocntl.c @@ -0,0 +1,524 @@ +/* + * 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 <stdlib.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/time.h> +#include <sys/procset.h> +#include <sys/priocntl.h> +#include <sys/rtpriocntl.h> +#include <sys/param.h> +#include <signal.h> +#include <libgen.h> +#include <limits.h> +#include <errno.h> + +#include "priocntl.h" + +/* + * This file contains the class specific code implementing + * the real-time priocntl sub-command. + */ + +#define ADDKEYVAL(p, k, v) { (p[0]) = (k); (p[1]) = (v); p += 2; } + +#define RT_KEYCNT 4 /* maximal number of (key, value) pairs */ + +/* + * control flags + */ +#define RT_DOPRI 0x01 /* change priority */ +#define RT_DOTQ 0x02 /* change RT time quantum */ +#define RT_DOSIG 0x10 /* change RT time quantum signal */ + + +static void print_rtinfo(void); +static int print_rtprocs(void); +static int rt_priocntl(idtype_t, id_t, int, char *, uintptr_t *); +static int set_rtprocs(idtype_t, int, char **, uint_t, pri_t, long, + long, int); +static void exec_rtcmd(char **, uint_t, pri_t, long, long, int); + + +static char usage[] = +"usage: priocntl -l\n" +" priocntl -d [-i idtype] [idlist]\n" +" priocntl -s [-c RT] [-p rtpri] [-t tqntm [-r res]] [-q tqsig]\n" +" [-i idtype] [idlist]\n" +" priocntl -e [-c RT] [-p rtpri] [-t tqntm [-r res]] [-q tqsig]\n" +" command [argument(s)]\n"; + +static char cmdpath[MAXPATHLEN]; +static char basenm[BASENMSZ]; + + +int +main(int argc, char *argv[]) +{ + int c; + int lflag, dflag, sflag, pflag, tflag, rflag, eflag, iflag; + int qflag; + pri_t rtpri; + long tqntm; + long res; + int tqsig; + char *idtypnm; + idtype_t idtype; + int idargc; + uint_t cflags; + + (void) strlcpy(cmdpath, argv[0], MAXPATHLEN); + (void) strlcpy(basenm, basename(argv[0]), BASENMSZ); + qflag = + lflag = dflag = sflag = pflag = tflag = rflag = eflag = iflag = 0; + while ((c = getopt(argc, argv, "ldsp:t:r:q:ec:i:")) != -1) { + switch (c) { + + case 'l': + lflag++; + break; + + case 'd': + dflag++; + break; + + case 's': + sflag++; + break; + + case 'p': + pflag++; + rtpri = (pri_t)str2num(optarg, SHRT_MIN, SHRT_MAX); + if (errno) + fatalerr("%s: Specified real time priority %s" + " out of configured range\n", + basenm, optarg); + break; + + case 't': + tflag++; + tqntm = str2num(optarg, 1, INT_MAX); + if (errno) + fatalerr("%s: Invalid time quantum specified;" + " time quantum must be positive\n", basenm); + break; + + case 'r': + rflag++; + res = str2num(optarg, 1, 1000000000); + if (errno) + fatalerr("%s: Invalid resolution specified;" + " resolution must be between" + " 1 and 1,000,000,000\n", basenm); + + break; + + case 'q': + qflag++; + if (str2sig(optarg, &tqsig) != 0) + fatalerr("%s: Invalid real time quantum signal" + " specified\n", basenm); + break; + + case 'e': + eflag++; + break; + + case 'c': + if (strcmp(optarg, "RT") != 0) + fatalerr("error: %s executed for %s class, %s" + " is actually sub-command for RT class\n", + cmdpath, optarg, cmdpath); + break; + + case 'i': + iflag++; + idtypnm = optarg; + break; + + case '?': + fatalerr(usage); + + default: + break; + } + } + + if (lflag) { + if (dflag || sflag || pflag || tflag || rflag || eflag || + iflag || qflag) + fatalerr(usage); + + print_rtinfo(); + + } else if (dflag) { + if (lflag || sflag || pflag || tflag || rflag || eflag || qflag) + fatalerr(usage); + + return (print_rtprocs()); + + } else if (sflag) { + if (lflag || dflag || eflag) + fatalerr(usage); + + if (iflag) { + if (str2idtyp(idtypnm, &idtype) == -1) + fatalerr("%s: Bad idtype %s\n", basenm, + idtypnm); + } else { + idtype = P_PID; + } + + cflags = (pflag ? RT_DOPRI : 0); + + if (tflag) + cflags |= RT_DOTQ; + + if (rflag == 0) + res = 1000; + + if (optind < argc) + idargc = argc - optind; + else + idargc = 0; + + if (qflag) + cflags |= RT_DOSIG; + + return (set_rtprocs(idtype, idargc, &argv[optind], cflags, + rtpri, tqntm, res, tqsig)); + + } else if (eflag) { + if (lflag || dflag || sflag || iflag) + fatalerr(usage); + + cflags = (pflag ? RT_DOPRI : 0); + + if (tflag) + cflags |= RT_DOTQ; + + if (rflag == 0) + res = 1000; + + if (qflag) + cflags |= RT_DOSIG; + + exec_rtcmd(&argv[optind], cflags, rtpri, tqntm, res, tqsig); + + } else { + fatalerr(usage); + } + + return (0); +} + + +/* + * Print our class name and the maximum configured real-time priority. + */ +static void +print_rtinfo(void) +{ + pcinfo_t pcinfo; + + (void) strcpy(pcinfo.pc_clname, "RT"); + + (void) printf("RT (Real Time)\n"); + + if (priocntl(0, 0, PC_GETCID, (caddr_t)&pcinfo) == -1) + fatalerr("\tCan't get maximum configured RT priority\n"); + + (void) printf("\tMaximum Configured RT Priority: %d\n", + ((rtinfo_t *)pcinfo.pc_clinfo)->rt_maxpri); +} + + +/* + * Read a list of pids from stdin and print the real-time priority and time + * quantum (in millisecond resolution) for each of the corresponding processes. + */ +static int +print_rtprocs(void) +{ + pid_t *pidlist; + size_t numread; + int i; + char clname[PC_CLNMSZ]; + pri_t rt_pri; + uint_t rt_tqsecs; + int rt_tqnsecs; + int rt_tqsig; + int error = 0; + + /* + * Read a list of pids from stdin. + */ + if ((pidlist = read_pidlist(&numread, stdin)) == NULL) + fatalerr("%s: Can't read pidlist.\n", basenm); + + (void) printf("REAL TIME PROCESSES:\n" + " PID RTPRI TQNTM TQSIG\n"); + + if (numread == 0) + fatalerr("%s: No pids on input\n", basenm); + + for (i = 0; i < numread; i++) { + (void) printf("%7ld", pidlist[i]); + if (priocntl(P_PID, pidlist[i], PC_GETXPARMS, "RT", + RT_KY_TQSECS, &rt_tqsecs, RT_KY_TQNSECS, &rt_tqnsecs, + RT_KY_PRI, &rt_pri, RT_KY_TQSIG, &rt_tqsig, 0) != -1) { + (void) printf(" %5d", rt_pri); + if (rt_tqnsecs == RT_TQINF) + (void) printf(" RT_TQINF"); + else + (void) printf(" %11lld", + (longlong_t)rt_tqsecs * 1000 + + rt_tqnsecs / 1000000); + + (void) printf(" %3d\n", rt_tqsig); + } else { + error = 1; + + if (priocntl(P_PID, pidlist[i], PC_GETXPARMS, NULL, + PC_KY_CLNAME, clname, 0) != -1 && + strcmp(clname, "RT")) + /* + * Process from some class other than real time. + * It has probably changed class while priocntl + * command was executing (otherwise we wouldn't + * have been passed its pid). Print the little + * we know about it. + */ + (void) printf("\tChanged to class %s while" + " priocntl command executing\n", clname); + else + (void) printf("\tCan't get real time" + " parameters\n"); + } + } + + free_pidlist(pidlist); + return (error); +} + + +/* + * Call priocntl() with command codes PC_SETXPARMS or PC_GETXPARMS. + * The first parameter behind the command code is always the class name. + * Each parameter is headed by a key, which determines the meaning of the + * following value. There are maximal RT_KEYCNT = 4 (key, value) pairs. + */ +static int +rt_priocntl(idtype_t idtype, id_t id, int cmd, char *clname, uintptr_t *argsp) +{ + return (priocntl(idtype, id, cmd, clname, argsp[0], argsp[1], + argsp[2], argsp[3], argsp[4], argsp[5], argsp[6], argsp[7], 0)); +} + + +/* + * Set all processes in the set specified by idtype/idargv to real time + * (if they aren't already real time) and set their real-time priority, + * real-time quantum and real-time quantum signal to those specified by + * rtpri, tqntm/res and rtqsig. + */ +static int +set_rtprocs(idtype_t idtype, int idargc, char **idargv, uint_t cflags, + pri_t rtpri, long tqntm, long res, int rtqsig) +{ + pcinfo_t pcinfo; + uintptr_t args[2*RT_KEYCNT+1]; + uintptr_t *argsp = &args[0]; + pri_t maxrtpri; + hrtimer_t hrtime; + char idtypnm[PC_IDTYPNMSZ]; + int i; + id_t id; + int error = 0; + + + /* + * Get the real time class ID and max configured RT priority. + */ + (void) strcpy(pcinfo.pc_clname, "RT"); + if (priocntl(0, 0, PC_GETCID, (caddr_t)&pcinfo) == -1) + fatalerr("%s: Can't get RT class ID, priocntl system call" + " failed with errno %d\n", basenm, errno); + maxrtpri = ((rtinfo_t *)pcinfo.pc_clinfo)->rt_maxpri; + + /* + * Validate the rtpri and res arguments. + */ + if ((cflags & RT_DOPRI) != 0) { + if (rtpri > maxrtpri || rtpri < 0) + fatalerr("%s: Specified real time priority %d out of" + " configured range\n", basenm, rtpri); + ADDKEYVAL(argsp, RT_KY_PRI, rtpri); + } + + if ((cflags & RT_DOTQ) != 0) { + hrtime.hrt_secs = 0; + hrtime.hrt_rem = tqntm; + hrtime.hrt_res = res; + if (_hrtnewres(&hrtime, NANOSEC, HRT_RNDUP) == -1) + fatalerr("%s: Can't convert resolution.\n", basenm); + ADDKEYVAL(argsp, RT_KY_TQSECS, hrtime.hrt_secs); + ADDKEYVAL(argsp, RT_KY_TQNSECS, hrtime.hrt_rem); + } + + if ((cflags & RT_DOSIG) != 0) + ADDKEYVAL(argsp, RT_KY_TQSIG, rtqsig); + *argsp = 0; + + if (idtype == P_ALL) { + if (rt_priocntl(P_ALL, 0, PC_SETXPARMS, "RT", args) == -1) { + if (errno == EPERM) { + (void) fprintf(stderr, + "Permissions error encountered" + " on one or more processes.\n"); + error = 1; + } else { + fatalerr("%s: Can't reset real time parameters" + "\npriocntl system call failed with" + " errno %d\n", basenm, errno); + } + } + } else if (idargc == 0) { + if (rt_priocntl(idtype, P_MYID, PC_SETXPARMS, "RT", + args) == -1) { + if (errno == EPERM) { + (void) idtyp2str(idtype, idtypnm); + (void) fprintf(stderr, "Permissions error" + " encountered on current %s.\n", idtypnm); + error = 1; + } else { + fatalerr("%s: Can't reset real time parameters" + "\npriocntl system call failed with" + " errno %d\n", basenm, errno); + } + } + } else { + (void) idtyp2str(idtype, idtypnm); + for (i = 0; i < idargc; i++) { + if (idtype == P_CID) { + (void) strcpy(pcinfo.pc_clname, idargv[i]); + if (priocntl(0, 0, PC_GETCID, + (caddr_t)&pcinfo) == -1) + fatalerr("%s: Invalid or unconfigured" + " class %s, priocntl system call" + " failed with errno %d\n", + basenm, pcinfo.pc_clname, errno); + id = pcinfo.pc_cid; + } else { + id = (id_t)str2num(idargv[i], INT_MIN, INT_MAX); + if (errno) + fatalerr("%s: Invalid id \"%s\"\n", + basenm, idargv[i]); + } + + if (rt_priocntl(idtype, id, PC_SETXPARMS, "RT", + args) == -1) { + if (errno == EPERM) { + (void) fprintf(stderr, + "Permissions error encountered on" + " %s %s.\n", idtypnm, idargv[i]); + error = 1; + } else { + fatalerr("%s: Can't reset real time" + " parameters\npriocntl system call" + " failed with errno %d\n", + basenm, errno); + } + } + } + } + + return (error); +} + + +/* + * Execute the command pointed to by cmdargv as a real-time process + * with real time priority rtpri, quantum tqntm/res and quantum signal rtqsig. + */ +static void +exec_rtcmd(char **cmdargv, uint_t cflags, pri_t rtpri, long tqntm, long res, + int rtqsig) +{ + pcinfo_t pcinfo; + uintptr_t args[2*RT_KEYCNT+1]; + uintptr_t *argsp = &args[0]; + pri_t maxrtpri; + hrtimer_t hrtime; + + /* + * Get the real time class ID and max configured RT priority. + */ + (void) strcpy(pcinfo.pc_clname, "RT"); + if (priocntl(0, 0, PC_GETCID, (caddr_t)&pcinfo) == -1) + fatalerr("%s: Can't get RT class ID, priocntl system call" + " failed with errno %d\n", basenm, errno); + maxrtpri = ((rtinfo_t *)pcinfo.pc_clinfo)->rt_maxpri; + + if ((cflags & RT_DOPRI) != 0) { + if (rtpri > maxrtpri || rtpri < 0) + fatalerr("%s: Specified real time priority %d out of" + " configured range\n", basenm, rtpri); + ADDKEYVAL(argsp, RT_KY_PRI, rtpri); + } + + if ((cflags & RT_DOTQ) != 0) { + hrtime.hrt_secs = 0; + hrtime.hrt_rem = tqntm; + hrtime.hrt_res = res; + if (_hrtnewres(&hrtime, NANOSEC, HRT_RNDUP) == -1) + fatalerr("%s: Can't convert resolution.\n", basenm); + ADDKEYVAL(argsp, RT_KY_TQSECS, hrtime.hrt_secs); + ADDKEYVAL(argsp, RT_KY_TQNSECS, hrtime.hrt_rem); + } + + if ((cflags & RT_DOSIG) != 0) + ADDKEYVAL(argsp, RT_KY_TQSIG, rtqsig); + *argsp = 0; + + if (rt_priocntl(P_PID, P_MYID, PC_SETXPARMS, "RT", args) == -1) + fatalerr("%s: Can't reset real time parameters\n" + "priocntl system call failed with errno %d\n", + basenm, errno); + + (void) execvp(cmdargv[0], cmdargv); + fatalerr("%s: Can't execute %s, exec failed with errno %d\n", + basenm, cmdargv[0], errno); +} 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); + } +} diff --git a/usr/src/cmd/priocntl/tspriocntl.c b/usr/src/cmd/priocntl/tspriocntl.c new file mode 100644 index 0000000000..21b4882c69 --- /dev/null +++ b/usr/src/cmd/priocntl/tspriocntl.c @@ -0,0 +1,477 @@ +/* + * 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 <stdlib.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/procset.h> +#include <sys/priocntl.h> +#include <sys/tspriocntl.h> +#include <libgen.h> +#include <limits.h> +#include <errno.h> + +#include "priocntl.h" + +/* + * This file contains the class specific code implementing + * the time-sharing priocntl sub-command. + */ + +#define ADDKEYVAL(p, k, v) { (p[0]) = (k); (p[1]) = (v); p += 2; } + +#define TS_KEYCNT 2 /* maximal number of (key, value) pairs */ + +/* + * control flags + */ +#define TS_DOUPRILIM 0x01 /* user priority limit */ +#define TS_DOUPRI 0x02 /* user priority */ + + +static void print_tsinfo(void); +static int print_tsprocs(void); +static int ts_priocntl(idtype_t, id_t, int, char *, uintptr_t *); +static int set_tsprocs(idtype_t, int, char **, uint_t, pri_t, pri_t); +static void exec_tscmd(char **, uint_t, pri_t, pri_t); + + +static char usage[] = +"usage: priocntl -l\n\ + priocntl -d [-d idtype] [idlist]\n\ + priocntl -s [-c TS] [-m tsuprilim] [-p tsupri] [-i idtype] [idlist]\n\ + priocntl -e [-c TS] [-m tsuprilim] [-p tsupri] command [argument(s)]\n"; + +static char cmdpath[MAXPATHLEN]; +static char basenm[BASENMSZ]; + + +int +main(int argc, char *argv[]) +{ + int c; + int lflag, dflag, sflag, mflag, pflag, eflag, iflag; + pri_t tsuprilim; + pri_t tsupri; + char *idtypnm; + idtype_t idtype; + int idargc; + uint_t cflags; + + (void) strlcpy(cmdpath, argv[0], MAXPATHLEN); + (void) strlcpy(basenm, basename(argv[0]), BASENMSZ); + lflag = dflag = sflag = mflag = pflag = eflag = iflag = 0; + while ((c = getopt(argc, argv, "ldsm:p:ec:i:")) != -1) { + switch (c) { + + case 'l': + lflag++; + break; + + case 'd': + dflag++; + break; + + case 's': + sflag++; + break; + + case 'm': + mflag++; + tsuprilim = (pri_t)str2num(optarg, SHRT_MIN, SHRT_MAX); + if (errno) + fatalerr("%s: Specified user priority limit %s" + " out of configured range\n", + basenm, optarg); + break; + + case 'p': + pflag++; + tsupri = (pri_t)str2num(optarg, SHRT_MIN, SHRT_MAX); + if (errno) + fatalerr("%s: Specified user priority %s out of" + " configured range\n", basenm, optarg); + break; + + case 'e': + eflag++; + break; + + case 'c': + if (strcmp(optarg, "TS") != 0) + fatalerr("error: %s executed for %s class, %s" + " is actually sub-command for TS class\n", + cmdpath, optarg, cmdpath); + break; + + case 'i': + iflag++; + idtypnm = optarg; + break; + + case '?': + fatalerr(usage); + + default: + break; + } + } + + if (lflag) { + if (dflag || sflag || mflag || pflag || eflag || iflag) + fatalerr(usage); + + print_tsinfo(); + + } else if (dflag) { + if (lflag || sflag || mflag || pflag || eflag) + fatalerr(usage); + + return (print_tsprocs()); + + } else if (sflag) { + if (lflag || dflag || eflag) + fatalerr(usage); + + if (iflag) { + if (str2idtyp(idtypnm, &idtype) == -1) + fatalerr("%s: Bad idtype %s\n", basenm, + idtypnm); + } else { + idtype = P_PID; + } + + cflags = (pflag ? TS_DOUPRI : 0); + + if (mflag) + cflags |= TS_DOUPRILIM; + + if (optind < argc) + idargc = argc - optind; + else + idargc = 0; + + return (set_tsprocs(idtype, idargc, &argv[optind], cflags, + tsuprilim, tsupri)); + + } else if (eflag) { + if (lflag || dflag || sflag || iflag) + fatalerr(usage); + + cflags = (pflag ? TS_DOUPRI : 0); + + if (mflag) + cflags |= TS_DOUPRILIM; + + exec_tscmd(&argv[optind], cflags, tsuprilim, tsupri); + + } else { + fatalerr(usage); + } + + return (0); +} + + +/* + * Print our class name and the configured user priority range. + */ +static void +print_tsinfo(void) +{ + pcinfo_t pcinfo; + + (void) strcpy(pcinfo.pc_clname, "TS"); + + (void) printf("TS (Time Sharing)\n"); + + if (priocntl(0, 0, PC_GETCID, (caddr_t)&pcinfo) == -1) + fatalerr("\tCan't get configured TS user priority range\n"); + + (void) printf("\tConfigured TS User Priority Range: -%d through %d\n", + ((tsinfo_t *)pcinfo.pc_clinfo)->ts_maxupri, + ((tsinfo_t *)pcinfo.pc_clinfo)->ts_maxupri); +} + + +/* + * Read a list of pids from stdin and print the user priority and user + * priority limit for each of the corresponding processes. + */ +static int +print_tsprocs(void) +{ + pid_t *pidlist; + size_t numread; + int i; + char clname[PC_CLNMSZ]; + pri_t ts_uprilim; + pri_t ts_upri; + int error = 0; + + /* + * Read a list of pids from stdin. + */ + if ((pidlist = read_pidlist(&numread, stdin)) == NULL) + fatalerr("%s: Can't read pidlist.\n", basenm); + + (void) printf( + "TIME SHARING PROCESSES:\n PID TSUPRILIM TSUPRI\n"); + + if (numread == 0) + fatalerr("%s: No pids on input\n", basenm); + + for (i = 0; i < numread; i++) { + (void) printf("%7ld", pidlist[i]); + if (priocntl(P_PID, pidlist[i], PC_GETXPARMS, "TS", TS_KY_UPRI, + &ts_upri, TS_KY_UPRILIM, &ts_uprilim, 0) != -1) { + (void) printf(" %5d %5d\n", + ts_uprilim, ts_upri); + } else { + error = 1; + + if (priocntl(P_PID, pidlist[i], PC_GETXPARMS, NULL, + PC_KY_CLNAME, clname, 0) != -1 && + strcmp(clname, "TS")) + /* + * Process from some class other than time + * sharing. It has probably changed class while + * priocntl command was executing (otherwise + * we wouldn't have been passed its pid). + * Print the little we know about it. + */ + (void) printf("\tChanged to class %s while" + " priocntl command executing\n", clname); + else + (void) printf("\tCan't get TS user priority\n"); + } + } + + free_pidlist(pidlist); + return (error); +} + + +/* + * Call priocntl() with command codes PC_SETXPARMS or PC_GETXPARMS. + * The first parameter behind the command code is always the class name. + * Each parameter is headed by a key, which determines the meaning of the + * following value. There are maximal TS_KEYCNT = 2 (key, value) pairs. + */ +static int +ts_priocntl(idtype_t idtype, id_t id, int cmd, char *clname, uintptr_t *argsp) +{ + return (priocntl(idtype, id, cmd, clname, argsp[0], argsp[1], + argsp[2], argsp[3], 0)); +} + + +/* + * Set all processes in the set specified by idtype/idargv to time-sharing + * (if they aren't already time-sharing) and set their user priority limit + * and user priority to those specified by tsuprilim and tsupri. + */ +static int +set_tsprocs(idtype_t idtype, int idargc, char **idargv, uint_t cflags, + pri_t tsuprilim, pri_t tsupri) +{ + pcinfo_t pcinfo; + uintptr_t args[2*TS_KEYCNT+1]; + uintptr_t *argsp = &args[0]; + pri_t maxupri; + char idtypnm[PC_IDTYPNMSZ]; + int i; + int error = 0; + id_t id; + + /* + * Get the time sharing class ID and max configured user priority. + */ + (void) strcpy(pcinfo.pc_clname, "TS"); + if (priocntl(0, 0, PC_GETCID, (caddr_t)&pcinfo) == -1) + fatalerr("%s: Can't get TS class ID, priocntl system call" + " failed with errno %d\n", basenm, errno); + maxupri = ((tsinfo_t *)pcinfo.pc_clinfo)->ts_maxupri; + + /* + * Validate the tsuprilim and tsupri arguments. + */ + if ((cflags & TS_DOUPRILIM) != 0) { + if (tsuprilim > maxupri || tsuprilim < -maxupri) + fatalerr("%s: Specified user priority limit %d out of" + " configured range\n", basenm, tsuprilim); + ADDKEYVAL(argsp, TS_KY_UPRILIM, tsuprilim); + } + + if ((cflags & TS_DOUPRI) != 0) { + if (tsupri > maxupri || tsupri < -maxupri) + fatalerr("%s: Specified user priority %d out of" + " configured range\n", basenm, tsupri); + ADDKEYVAL(argsp, TS_KY_UPRI, tsupri); + } + *argsp = 0; + + if (idtype == P_ALL) { + if (ts_priocntl(P_ALL, 0, PC_SETXPARMS, "TS", args) == -1) { + if (errno == EPERM) { + (void) fprintf(stderr, + "Permissions error encountered" + " on one or more processes.\n"); + error = 1; + } else { + fatalerr("%s: Can't reset time sharing" + " parameters\npriocntl system call failed" + " with errno %d\n", basenm, errno); + } + } else if ((cflags & (TS_DOUPRILIM|TS_DOUPRI)) == TS_DOUPRI) { + (void) verifyupri(idtype, 0, "TS", TS_KY_UPRILIM, + tsupri, basenm); + } + } else if (idargc == 0) { + if (ts_priocntl(idtype, P_MYID, PC_SETXPARMS, "TS", + args) == -1) { + if (errno == EPERM) { + (void) idtyp2str(idtype, idtypnm); + (void) fprintf(stderr, "Permissions error" + " encountered on current %s.\n", idtypnm); + error = 1; + } else { + fatalerr("%s: Can't reset time sharing" + " parameters\npriocntl system call failed" + " with errno %d\n", basenm, errno); + } + } else if ((cflags & (TS_DOUPRILIM|TS_DOUPRI)) == TS_DOUPRI && + getmyid(idtype, &id) != -1) { + (void) verifyupri(idtype, id, "TS", TS_KY_UPRILIM, + tsupri, basenm); + } + } else { + (void) idtyp2str(idtype, idtypnm); + for (i = 0; i < idargc; i++) { + if (idtype == P_CID) { + (void) strcpy(pcinfo.pc_clname, idargv[i]); + if (priocntl(0, 0, PC_GETCID, + (caddr_t)&pcinfo) == -1) + fatalerr("%s: Invalid or unconfigured" + " class %s, priocntl system call" + " failed with errno %d\n", + basenm, pcinfo.pc_clname, errno); + id = pcinfo.pc_cid; + } else { + id = (id_t)str2num(idargv[i], INT_MIN, INT_MAX); + if (errno) + fatalerr("%s: Invalid id \"%s\"\n", + basenm, idargv[i]); + } + + if (ts_priocntl(idtype, id, PC_SETXPARMS, "TS", + args) == -1) { + if (errno == EPERM) { + (void) fprintf(stderr, + "Permissions error encountered on" + " %s %s.\n", idtypnm, idargv[i]); + error = 1; + } else { + fatalerr("%s: Can't reset time sharing" + " parameters\npriocntl system call" + " failed with errno %d\n", + basenm, errno); + } + } else if ((cflags & (TS_DOUPRILIM|TS_DOUPRI)) == + TS_DOUPRI) { + (void) verifyupri(idtype, id, "TS", + TS_KY_UPRILIM, tsupri, basenm); + } + } + } + + return (error); +} + + +/* + * Execute the command pointed to by cmdargv as a time-sharing process + * with the user priority limit given by tsuprilim and user priority tsupri. + */ +static void +exec_tscmd(char **cmdargv, uint_t cflags, pri_t tsuprilim, pri_t tsupri) +{ + pcinfo_t pcinfo; + uintptr_t args[2*TS_KEYCNT+1]; + uintptr_t *argsp = &args[0]; + pri_t maxupri; + pri_t uprilim; + + /* + * Get the time sharing class ID and max configured user priority. + */ + (void) strcpy(pcinfo.pc_clname, "TS"); + if (priocntl(0, 0, PC_GETCID, (caddr_t)&pcinfo) == -1) + fatalerr("%s: Can't get TS class ID, priocntl system call" + " failed with errno %d\n", basenm, errno); + maxupri = ((tsinfo_t *)pcinfo.pc_clinfo)->ts_maxupri; + + if ((cflags & TS_DOUPRILIM) != 0) { + if (tsuprilim > maxupri || tsuprilim < -maxupri) + fatalerr("%s: Specified user priority limit %d out of" + " configured range\n", basenm, tsuprilim); + ADDKEYVAL(argsp, TS_KY_UPRILIM, tsuprilim); + } + + if ((cflags & TS_DOUPRI) != 0) { + if (tsupri > maxupri || tsupri < -maxupri) + fatalerr("%s: Specified user priority %d out of" + " configured range\n", basenm, tsupri); + ADDKEYVAL(argsp, TS_KY_UPRI, tsupri); + } + *argsp = 0; + + if (ts_priocntl(P_PID, P_MYID, PC_SETXPARMS, "TS", args) == -1) + fatalerr("%s: Can't reset time sharing parameters\n" + "priocntl system call failed with errno %d\n", + basenm, errno); + + if ((cflags & (TS_DOUPRILIM|TS_DOUPRI)) == TS_DOUPRI) { + if (priocntl(P_PID, P_MYID, PC_GETXPARMS, "TS", + TS_KY_UPRILIM, &uprilim, 0) != -1 && tsupri > uprilim) + (void) fprintf(stderr, + "%s: Specified user priority %d exceeds" + " limit %d; set to %d (pid %d)\n", + basenm, tsupri, uprilim, uprilim, (int)getpid()); + } + + (void) execvp(cmdargv[0], cmdargv); + fatalerr("%s: Can't execute %s, exec failed with errno %d\n", + basenm, cmdargv[0], errno); +} |
