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