1 /*
2  * priocntl - process scheduler control
3  *
4  * Gunnar Ritter, Freiburg i. Br., Germany, March 2003.
5  */
6 /*
7  * Copyright (c) 2003 Gunnar Ritter
8  *
9  * This software is provided 'as-is', without any express or implied
10  * warranty. In no event will the authors be held liable for any damages
11  * arising from the use of this software.
12  *
13  * Permission is granted to anyone to use this software for any purpose,
14  * including commercial applications, and to alter it and redistribute
15  * it freely, subject to the following restrictions:
16  *
17  * 1. The origin of this software must not be misrepresented; you must not
18  *    claim that you wrote the original software. If you use this software
19  *    in a product, an acknowledgment in the product documentation would be
20  *    appreciated but is not required.
21  *
22  * 2. Altered source versions must be plainly marked as such, and must not be
23  *    misrepresented as being the original software.
24  *
25  * 3. This notice may not be removed or altered from any source distribution.
26  */
27 #if defined (__linux__) || defined (__FreeBSD__)
28 
29 #if __GNUC__ >= 3 && __GNUC_MINOR__ >= 4 || __GNUC__ >= 4
30 #define	USED	__attribute__ ((used))
31 #elif defined __GNUC__
32 #define	USED	__attribute__ ((unused))
33 #else
34 #define	USED
35 #endif
36 #if defined (S42)
37 static const char sccsid[] USED = "@(#)priocntl_s42.sl	1.19 (gritter) 7/23/06";
38 #else
39 static const char sccsid[] USED = "@(#)priocntl.sl	1.19 (gritter) 7/23/06";
40 #endif
41 
42 #include	<sys/time.h>
43 #include	<sys/resource.h>
44 #include	<unistd.h>
45 #include	<stdio.h>
46 #include	<string.h>
47 #include	<stdlib.h>
48 #include	<errno.h>
49 #include	<sched.h>
50 #include	<libgen.h>
51 #include	<dirent.h>
52 #include	<ctype.h>
53 
54 #ifndef	SCHED_BATCH
55 #define	SCHED_BATCH	3
56 #endif
57 #ifndef	SCHED_ISO
58 #define	SCHED_ISO	4
59 #endif
60 
61 #define	eq(p, q)	(strcmp(p, q) == 0)
62 
63 struct	idnode {
64 	struct idnode	*id_nxt;
65 	int	id_val;
66 	int	id_upri;
67 	int	id_class;
68 	unsigned long	id_tqntm;
69 };
70 
71 struct	proc {
72 	int	p_pid;
73 	int	p_ppid;
74 	int	p_pgid;
75 	int	p_sid;
76 	int	p_class;
77 	int	p_uid;
78 	int	p_gid;
79 	int	p_upri;
80 	unsigned long	p_tqntm;
81 };
82 
83 static enum {
84 	ID_PID,
85 	ID_PPID,
86 	ID_PGID,
87 	ID_SID,
88 	ID_CLASS,
89 	ID_UID,
90 	ID_GID,
91 	ID_ALL
92 } idtype = ID_PID;
93 
94 enum	valtype {
95 	VT_CHAR,
96 	VT_INT,
97 	VT_UINT,
98 	VT_LONG,
99 	VT_ULONG
100 };
101 
102 union	value {
103 	char	v_char;
104 	int	v_int;
105 	unsigned int	v_uint;
106 	long	v_long;
107 	unsigned long	v_ulong;
108 };
109 
110 static unsigned		errcnt;		/* count of errors */
111 static int		action;		/* one of l, d, s, e */
112 static int		cflag = -1;	/* selected class */
113 static int		iflag;		/* an id was specified */
114 static int		pflag = -1;	/* numeric argument to -p option */
115 static char		*progname;	/* argv[0] to main() */
116 static struct idnode	*idlist;	/* list of id criteria */
117 static struct idnode	*proclist;	/* list of processes */
118 
119 static union value *
getval(char ** listp,enum valtype type,int separator,int sep2)120 getval(char **listp, enum valtype type, int separator, int sep2)
121 {
122 	char	*buf;
123 	static union value	v;
124 	const char	*cp, *op;
125 	char	*cq, *x;
126 
127 	if (**listp == '\0')
128 		return NULL;
129 	op = *listp;
130 	while (**listp != '\0') {
131 		if ((separator == ' ' ? isspace(**listp) :
132 					**listp == separator) ||
133 				**listp == sep2)
134 			break;
135 		(*listp)++;
136 	}
137 	buf = alloca(*listp - op + 1);
138 	for (cp = op, cq = buf; cp < *listp; cp++, cq++)
139 		*cq = *cp;
140 	*cq = '\0';
141 	if (**listp) {
142 		while ((separator == ' ' ?
143 				isspace(**listp) : **listp == separator) ||
144 				**listp == sep2)
145 			(*listp)++;
146 	}
147 	switch (type) {
148 	case VT_CHAR:
149 		if (buf[1] != '\0')
150 			return NULL;
151 		v.v_char = buf[0];
152 		break;
153 	case VT_INT:
154 		v.v_int = strtol(buf, &x, 10);
155 		if (*x != '\0')
156 			return NULL;
157 		break;
158 	case VT_UINT:
159 		v.v_uint = strtoul(buf, &x, 10);
160 		if (*x != '\0')
161 			return NULL;
162 		break;
163 	case VT_LONG:
164 		v.v_long = strtol(buf, &x, 10);
165 		if (*x != '\0')
166 			return NULL;
167 		break;
168 	case VT_ULONG:
169 		v.v_ulong = strtoul(buf, &x, 10);
170 		if (*x != '\0')
171 			return NULL;
172 		break;
173 	}
174 	return &v;
175 }
176 
177 static void *
srealloc(char * op,size_t size)178 srealloc(char *op, size_t size)
179 {
180 	char	*np;
181 
182 	if ((np = realloc(op, size)) == NULL) {
183 		write(2, "Out of memory\n", 14);
184 		exit(077);
185 	}
186 	return np;
187 }
188 
189 static void *
scalloc(size_t nmemb,size_t size)190 scalloc(size_t nmemb, size_t size)
191 {
192 	void	*p;
193 
194 	if ((p = calloc(nmemb, size)) == NULL) {
195 		write(2, "Out of memory\n", 14);
196 		exit(077);
197 	}
198 	return p;
199 }
200 
201 static void
usage(void)202 usage(void)
203 {
204 	fprintf(stderr, "usage:\t%s -l\n\
205 \t%s -d [-i idtype] [idlist]\n\
206 \t%s -s [-c class] [c.s.o.] [-i idtype] [idlist]\n\
207 \t%s -e [-c class] [c.s.o.] command [argument(s)]\n",
208 	progname, progname, progname, progname);
209 	exit(2);
210 }
211 
212 static void
addproc(struct proc * sp)213 addproc(struct proc *sp)
214 {
215 	struct idnode	*id, *iq;
216 
217 	id = calloc(1, sizeof *id);
218 	id->id_val = sp->p_pid;
219 	id->id_upri = sp->p_upri;
220 	id->id_class = sp->p_class;
221 	id->id_tqntm = sp->p_tqntm;
222 	if (proclist) {
223 		for (iq = proclist; iq->id_nxt; iq = iq->id_nxt);
224 		iq->id_nxt = id;
225 	} else
226 		proclist = id;
227 }
228 
229 static void
tryproc(int val,struct proc * sp)230 tryproc(int val, struct proc *sp)
231 {
232 	struct idnode	*id;
233 
234 	if (sp->p_pid == 1 && action == 's' && idtype != ID_PID)
235 		return;
236 	for (id = idlist; id; id = id->id_nxt) {
237 		if (id->id_val == val) {
238 			addproc(sp);
239 			return;
240 		}
241 	}
242 }
243 
244 #define	GETVAL(a)	if ((v = getval(&cp, (a), ' ', 0)) == NULL) { \
245 				free(sp); \
246 				return NULL; \
247 			}
248 
249 #define	GETVAL_COMMA(a)	if ((v = getval(&cp, (a), ' ', ',')) == NULL) { \
250 				free(sp); \
251 				return NULL; \
252 			}
253 
254 #if defined (__linux__)
255 static struct proc *
getproc(int pid)256 getproc(int pid)
257 {
258 	struct proc	*sp;
259 	static char	*buf;
260 	static size_t	buflen;
261 	char	fn[256];
262 	char	line[2049];
263 	FILE	*fp;
264 	union value	*v;
265 	char	*cp, *cq, *ce;
266 	size_t	sz, sc;
267 	int	c;
268 
269 	sp = scalloc(1, sizeof *sp);
270 	snprintf(fn, sizeof fn, "/proc/%d/stat", pid);
271 	if ((fp = fopen(fn, "r")) == NULL) {
272 		free(sp);
273 		return NULL;
274 	}
275 	for (cp = buf; ; ) {
276 		const unsigned	chunk = 32;
277 
278 		if (buflen < (sz = cp - buf + chunk)) {
279 			sc = cp - buf;
280 			buf = srealloc(buf, buflen = sz);
281 			cp = &buf[sc];
282 		}
283 		if ((sz = fread(cp, 1, chunk, fp)) < chunk) {
284 			ce = &cp[sz - 1];
285 			break;
286 		}
287 		cp += chunk;
288 	}
289 	fclose(fp);
290 	if (*ce != '\n') {
291 		free(sp);
292 		return NULL;
293 	}
294 	*ce-- = '\0';
295 	cp = buf;
296 	GETVAL(VT_INT);
297 	sp->p_pid = v->v_int;
298 	if (*cp++ != '(') {
299 		free(sp);
300 		return NULL;
301 	}
302 	for (cq = ce; cq >= cp && *cq != ')'; cq--);
303 	if (cq < cp) {
304 		free(sp);
305 		return NULL;
306 	}
307 	*cq = '\0';
308 	cp = &cq[1];
309 	while (isspace(*cp))
310 		cp++;
311 	GETVAL(VT_CHAR);	/* state */
312 	if (v->v_char == 'Z') {
313 		free(sp);
314 		return NULL;
315 	}
316 	GETVAL(VT_INT);
317 	sp->p_ppid = v->v_int;
318 	GETVAL(VT_INT);
319 	sp->p_pgid = v->v_int;
320 	GETVAL(VT_INT);
321 	sp->p_sid = v->v_int;
322 	sp->p_class = sched_getscheduler(pid);
323 	if (sp->p_class == SCHED_OTHER || sp->p_class == SCHED_BATCH ||
324 			sp->p_class == SCHED_ISO) {
325 		GETVAL(VT_INT);		/* device */
326 		GETVAL(VT_INT);		/* tty_pgrp */
327 		GETVAL(VT_ULONG);	/* flags */
328 		GETVAL(VT_ULONG);	/* min_flt */
329 		GETVAL(VT_ULONG);	/* cmin_flt */
330 		GETVAL(VT_ULONG);	/* maj_flt */
331 		GETVAL(VT_ULONG);	/* cmaj_flt */
332 		GETVAL(VT_ULONG);	/* utime */
333 		GETVAL(VT_ULONG);	/* stime */
334 		GETVAL(VT_ULONG);	/* cutime */
335 		GETVAL(VT_ULONG);	/* cstime */
336 		GETVAL(VT_LONG);	/* internal priority */
337 		GETVAL(VT_LONG);	/* nice */
338 		sp->p_upri = 0 - v->v_long;
339 	} else {
340 		struct sched_param	scp;
341 		sched_getparam(pid, &scp);
342 		sp->p_upri = scp.sched_priority;
343 		if (sp->p_class == SCHED_RR) {
344 			struct timespec	ts;
345 			if (sched_rr_get_interval(pid, &ts) == 0) {
346 				sp->p_tqntm = ts.tv_sec * 1000;
347 				sp->p_tqntm += ts.tv_nsec / 1000000;
348 			} else
349 				sp->p_tqntm = 150;
350 		}
351 	}
352 	snprintf(fn, sizeof fn, "/proc/%d/status", pid);
353 	if ((fp = fopen(fn, "r")) == NULL) {
354 		free(sp);
355 		return NULL;
356 	}
357 	c = 0;
358 	while (fgets(line, sizeof line, fp) != NULL) {
359 		if (strncmp(line, "Uid:", 4) == 0) {
360 			cp = &line[4];
361 			while (isspace(*cp))
362 				cp++;
363 			if ((v = getval(&cp, VT_INT, ' ', 0)) == NULL) {
364 				fclose(fp);
365 				free(sp);
366 				return NULL;
367 			}
368 			if ((v = getval(&cp, VT_INT, ' ', 0)) == NULL) {
369 				fclose(fp);
370 				free(sp);
371 				return NULL;
372 			}
373 			sp->p_uid = v->v_int;
374 			c++;
375 		}
376 		if (strncmp(line, "Gid:", 4) == 0) {
377 			cp = &line[4];
378 			while (isspace(*cp))
379 				cp++;
380 			if ((v = getval(&cp, VT_INT, ' ', 0)) == NULL) {
381 				fclose(fp);
382 				free(sp);
383 				return NULL;
384 			}
385 			if ((v = getval(&cp, VT_INT, ' ', 0)) == NULL) {
386 				fclose(fp);
387 				free(sp);
388 				return NULL;
389 			}
390 			sp->p_gid = v->v_int;
391 			c++;
392 		}
393 	}
394 	fclose(fp);
395 	if (c != 2) {
396 		free(sp);
397 		return NULL;
398 	}
399 	return sp;
400 }
401 
402 #elif defined (__FreeBSD__)
403 
404 #define	SKIPFIELD	while (*cp != ' ') cp++; while (*cp == ' ') cp++;
405 
406 static struct proc *
getproc(int pid)407 getproc(int pid)
408 {
409 	struct proc	*sp;
410 	static char	*buf;
411 	static size_t	buflen;
412 	struct sched_param	s;
413 	struct timespec	ts;
414 	char	fn[256];
415 	FILE	*fp;
416 	union value	*v;
417 	char	*cp, *ce;
418 	size_t	sz, sc;
419 
420 	sp = scalloc(1, sizeof *sp);
421 	snprintf(fn, sizeof fn, "/proc/%d/status", pid);
422 	if ((fp = fopen(fn, "r")) == NULL) {
423 		free(sp);
424 		return NULL;
425 	}
426 	for (cp = buf; ; ) {
427 		const unsigned	chunk = 32;
428 
429 		if (buflen < (sz = cp - buf + chunk)) {
430 			sc = cp - buf;
431 			buf = srealloc(buf, buflen = sz);
432 			cp = &buf[sc];
433 		}
434 		if ((sz = fread(cp, 1, chunk, fp)) < chunk) {
435 			ce = &cp[sz - 1];
436 			break;
437 		}
438 		cp += chunk;
439 	}
440 	fclose(fp);
441 	if (*ce != '\n') {
442 		free(sp);
443 		return NULL;
444 	}
445 	*ce-- = '\0';
446 	cp = buf;
447 	SKIPFIELD;	/* name */
448 	GETVAL(VT_INT);
449 	sp->p_pid = v->v_int;
450 	GETVAL(VT_INT);
451 	sp->p_ppid = v->v_int;
452 	GETVAL(VT_INT);
453 	sp->p_pgid = v->v_int;
454 	GETVAL(VT_INT);
455 	sp->p_sid = v->v_int;
456 	SKIPFIELD;	/* flags */
457 	SKIPFIELD;	/* start */
458 	SKIPFIELD;	/* user time */
459 	SKIPFIELD;	/* system time */
460 	SKIPFIELD;	/* wchan */
461 	SKIPFIELD;	/* euid */
462 	GETVAL(VT_INT);
463 	sp->p_uid = v->v_int;
464 	GETVAL_COMMA(VT_INT);
465 	sp->p_gid = v->v_int;
466 	switch (sp->p_class = sched_getscheduler(sp->p_pid)) {
467 	case SCHED_OTHER:
468 		sp->p_upri = 0 - getpriority(PRIO_PROCESS, sp->p_pid);
469 		break;
470 	case SCHED_RR:
471 		if (sched_rr_get_interval(pid, &ts) == 0) {
472 			sp->p_tqntm = ts.tv_sec * 1000;
473 			sp->p_tqntm += ts.tv_nsec / 1000000;
474 		} else
475 			sp->p_tqntm = 150;
476 		/*FALLTHRU*/
477 	case SCHED_FIFO:
478 		if (sched_getparam(sp->p_pid, &s) == 0) {
479 			sp->p_upri = s.sched_priority;
480 			break;
481 		}
482 		/*FALLTHRU*/
483 	default:
484 		free(sp);
485 		return NULL;
486 	}
487 	return sp;
488 }
489 #endif	/* __FreeBSD__ */
490 
491 static void
evalproc(int pid)492 evalproc(int pid)
493 {
494 	struct proc	*sp;
495 
496 	if ((sp = getproc(pid)) == NULL)
497 		return;
498 	switch (idtype) {
499 	case ID_PID:
500 		tryproc(sp->p_pid, sp);
501 		break;
502 	case ID_PPID:
503 		tryproc(sp->p_ppid, sp);
504 		break;
505 	case ID_PGID:
506 		tryproc(sp->p_pgid, sp);
507 		break;
508 	case ID_SID:
509 		tryproc(sp->p_sid, sp);
510 		break;
511 	case ID_CLASS:
512 		tryproc(sp->p_class, sp);
513 		break;
514 	case ID_UID:
515 		tryproc(sp->p_uid, sp);
516 		break;
517 	case ID_GID:
518 		tryproc(sp->p_gid, sp);
519 		break;
520 	case ID_ALL:
521 		addproc(sp);
522 	}
523 	free(sp);
524 }
525 
526 static void
getprocs(void)527 getprocs(void)
528 {
529 	DIR	*Dp;
530 	struct dirent	*dp;
531 
532 	if ((Dp = opendir("/proc")) == NULL) {
533 		fprintf(stderr, "%s: cannot open /proc, errno %d\n",
534 				progname, errno);
535 		exit(1);
536 	}
537 	while ((dp = readdir(Dp)) != NULL) {
538 		char	*x;
539 		int	pid;
540 
541 		if ((pid = strtol(dp->d_name, &x, 10)) >= 0 && *x == '\0')
542 			evalproc(pid);
543 	}
544 	closedir(Dp);
545 }
546 
547 static void
getidlist(char ** args)548 getidlist(char **args)
549 {
550 	if (idtype == ID_ALL)
551 		return;
552 	if (args == NULL || *args == NULL) {
553 		if (iflag) {
554 			idlist = scalloc(1, sizeof *idlist);
555 			switch (idtype) {
556 			case ID_PID:
557 				idlist->id_val = getpid();
558 				break;
559 			case ID_PPID:
560 				idlist->id_val = getppid();
561 				break;
562 			case ID_PGID:
563 				idlist->id_val = getpgid(0);
564 				break;
565 			case ID_SID:
566 				idlist->id_val = getsid(0);
567 				break;
568 			case ID_CLASS:
569 				idlist->id_val = sched_getscheduler(0);
570 				break;
571 			case ID_UID:
572 				idlist->id_val = geteuid();
573 				break;
574 			case ID_GID:
575 				idlist->id_val = getegid();
576 				break;
577 			}
578 			return;
579 		} else
580 			usage();
581 	}
582 	do {
583 		struct idnode	*id;
584 
585 		id = scalloc(1, sizeof *id);
586 		if (idtype == ID_CLASS) {
587 			if (eq(args[0], "TS"))
588 				id->id_val = SCHED_OTHER;
589 			else if (eq(args[0], "B"))
590 				id->id_val = SCHED_BATCH;
591 			else if (eq(args[0], "BA"))	/* old */
592 				id->id_val = SCHED_BATCH;
593 			else if (eq(args[0], "ISO"))
594 				id->id_val = SCHED_ISO;
595 			else if (eq(args[0], "IS"))	/* old */
596 				id->id_val = SCHED_ISO;
597 			else if (eq(args[0], "FF"))
598 				id->id_val = SCHED_FIFO;
599 			else if (eq(args[0], "FI"))	/* old */
600 				id->id_val = SCHED_FIFO;
601 			else if (eq(args[0], "RT") || eq(args[0], "FP"))
602 				id->id_val = SCHED_RR;
603 			else {
604 				fprintf(stderr, "%s: Invalid or unconfigured "
605 						"class %s in idlist - "
606 						"ignored\n",
607 						progname, args[0]);
608 				free(id);
609 				continue;
610 			}
611 		} else
612 			id->id_val = atoi(args[0]);
613 		id->id_nxt = idlist;
614 		idlist = id;
615 	} while (*++args != NULL);
616 }
617 
618 static void
noprocs(void)619 noprocs(void)
620 {
621 	fprintf(stderr, "%s: Process(es) not found.\n", progname);
622 	exit(1);
623 }
624 
625 static void
getproclist(char ** args)626 getproclist(char **args)
627 {
628 	getidlist(args);
629 	getprocs();
630 	if (proclist == NULL)
631 		noprocs();
632 }
633 
634 static void
listclasses(void)635 listclasses(void)
636 {
637 	const char	*batchclass;
638 	const char	*isoclass;
639 
640 	batchclass = sched_get_priority_max(SCHED_BATCH) < 0 ? "" : "\
641 B (Batch)\n\
642 \tConfigured BA User Priority Range: -19 through 20\n\
643 \n";
644 	isoclass = sched_get_priority_max(SCHED_ISO) < 0 ? "" : "\
645 ISO (Isochronous)\n\
646 \tConfigured IS User Priority Range: -19 through 20\n\
647 \n";
648 	fprintf(stderr, "\
649 CONFIGURED CLASSES\n\
650 ==================\n\
651 \n\
652 TS (Time Sharing)\n\
653 \tConfigured TS User Priority Range: -19 through 20\n\
654 \n"
655 "%s%s"
656 #ifndef	S42
657 "\
658 RT (Real Time Round Robin)\n\
659 \tMaximum Configured RT Priority: 99\n\
660 \n"
661 #else	/* S42 */
662 "\
663 FP (Real Time Round Robin)\n\
664 \tMaximum Configured FP Priority: 99\n\
665 \n"
666 #endif	/* S42 */
667 "FF (Real Time First In-First Out)\n\
668 \tMaximum Configured FI Priority: 99\n\
669 ", batchclass, isoclass);
670 }
671 
672 static void
disp_this(int class,const char * title,const char * head)673 disp_this(int class, const char *title, const char *head)
674 {
675 	struct idnode	*id;
676 
677 	for (id = proclist; id; id = id->id_nxt)
678 		if (id->id_class == class)
679 			break;
680 	if (id == NULL)
681 		return;
682 	printf("%s\n    PID    %s", title, head);
683 	if (class == SCHED_RR)
684 		printf("       TQNTM");
685 	putchar('\n');
686 	for (id = proclist; id; id = id->id_nxt) {
687 		if (id->id_class == class) {
688 			printf("%7d %7d", id->id_val, id->id_upri);
689 			if (class == SCHED_RR)
690 				printf(" %11lu", id->id_tqntm);
691 			putchar('\n');
692 		}
693 	}
694 }
695 
696 static void
display(void)697 display(void)
698 {
699 	disp_this(SCHED_OTHER, "TIME SHARING PROCESSES", "TSUPRI");
700 	if (sched_get_priority_max(SCHED_BATCH) >= 0)
701 		disp_this(SCHED_BATCH, "BATCH PROCESSES", "BAUPRI");
702 	if (sched_get_priority_max(SCHED_ISO) >= 0)
703 		disp_this(SCHED_ISO, "ISOCHRONOUS PROCESSES", "ISUPRI");
704 #ifndef	S42
705 	disp_this(SCHED_RR, "REAL TIME PROCESSES", "RTPRI");
706 #else	/* S42 */
707 	disp_this(SCHED_RR, "FIXED PRIORITY PROCESSES", "FPPRI");
708 #endif	/* S42 */
709 	disp_this(SCHED_FIFO, "FIFO CLASS PROCESSES", "FIPRI");
710 }
711 
712 static int
set_this(int pid,int defclass)713 set_this(int pid, int defclass)
714 {
715 	struct sched_param	scp;
716 	int	class, upri;
717 
718 	memset(&scp, 0, sizeof scp);
719 	if (cflag != -1)
720 		class = cflag;
721 	else
722 		class = defclass;
723 	if (pflag != -1) {
724 		if (class != SCHED_BATCH)
725 			scp.sched_priority = pflag;
726 		else
727 			scp.sched_priority = 0;
728 		upri = pflag;
729 	} else {
730 		sched_getparam(pid, &scp);
731 		if (scp.sched_priority == 0 && class != SCHED_BATCH &&
732 				class != SCHED_ISO)
733 			scp.sched_priority = 1;
734 		upri = 0 - getpriority(PRIO_PROCESS, pid);
735 	}
736 	errno = 0;
737 	if ((class == SCHED_RR || class == SCHED_FIFO ||
738 				class == SCHED_ISO ||
739 				class == SCHED_BATCH) &&
740 			sched_setscheduler(pid, class, &scp) < 0)
741 		return 1;
742 	if ((class == SCHED_OTHER || class == SCHED_BATCH ||
743 				class == SCHED_ISO) &&
744 			setpriority(PRIO_PROCESS, pid, 0 - upri) < 0)
745 		return 2;
746 	return 0;
747 }
748 
749 static void
set(void)750 set(void)
751 {
752 	struct idnode	*id;
753 	int	cnt = 0;
754 
755 	if (cflag == -1) {
756 		for (id = proclist; id; id = id->id_nxt) {
757 			if (id->id_class != proclist->id_class) {
758 				fprintf(stderr, "%s: Specified processes from "
759 						"different classes.\n",
760 					progname);
761 				exit(1);
762 			}
763 		}
764 	}
765 	for (id = proclist; id; id = id->id_nxt) {
766 		if (set_this(id->id_val, id->id_class) != 0) {
767 			switch (errno) {
768 			case EPERM:
769 				errcnt |= 1;
770 				cnt++;
771 				fprintf(stderr, "Permissions error encountered "
772 						"on pid %d.\n", id->id_val);
773 				break;
774 			case ESRCH:
775 				/*
776 				 * Do nothing as this just means that the
777 				 * process has terminated after we read its
778 				 * data before.
779 				 */
780 				break;
781 			case EINVAL:
782 				errcnt |= 1;
783 				cnt++;
784 				fprintf(stderr, "%s: Invalid argument on "
785 						"pid %d.\n",
786 					progname, id->id_val);
787 				break;
788 			default:
789 				errcnt |= 1;
790 				cnt++;
791 				fprintf(stderr, "%s: pid %d: errno %d\n",
792 					progname, id->id_val, errno);
793 			}
794 		} else
795 			cnt++;
796 	}
797 	if (cnt == 0)
798 		noprocs();
799 }
800 
801 static void
execute(char ** args)802 execute(char **args)
803 {
804 	struct proc	*sp;
805 	int	defclass;
806 	const char	*fcall = NULL;
807 
808 	if (args == NULL || *args == NULL)
809 		usage();
810 	if ((sp = getproc(getpid())) != NULL)
811 		defclass = sp->p_class;
812 	else
813 		defclass = SCHED_OTHER;
814 	switch (set_this(0, defclass)) {
815 	case 0:
816 		break;
817 	case 1:
818 		if (fcall == NULL)
819 			fcall = "sched_setparam";
820 		/*FALLTHRU*/
821 	case 2:
822 		if (fcall == NULL)
823 			fcall = "setpriority";
824 		/*FALLTHRU*/
825 	default:
826 		if (fcall == NULL)
827 			fcall = "unknown";
828 		fprintf(stderr, "%s: Can't set scheduling parameters\n"
829 				"%s system call failed with errno %d\n",
830 			progname, fcall, errno);
831 		exit(1);
832 	}
833 	execvp(args[0], args);
834 	fprintf(stderr, "%s: Can't execute %s, exec failed with errno %d\n",
835 			progname, args[0], errno);
836 	exit(1);
837 }
838 
839 static void
selclass(const char * s)840 selclass(const char *s)
841 {
842 	if (eq(s, "TS"))
843 		cflag = SCHED_OTHER;
844 	else if (eq(s, "B"))
845 		cflag = SCHED_BATCH;
846 	else if (eq(s, "BA"))	/* old */
847 		cflag = SCHED_BATCH;
848 	else if (eq(s, "ISO"))
849 		cflag = SCHED_ISO;
850 	else if (eq(s, "IS"))	/* old */
851 		cflag = SCHED_ISO;
852 	else if (eq(s, "FF"))
853 		cflag = SCHED_FIFO;
854 	else if (eq(s, "FI"))	/* old */
855 		cflag = SCHED_FIFO;
856 	else if (eq(s, "RT") || eq(s, "FP"))
857 		cflag = SCHED_RR;
858 	else {
859 		fprintf(stderr, "%s: Invalid or unconfigured class %s\n",
860 				progname, s);
861 		exit(2);
862 	}
863 }
864 
865 static void
selid(const char * s)866 selid(const char *s)
867 {
868 	if (eq(s, "pid"))
869 		idtype = ID_PID;
870 	else if (eq(s, "ppid"))
871 		idtype = ID_PPID;
872 	else if (eq(s, "pgid"))
873 		idtype = ID_PGID;
874 	else if (eq(s, "sid"))
875 		idtype = ID_SID;
876 	else if (eq(s, "class"))
877 		idtype = ID_CLASS;
878 	else if (eq(s, "uid"))
879 		idtype = ID_UID;
880 	else if (eq(s, "gid"))
881 		idtype = ID_GID;
882 	else if (eq(s, "all"))
883 		idtype = ID_ALL;
884 	else {
885 		fprintf(stderr, "%s: bad idtype %s\n", progname, s);
886 		exit(2);
887 	}
888 }
889 
890 int
main(int argc,char ** argv)891 main(int argc, char **argv)
892 {
893 	const char	optstring[] = "ldi:sc:ep:";
894 	int	i;
895 
896 #ifdef	__GLIBC__
897 	putenv("POSIXLY_CORRECT=1");
898 #endif
899 	progname = basename(argv[0]);
900 	while ((i = getopt(argc, argv, optstring)) != EOF) {
901 		switch (i) {
902 		case 'l':
903 		case 'd':
904 		case 's':
905 		case 'e':
906 			if (action)
907 				usage();
908 			action = i;
909 			break;
910 		case 'c':
911 			selclass(optarg);
912 			break;
913 		case 'p':
914 			pflag = atoi(optarg);
915 			break;
916 		case 'i':
917 			iflag = 1;
918 			selid(optarg);
919 			break;
920 		default:
921 			usage();
922 		}
923 	}
924 	switch (action) {
925 	case 'l':
926 		if (iflag || cflag != -1 || pflag != -1)
927 			usage();
928 		listclasses();
929 		break;
930 	case 'd':
931 		if (cflag != -1)
932 			usage();
933 		getproclist(&argv[optind]);
934 		display();
935 		break;
936 	case 's':
937 		getproclist(&argv[optind]);
938 		set();
939 		break;
940 	case 'e':
941 		if (iflag)
942 			usage();
943 		execute(&argv[optind]);
944 		break;
945 	default:
946 		usage();
947 	}
948 	return errcnt;
949 }
950 
951 #else	/* !__linux__, !__FreeBSD__ */
952 
953 #include	<unistd.h>
954 
955 int
main(int argc,char ** argv)956 main(int argc, char **argv)
957 {
958 	execv("/usr/bin/priocntl", argv);
959 	write(2, "priocntl not available\n", 23);
960 	_exit(0177);
961 }
962 
963 #endif	/* !__linux__, !__FreeBSD__ */
964