xref: /illumos-gate/usr/src/cmd/priocntl/fsspriocntl.c (revision 3ce5372277f4657ad0e52d36c979527c4ca22de2)
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 
57 static void print_fssinfo(void);
58 static int print_fssprocs(void);
59 static int fss_priocntl(idtype_t, id_t, int, char *, uintptr_t *);
60 static int set_fssprocs(idtype_t, int, char **, uint_t, pri_t, pri_t);
61 static void exec_fsscmd(char **, uint_t, pri_t, pri_t);
62 
63 static 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 
71 static char	cmdpath[MAXPATHLEN];
72 static char	basenm[BASENMSZ];
73 
74 
75 int
76 main(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  */
195 static void
196 print_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  */
216 static int
217 print_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  */
277 static int
278 fss_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  */
289 static int
290 set_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  */
411 static void
412 exec_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