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 (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright 2008 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
31 #include <stdio.h>
32 #include <string.h>
33 #include <stdlib.h>
34 #include <unistd.h>
35 #include <sys/types.h>
36 #include <sys/time.h>
37 #include <sys/procset.h>
38 #include <sys/priocntl.h>
39 #include <sys/rtpriocntl.h>
40 #include <sys/param.h>
41 #include <signal.h>
42 #include <libgen.h>
43 #include <limits.h>
44 #include <errno.h>
45
46 #include "priocntl.h"
47
48 /*
49 * This file contains the class specific code implementing
50 * the real-time priocntl sub-command.
51 */
52
53 #define ADDKEYVAL(p, k, v) { (p[0]) = (k); (p[1]) = (v); p += 2; }
54
55 #define RT_KEYCNT 4 /* maximal number of (key, value) pairs */
56
57 /*
58 * control flags
59 */
60 #define RT_DOPRI 0x01 /* change priority */
61 #define RT_DOTQ 0x02 /* change RT time quantum */
62 #define RT_DOSIG 0x10 /* change RT time quantum signal */
63
64
65 static void print_rtinfo(void);
66 static int print_rtprocs(void);
67 static int rt_priocntl(idtype_t, id_t, int, char *, uintptr_t *);
68 static int set_rtprocs(idtype_t, int, char **, uint_t, pri_t, long,
69 long, int);
70 static void exec_rtcmd(char **, uint_t, pri_t, long, long, int);
71
72
73 static char usage[] =
74 "usage: priocntl -l\n"
75 " priocntl -d [-i idtype] [idlist]\n"
76 " priocntl -s [-c RT] [-p rtpri] [-t tqntm [-r res]] [-q tqsig]\n"
77 " [-i idtype] [idlist]\n"
78 " priocntl -e [-c RT] [-p rtpri] [-t tqntm [-r res]] [-q tqsig]\n"
79 " command [argument(s)]\n";
80
81 static char cmdpath[MAXPATHLEN];
82 static char basenm[BASENMSZ];
83
84
85 int
main(int argc,char * argv[])86 main(int argc, char *argv[])
87 {
88 int c;
89 int lflag, dflag, sflag, pflag;
90 int tflag, rflag, eflag, iflag, qflag;
91 pri_t rtpri;
92 long tqntm;
93 long res;
94 int tqsig;
95 char *idtypnm;
96 idtype_t idtype;
97 int idargc;
98 uint_t cflags;
99
100 (void) strlcpy(cmdpath, argv[0], MAXPATHLEN);
101 (void) strlcpy(basenm, basename(argv[0]), BASENMSZ);
102 lflag = dflag = sflag = pflag = 0;
103 tflag = rflag = eflag = iflag = qflag = 0;
104 while ((c = getopt(argc, argv, "ldsp:t:r:q:ec:i:")) != -1) {
105 switch (c) {
106
107 case 'l':
108 lflag++;
109 break;
110
111 case 'd':
112 dflag++;
113 break;
114
115 case 's':
116 sflag++;
117 break;
118
119 case 'p':
120 pflag++;
121 rtpri = (pri_t)str2num(optarg, SHRT_MIN, SHRT_MAX);
122 if (errno)
123 fatalerr("%s: Specified real time priority %s"
124 " out of configured range\n",
125 basenm, optarg);
126 break;
127
128 case 't':
129 tflag++;
130 tqntm = str2num(optarg, 1, INT_MAX);
131 if (errno)
132 fatalerr("%s: Invalid time quantum specified;"
133 " time quantum must be positive\n", basenm);
134 break;
135
136 case 'r':
137 rflag++;
138 res = str2num(optarg, 1, 1000000000);
139 if (errno)
140 fatalerr("%s: Invalid resolution specified;"
141 " resolution must be between"
142 " 1 and 1,000,000,000\n", basenm);
143
144 break;
145
146 case 'q':
147 qflag++;
148 if (str2sig(optarg, &tqsig) != 0)
149 fatalerr("%s: Invalid real time quantum signal"
150 " specified\n", basenm);
151 break;
152
153 case 'e':
154 eflag++;
155 break;
156
157 case 'c':
158 if (strcmp(optarg, "RT") != 0)
159 fatalerr("error: %s executed for %s class, %s"
160 " is actually sub-command for RT class\n",
161 cmdpath, optarg, cmdpath);
162 break;
163
164 case 'i':
165 iflag++;
166 idtypnm = optarg;
167 break;
168
169 case '?':
170 fatalerr(usage);
171
172 default:
173 break;
174 }
175 }
176
177 if (lflag) {
178 if (dflag || sflag || pflag || tflag || rflag || eflag ||
179 iflag || qflag)
180 fatalerr(usage);
181
182 print_rtinfo();
183
184 } else if (dflag) {
185 if (lflag || sflag || pflag || tflag || rflag || eflag || qflag)
186 fatalerr(usage);
187
188 return (print_rtprocs());
189
190 } else if (sflag) {
191 if (lflag || dflag || eflag)
192 fatalerr(usage);
193
194 if (iflag) {
195 if (str2idtyp(idtypnm, &idtype) == -1)
196 fatalerr("%s: Bad idtype %s\n", basenm,
197 idtypnm);
198 } else {
199 idtype = P_PID;
200 }
201
202 cflags = (pflag ? RT_DOPRI : 0);
203
204 if (tflag)
205 cflags |= RT_DOTQ;
206
207 if (rflag == 0)
208 res = 1000;
209
210 if (optind < argc)
211 idargc = argc - optind;
212 else
213 idargc = 0;
214
215 if (qflag)
216 cflags |= RT_DOSIG;
217
218 return (set_rtprocs(idtype, idargc, &argv[optind], cflags,
219 rtpri, tqntm, res, tqsig));
220
221 } else if (eflag) {
222 if (lflag || dflag || sflag || iflag)
223 fatalerr(usage);
224
225 cflags = (pflag ? RT_DOPRI : 0);
226
227 if (tflag)
228 cflags |= RT_DOTQ;
229
230 if (rflag == 0)
231 res = 1000;
232
233 if (qflag)
234 cflags |= RT_DOSIG;
235
236 exec_rtcmd(&argv[optind], cflags, rtpri, tqntm, res, tqsig);
237
238 } else {
239 fatalerr(usage);
240 }
241
242 return (0);
243 }
244
245
246 /*
247 * Print our class name and the configured user priority range.
248 */
249 static void
print_rtinfo(void)250 print_rtinfo(void)
251 {
252 pcinfo_t pcinfo;
253
254 (void) strcpy(pcinfo.pc_clname, "RT");
255
256 (void) printf("RT (Real Time)\n");
257
258 if (priocntl(0, 0, PC_GETCID, (caddr_t)&pcinfo) == -1)
259 fatalerr("\tCan't get maximum configured RT priority\n");
260
261 (void) printf("\tConfigured RT User Priority Range: 0 through %d\n",
262 ((rtinfo_t *)pcinfo.pc_clinfo)->rt_maxpri);
263 }
264
265
266 /*
267 * Read a list of pids from stdin and print the real-time priority and time
268 * quantum (in millisecond resolution) for each of the corresponding processes.
269 */
270 static int
print_rtprocs(void)271 print_rtprocs(void)
272 {
273 pid_t *pidlist;
274 size_t numread;
275 int i;
276 char clname[PC_CLNMSZ];
277 pri_t rt_pri;
278 uint_t rt_tqsecs;
279 int rt_tqnsecs;
280 int rt_tqsig;
281 int error = 0;
282
283 /*
284 * Read a list of pids from stdin.
285 */
286 if ((pidlist = read_pidlist(&numread, stdin)) == NULL)
287 fatalerr("%s: Can't read pidlist.\n", basenm);
288
289 (void) printf("REAL TIME PROCESSES:\n"
290 " PID RTPRI TQNTM TQSIG\n");
291
292 if (numread == 0)
293 fatalerr("%s: No pids on input\n", basenm);
294
295 for (i = 0; i < numread; i++) {
296 (void) printf("%7ld", pidlist[i]);
297 if (priocntl(P_PID, pidlist[i], PC_GETXPARMS, "RT",
298 RT_KY_TQSECS, &rt_tqsecs, RT_KY_TQNSECS, &rt_tqnsecs,
299 RT_KY_PRI, &rt_pri, RT_KY_TQSIG, &rt_tqsig, 0) != -1) {
300 (void) printf(" %5d", rt_pri);
301 if (rt_tqnsecs == RT_TQINF)
302 (void) printf(" RT_TQINF");
303 else
304 (void) printf(" %11lld",
305 (longlong_t)rt_tqsecs * 1000 +
306 rt_tqnsecs / 1000000);
307
308 (void) printf(" %3d\n", rt_tqsig);
309 } else {
310 error = 1;
311
312 if (priocntl(P_PID, pidlist[i], PC_GETXPARMS, NULL,
313 PC_KY_CLNAME, clname, 0) != -1 &&
314 strcmp(clname, "RT"))
315 /*
316 * Process from some class other than real time.
317 * It has probably changed class while priocntl
318 * command was executing (otherwise we wouldn't
319 * have been passed its pid). Print the little
320 * we know about it.
321 */
322 (void) printf("\tChanged to class %s while"
323 " priocntl command executing\n", clname);
324 else
325 (void) printf("\tCan't get real time"
326 " parameters\n");
327 }
328 }
329
330 free_pidlist(pidlist);
331 return (error);
332 }
333
334
335 /*
336 * Call priocntl() with command codes PC_SETXPARMS or PC_GETXPARMS.
337 * The first parameter behind the command code is always the class name.
338 * Each parameter is headed by a key, which determines the meaning of the
339 * following value. There are maximal RT_KEYCNT = 4 (key, value) pairs.
340 */
341 static int
rt_priocntl(idtype_t idtype,id_t id,int cmd,char * clname,uintptr_t * argsp)342 rt_priocntl(idtype_t idtype, id_t id, int cmd, char *clname, uintptr_t *argsp)
343 {
344 return (priocntl(idtype, id, cmd, clname, argsp[0], argsp[1],
345 argsp[2], argsp[3], argsp[4], argsp[5], argsp[6], argsp[7], 0));
346 }
347
348
349 /*
350 * Set all processes in the set specified by idtype/idargv to real time
351 * (if they aren't already real time) and set their real-time priority,
352 * real-time quantum and real-time quantum signal to those specified by
353 * rtpri, tqntm/res and rtqsig.
354 */
355 static int
set_rtprocs(idtype_t idtype,int idargc,char ** idargv,uint_t cflags,pri_t rtpri,long tqntm,long res,int rtqsig)356 set_rtprocs(idtype_t idtype, int idargc, char **idargv, uint_t cflags,
357 pri_t rtpri, long tqntm, long res, int rtqsig)
358 {
359 pcinfo_t pcinfo;
360 uintptr_t args[2*RT_KEYCNT+1];
361 uintptr_t *argsp = &args[0];
362 pri_t maxrtpri;
363 hrtimer_t hrtime;
364 char idtypnm[PC_IDTYPNMSZ];
365 int i;
366 id_t id;
367 int error = 0;
368
369
370 /*
371 * Get the real time class ID and max configured RT priority.
372 */
373 (void) strcpy(pcinfo.pc_clname, "RT");
374 if (priocntl(0, 0, PC_GETCID, (caddr_t)&pcinfo) == -1)
375 fatalerr("%s: Can't get RT class ID, priocntl system call"
376 " failed with errno %d\n", basenm, errno);
377 maxrtpri = ((rtinfo_t *)pcinfo.pc_clinfo)->rt_maxpri;
378
379 /*
380 * Validate the rtpri and res arguments.
381 */
382 if ((cflags & RT_DOPRI) != 0) {
383 if (rtpri > maxrtpri || rtpri < 0)
384 fatalerr("%s: Specified real time priority %d out of"
385 " configured range\n", basenm, rtpri);
386 ADDKEYVAL(argsp, RT_KY_PRI, rtpri);
387 }
388
389 if ((cflags & RT_DOTQ) != 0) {
390 hrtime.hrt_secs = 0;
391 hrtime.hrt_rem = tqntm;
392 hrtime.hrt_res = res;
393 if (_hrtnewres(&hrtime, NANOSEC, HRT_RNDUP) == -1)
394 fatalerr("%s: Can't convert resolution.\n", basenm);
395 ADDKEYVAL(argsp, RT_KY_TQSECS, hrtime.hrt_secs);
396 ADDKEYVAL(argsp, RT_KY_TQNSECS, hrtime.hrt_rem);
397 }
398
399 if ((cflags & RT_DOSIG) != 0)
400 ADDKEYVAL(argsp, RT_KY_TQSIG, rtqsig);
401 *argsp = 0;
402
403 if (idtype == P_ALL) {
404 if (rt_priocntl(P_ALL, 0, PC_SETXPARMS, "RT", args) == -1) {
405 if (errno == EPERM) {
406 (void) fprintf(stderr,
407 "Permissions error encountered"
408 " on one or more processes.\n");
409 error = 1;
410 } else {
411 fatalerr("%s: Can't reset real time parameters"
412 "\npriocntl system call failed with"
413 " errno %d\n", basenm, errno);
414 }
415 }
416 } else if (idargc == 0) {
417 if (rt_priocntl(idtype, P_MYID, PC_SETXPARMS, "RT",
418 args) == -1) {
419 if (errno == EPERM) {
420 (void) idtyp2str(idtype, idtypnm);
421 (void) fprintf(stderr, "Permissions error"
422 " encountered on current %s.\n", idtypnm);
423 error = 1;
424 } else {
425 fatalerr("%s: Can't reset real time parameters"
426 "\npriocntl system call failed with"
427 " errno %d\n", basenm, errno);
428 }
429 }
430 } else {
431 (void) idtyp2str(idtype, idtypnm);
432 for (i = 0; i < idargc; i++) {
433 if (idtype == P_CID) {
434 (void) strcpy(pcinfo.pc_clname, idargv[i]);
435 if (priocntl(0, 0, PC_GETCID,
436 (caddr_t)&pcinfo) == -1)
437 fatalerr("%s: Invalid or unconfigured"
438 " class %s, priocntl system call"
439 " failed with errno %d\n",
440 basenm, pcinfo.pc_clname, errno);
441 id = pcinfo.pc_cid;
442 } else {
443 id = (id_t)str2num(idargv[i], INT_MIN, INT_MAX);
444 if (errno)
445 fatalerr("%s: Invalid id \"%s\"\n",
446 basenm, idargv[i]);
447 }
448
449 if (rt_priocntl(idtype, id, PC_SETXPARMS, "RT",
450 args) == -1) {
451 if (errno == EPERM) {
452 (void) fprintf(stderr,
453 "Permissions error encountered on"
454 " %s %s.\n", idtypnm, idargv[i]);
455 error = 1;
456 } else {
457 fatalerr("%s: Can't reset real time"
458 " parameters\npriocntl system call"
459 " failed with errno %d\n",
460 basenm, errno);
461 }
462 }
463 }
464 }
465
466 return (error);
467 }
468
469
470 /*
471 * Execute the command pointed to by cmdargv as a real-time process
472 * with real time priority rtpri, quantum tqntm/res and quantum signal rtqsig.
473 */
474 static void
exec_rtcmd(char ** cmdargv,uint_t cflags,pri_t rtpri,long tqntm,long res,int rtqsig)475 exec_rtcmd(char **cmdargv, uint_t cflags, pri_t rtpri, long tqntm, long res,
476 int rtqsig)
477 {
478 pcinfo_t pcinfo;
479 uintptr_t args[2*RT_KEYCNT+1];
480 uintptr_t *argsp = &args[0];
481 pri_t maxrtpri;
482 hrtimer_t hrtime;
483
484 /*
485 * Get the real time class ID and max configured RT priority.
486 */
487 (void) strcpy(pcinfo.pc_clname, "RT");
488 if (priocntl(0, 0, PC_GETCID, (caddr_t)&pcinfo) == -1)
489 fatalerr("%s: Can't get RT class ID, priocntl system call"
490 " failed with errno %d\n", basenm, errno);
491 maxrtpri = ((rtinfo_t *)pcinfo.pc_clinfo)->rt_maxpri;
492
493 if ((cflags & RT_DOPRI) != 0) {
494 if (rtpri > maxrtpri || rtpri < 0)
495 fatalerr("%s: Specified real time priority %d out of"
496 " configured range\n", basenm, rtpri);
497 ADDKEYVAL(argsp, RT_KY_PRI, rtpri);
498 }
499
500 if ((cflags & RT_DOTQ) != 0) {
501 hrtime.hrt_secs = 0;
502 hrtime.hrt_rem = tqntm;
503 hrtime.hrt_res = res;
504 if (_hrtnewres(&hrtime, NANOSEC, HRT_RNDUP) == -1)
505 fatalerr("%s: Can't convert resolution.\n", basenm);
506 ADDKEYVAL(argsp, RT_KY_TQSECS, hrtime.hrt_secs);
507 ADDKEYVAL(argsp, RT_KY_TQNSECS, hrtime.hrt_rem);
508 }
509
510 if ((cflags & RT_DOSIG) != 0)
511 ADDKEYVAL(argsp, RT_KY_TQSIG, rtqsig);
512 *argsp = 0;
513
514 if (rt_priocntl(P_PID, P_MYID, PC_SETXPARMS, "RT", args) == -1)
515 fatalerr("%s: Can't reset real time parameters\n"
516 "priocntl system call failed with errno %d\n",
517 basenm, errno);
518
519 (void) execvp(cmdargv[0], cmdargv);
520 fatalerr("%s: Can't execute %s, exec failed with errno %d\n",
521 basenm, cmdargv[0], errno);
522 }
523