1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22/*
23 * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
28/*	  All Rights Reserved  	*/
29
30#pragma ident	"%Z%%M%	%I%	%E% SMI"
31
32#include <sys/types.h>
33#include <sys/procset.h>
34#include <sys/priocntl.h>
35#include <sys/fsspriocntl.h>
36#include <string.h>
37#include <stdio.h>
38#include <unistd.h>
39#include <libgen.h>
40#include <limits.h>
41#include <errno.h>
42#include "priocntl.h"
43
44/*
45 * This file contains the class specific code implementing the fair-share
46 * scheduler priocntl sub-command.
47 */
48#define	ADDKEYVAL(p, k, v)	{ (p[0]) = (k); (p[1]) = (v); p += 2; }
49#define	FSS_KEYCNT		2	/* max number of (key, value) pairs */
50
51/*
52 * Control flags
53 */
54#define	FSS_DOUPRILIM	0x01	/* user priority limit */
55#define	FSS_DOUPRI	0x02	/* user priority */
56
57static void print_fssinfo(void);
58static int print_fssprocs(void);
59static int fss_priocntl(idtype_t, id_t, int, char *, uintptr_t *);
60static int set_fssprocs(idtype_t, int, char **, uint_t, pri_t, pri_t);
61static void exec_fsscmd(char **, uint_t, pri_t, pri_t);
62
63static char usage[] =
64"usage:	priocntl -l\n"
65"	priocntl -d [-d idtype] [idlist]\n"
66"	priocntl -s [-c FSS] [-m fssuprilim] [-p fssupri] [-i idtype] "
67"[idlist]\n"
68"	priocntl -e [-c FSS] [-m fssuprilim] [-p fssupri] command [argument(s)]"
69"\n";
70
71static char	cmdpath[MAXPATHLEN];
72static char	basenm[BASENMSZ];
73
74
75int
76main(int argc, char *argv[])
77{
78	int c;
79	int lflag, dflag, sflag, mflag, pflag, eflag, iflag;
80	pri_t fssuprilim;
81	pri_t fssupri;
82	char *idtypnm;
83	idtype_t idtype;
84	int idargc;
85	uint_t cflags;
86
87	(void) strlcpy(cmdpath, argv[0], MAXPATHLEN);
88	(void) strlcpy(basenm, basename(argv[0]), BASENMSZ);
89	lflag = dflag = sflag = mflag = pflag = eflag = iflag = 0;
90	while ((c = getopt(argc, argv, "ldsm:p:ec:i:")) != -1) {
91		switch (c) {
92		case 'l':
93			lflag++;
94			break;
95		case 'd':
96			dflag++;
97			break;
98		case 's':
99			sflag++;
100			break;
101		case 'm':
102			mflag++;
103			fssuprilim = (pri_t)str2num(optarg, SHRT_MIN, SHRT_MAX);
104			if (errno)
105				fatalerr("%s: Specified user priority limit %s "
106				    "out of configured range\n",
107				    basenm, optarg);
108			break;
109		case 'p':
110			pflag++;
111			fssupri = (pri_t)str2num(optarg, SHRT_MIN, SHRT_MAX);
112			if (errno)
113				fatalerr("%s: Specified user priority %s "
114				    "out of configured range\n",
115				    basenm, optarg);
116			break;
117		case 'e':
118			eflag++;
119			break;
120		case 'c':
121			if (strcmp(optarg, "FSS") != 0)
122				fatalerr("error: %s executed for %s class, %s "
123				    "is actually sub-command for FSS class\n",
124				    cmdpath, optarg, cmdpath);
125			break;
126		case 'i':
127			iflag++;
128			idtypnm = optarg;
129			break;
130		case '?':
131			fatalerr(usage);
132		default:
133			break;
134		}
135	}
136
137	if (lflag) {
138		if (dflag || sflag || mflag || pflag || eflag || iflag)
139			fatalerr(usage);
140
141		print_fssinfo();
142
143	} else if (dflag) {
144		if (lflag || sflag || mflag || pflag || eflag)
145			fatalerr(usage);
146
147		return (print_fssprocs());
148
149	} else if (sflag) {
150		if (lflag || dflag || eflag)
151			fatalerr(usage);
152
153		if (iflag) {
154			if (str2idtyp(idtypnm, &idtype) == -1)
155				fatalerr("%s: Bad idtype %s\n", basenm,
156				    idtypnm);
157		} else {
158			idtype = P_PID;
159		}
160
161		cflags = (pflag ? FSS_DOUPRI : 0);
162
163		if (mflag)
164			cflags |= FSS_DOUPRILIM;
165
166		if (optind < argc)
167			idargc = argc - optind;
168		else
169			idargc = 0;
170
171		return (set_fssprocs(idtype, idargc, &argv[optind], cflags,
172		    fssuprilim, fssupri));
173
174	} else if (eflag) {
175		if (lflag || dflag || sflag || iflag)
176			fatalerr(usage);
177
178		cflags = (pflag ? FSS_DOUPRI : 0);
179
180		if (mflag)
181			cflags |= FSS_DOUPRILIM;
182
183		exec_fsscmd(&argv[optind], cflags, fssuprilim, fssupri);
184
185	} else {
186		fatalerr(usage);
187	}
188
189	return (0);
190}
191
192/*
193 * Print our class name and the configured user priority range.
194 */
195static void
196print_fssinfo(void)
197{
198	pcinfo_t pcinfo;
199
200	(void) strcpy(pcinfo.pc_clname, "FSS");
201
202	(void) printf("FSS (Fair Share)\n");
203
204	if (priocntl(0, 0, PC_GETCID, (caddr_t)&pcinfo) == -1)
205		fatalerr("\tCan't get configured FSS user priority range\n");
206
207	(void) printf("\tConfigured FSS User Priority Range: -%d through %d\n",
208	    ((fssinfo_t *)pcinfo.pc_clinfo)->fss_maxupri,
209	    ((fssinfo_t *)pcinfo.pc_clinfo)->fss_maxupri);
210}
211
212/*
213 * Read a list of pids from stdin and print the user priority and user
214 * priority limit for each of the corresponding processes.
215 */
216static int
217print_fssprocs(void)
218{
219	pid_t *pidlist;
220	size_t numread;
221	int i;
222	char clname[PC_CLNMSZ];
223	pri_t fssuprilim;
224	pri_t fssupri;
225	int error = 0;
226
227	/*
228	 * Read a list of pids from stdin.
229	 */
230	if ((pidlist = read_pidlist(&numread, stdin)) == NULL)
231		fatalerr("%s: Can't read pidlist.\n", basenm);
232
233	(void) printf("FAIR SHARING PROCESSES:\n"
234	    "    PID    FSSUPRILIM   FSSUPRI\n");
235
236	if (numread == 0)
237		fatalerr("%s: No pids on input\n", basenm);
238
239	for (i = 0; i < numread; i++) {
240		(void) printf("%7ld", pidlist[i]);
241		if (priocntl(P_PID, pidlist[i], PC_GETXPARMS, "FSS",
242		    FSS_KY_UPRI, &fssupri,
243		    FSS_KY_UPRILIM, &fssuprilim, 0) != -1) {
244			(void) printf("    %5d       %5d\n",
245			    fssuprilim, fssupri);
246		} else {
247			error = 1;
248
249			if (priocntl(P_PID, pidlist[i], PC_GETXPARMS, NULL,
250			    PC_KY_CLNAME, clname, 0) == -1 &&
251			    strcmp(clname, "FSS"))
252				/*
253				 * Process from some class other than fair
254				 * sharing. It has probably changed class while
255				 * priocntl command was executing (otherwise
256				 * we wouldn't have been passed its pid).
257				 * Print the little we know about it.
258				 */
259				(void) printf("\tChanged to class %s while"
260				    " priocntl command executing\n", clname);
261			else
262				(void) printf("\tCan't get FSS user priority"
263				    "\n");
264		}
265	}
266
267	free_pidlist(pidlist);
268	return (error);
269}
270
271/*
272 * Call priocntl() with command codes PC_SETXPARMS or PC_GETXPARMS.  The
273 * first parameter behind the command code is always the class name.
274 * Each parameter is headed by a key, which detemines the meanin of the
275 * following value.  There is maximum FSS_KEYCNT == 2 of (key, value) pairs.
276 */
277static int
278fss_priocntl(idtype_t idtype, id_t id, int cmd, char *clname, uintptr_t *argsp)
279{
280	return (priocntl(idtype, id, cmd, clname, argsp[0], argsp[1],
281	    argsp[2], argsp[3], 0));
282}
283
284/*
285 * Set all processes in the set specified by idtype/idargv to fair-sharing
286 * (if they aren't already fair-sharing) and set their user priority limit
287 * and user priority to those specified by fssuprilim and fssupri.
288 */
289static int
290set_fssprocs(idtype_t idtype, int idargc, char **idargv, uint_t cflags,
291    short fssuprilim, short fssupri)
292{
293	pcinfo_t pcinfo;
294	uintptr_t args[2 * FSS_KEYCNT + 1];
295	uintptr_t *argsp = &args[0];
296	pri_t maxupri;
297	char idtypnm[PC_IDTYPNMSZ];
298	int i;
299	int error = 0;
300	id_t id;
301
302	/*
303	 * Get the fair sharing class ID and max configured user priority.
304	 */
305	(void) strcpy(pcinfo.pc_clname, "FSS");
306	if (priocntl(0, 0, PC_GETCID, (caddr_t)&pcinfo) == -1)
307		fatalerr("%s: Can't get FSS class ID, priocntl system call "
308		    "failed (%s)\n", basenm, strerror(errno));
309	maxupri = ((fssinfo_t *)pcinfo.pc_clinfo)->fss_maxupri;
310
311	/*
312	 * Validate the fssuprilim and fssupri arguments.
313	 */
314	if ((cflags & FSS_DOUPRILIM) != 0) {
315		if (fssuprilim > maxupri || fssuprilim < -maxupri)
316			fatalerr("%s: Specified user priority limit %d out of "
317			    "configured range\n", basenm, fssuprilim);
318		ADDKEYVAL(argsp, FSS_KY_UPRILIM, fssuprilim);
319	}
320
321	if ((cflags & FSS_DOUPRI) != 0) {
322		if (fssupri > maxupri || fssupri < -maxupri)
323			fatalerr("%s: Specified user priority %d out of "
324			    "configured range\n", basenm, fssupri);
325		ADDKEYVAL(argsp, FSS_KY_UPRI, fssupri);
326	}
327	*argsp = 0;
328
329	if (idtype == P_ALL) {
330		if (fss_priocntl(P_ALL, 0, PC_SETXPARMS, "FSS", args) == -1) {
331			if (errno == EPERM) {
332				(void) fprintf(stderr, "Permissions error "
333				    "encountered on one or more processes.\n");
334				error = 1;
335			} else {
336				fatalerr("%s: Can't reset fair sharing "
337				    "parameters\npriocntl system call failed "
338				    "(%s)\n", basenm, strerror(errno));
339			}
340		} else if ((cflags & (FSS_DOUPRILIM|FSS_DOUPRI)) ==
341		    FSS_DOUPRI) {
342			(void) verifyupri(idtype, 0, "FSS", FSS_KY_UPRILIM,
343			    fssupri, basenm);
344		}
345	} else if (idargc == 0) {
346		if (fss_priocntl(idtype, P_MYID, PC_SETXPARMS, "FSS",
347		    args) == -1) {
348			if (errno == EPERM) {
349				(void) idtyp2str(idtype, idtypnm);
350				(void) fprintf(stderr, "Permissions error "
351				    "encountered on current %s.\n", idtypnm);
352				error = 1;
353			} else {
354				fatalerr("%s: Can't reset fair sharing "
355				    "parameters\npriocntl system call failed "
356				    "(%s)\n", basenm, strerror(errno));
357			}
358		} else if ((cflags & (FSS_DOUPRILIM|FSS_DOUPRI)) ==
359		    FSS_DOUPRI && getmyid(idtype, &id) != -1) {
360			(void) verifyupri(idtype, id, "FSS", FSS_KY_UPRILIM,
361			    fssupri, basenm);
362		}
363	} else {
364		(void) idtyp2str(idtype, idtypnm);
365		for (i = 0; i < idargc; i++) {
366			if (idtype == P_CID) {
367				(void) strcpy(pcinfo.pc_clname, idargv[i]);
368				if (priocntl(0, 0, PC_GETCID,
369				    (caddr_t)&pcinfo) == -1)
370					fatalerr("%s: Invalid or unconfigured "
371					    "class %s, priocntl system call "
372					    "failed (%s)\n",
373					    basenm, pcinfo.pc_clname,
374					    strerror(errno));
375				id = pcinfo.pc_cid;
376			} else {
377				id = (id_t)str2num(idargv[i], INT_MIN, INT_MAX);
378				if (errno)
379					fatalerr("%s: Invalid id \"%s\"\n",
380					    basenm, idargv[i]);
381			}
382
383			if (fss_priocntl(idtype, id, PC_SETXPARMS, "FSS",
384			    args) == -1) {
385				if (errno == EPERM) {
386					(void) fprintf(stderr, "Permissions "
387					    "error encountered on %s %s.\n",
388					    idtypnm, idargv[i]);
389					error = 1;
390				} else {
391					fatalerr("%s: Can't reset fair sharing"
392					    " parameters\npriocntl system call"
393					    " failed (%s)\n",
394					    basenm, strerror(errno));
395				}
396			} else if ((cflags & (FSS_DOUPRILIM|FSS_DOUPRI)) ==
397			    FSS_DOUPRI) {
398				(void) verifyupri(idtype, id, "FSS",
399				    FSS_KY_UPRILIM, fssupri, basenm);
400			}
401		}
402	}
403
404	return (error);
405}
406
407/*
408 * Execute the command pointed to by cmdargv as a fair-sharing process
409 * with the user priority limit given by fssuprilim and user priority fssupri.
410 */
411static void
412exec_fsscmd(char **cmdargv, uint_t cflags, pri_t fssuprilim, pri_t fssupri)
413{
414	pcinfo_t pcinfo;
415	uintptr_t args[2 * FSS_KEYCNT + 1];
416	uintptr_t *argsp = &args[0];
417	pri_t maxupri;
418	pri_t uprilim;
419
420	/*
421	 * Get the fair sharing class ID and max configured user priority.
422	 */
423	(void) strcpy(pcinfo.pc_clname, "FSS");
424	if (priocntl(0, 0, PC_GETCID, (caddr_t)&pcinfo) == -1)
425		fatalerr("%s: Can't get FSS class ID, priocntl system call "
426		    "failed (%s)\n", basenm, strerror(errno));
427	maxupri = ((fssinfo_t *)pcinfo.pc_clinfo)->fss_maxupri;
428
429	if ((cflags & FSS_DOUPRILIM) != 0) {
430		if (fssuprilim > maxupri || fssuprilim < -maxupri)
431			fatalerr("%s: Specified user priority limit %d out of "
432			    "configured range\n", basenm, fssuprilim);
433		ADDKEYVAL(argsp, FSS_KY_UPRILIM, fssuprilim);
434	}
435
436	if ((cflags & FSS_DOUPRI) != 0) {
437		if (fssupri > maxupri || fssupri < -maxupri)
438			fatalerr("%s: Specified user priority %d out of "
439			    "configured range\n", basenm, fssupri);
440		ADDKEYVAL(argsp, FSS_KY_UPRI, fssupri);
441	}
442	*argsp = 0;
443
444	if (fss_priocntl(P_PID, P_MYID, PC_SETXPARMS, "FSS", args) == -1)
445		fatalerr("%s: Can't reset fair sharing parameters\n"
446		    "priocntl system call failed (%s)\n",
447		    basenm, strerror(errno));
448
449	if ((cflags & (FSS_DOUPRILIM|FSS_DOUPRI)) == FSS_DOUPRI) {
450		if (priocntl(P_PID, P_MYID, PC_GETXPARMS, "FSS",
451		    FSS_KY_UPRILIM, &uprilim, 0) != -1 && fssupri > uprilim)
452			(void) fprintf(stderr,
453			    "%s: Specified user priority %d exceeds"
454			    " limit %d; set to %d (pid %d)\n",
455			    basenm, fssupri, uprilim, uprilim, (int)getpid());
456	}
457
458	(void) execvp(cmdargv[0], cmdargv);
459	fatalerr("%s: Can't execute %s, exec failed (%s)\n",
460	    basenm, cmdargv[0], strerror(errno));
461}
462