summaryrefslogtreecommitdiffhomepage
path: root/util-linux/chrt.c
blob: 6e8f6674115f99896b736b4e62d8822661799e41 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
/* vi: set sw=4 ts=4: */
/*
 * chrt - manipulate real-time attributes of a process
 * Copyright (c) 2006-2007 Bernhard Reutner-Fischer
 *
 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
 */
//config:config CHRT
//config:	bool "chrt (4.7 kb)"
//config:	default y
//config:	help
//config:	Manipulate real-time attributes of a process.
//config:	This requires sched_{g,s}etparam support in your libc.

//applet:IF_CHRT(APPLET_NOEXEC(chrt, chrt, BB_DIR_USR_BIN, BB_SUID_DROP, chrt))

//kbuild:lib-$(CONFIG_CHRT) += chrt.o

//usage:#define chrt_trivial_usage
//usage:       "-m | -p [PRIO] PID | [-rfobi] PRIO PROG [ARGS]"
//usage:#define chrt_full_usage "\n\n"
//usage:       "Change scheduling priority and class for a process\n"
//usage:     "\n	-m	Show min/max priorities"
//usage:     "\n	-p	Operate on PID"
//usage:     "\n	-r	Set SCHED_RR class"
//usage:     "\n	-f	Set SCHED_FIFO class"
//usage:     "\n	-o	Set SCHED_OTHER class"
//usage:     "\n	-b	Set SCHED_BATCH class"
//usage:     "\n	-i	Set SCHED_IDLE class"
//usage:
//usage:#define chrt_example_usage
//usage:       "$ chrt -r 4 sleep 900; x=$!\n"
//usage:       "$ chrt -f -p 3 $x\n"
//usage:       "You need CAP_SYS_NICE privileges to set scheduling attributes of a process"

#include <sched.h>
#include "libbb.h"
#ifndef SCHED_IDLE
# define SCHED_IDLE 5
#endif

//musl has no __MUSL__ or similar define to check for,
//but its <sys/types.h> has these lines:
// #define __NEED_fsblkcnt_t
// #define __NEED_fsfilcnt_t
#if defined(__linux__) && defined(__NEED_fsblkcnt_t) && defined(__NEED_fsfilcnt_t)
# define LIBC_IS_MUSL 1
# include <sys/syscall.h>
#else
# define LIBC_IS_MUSL 0
#endif

static const char *policy_name(int pol)
{
	if (pol > 6)
		return utoa(pol);
	return nth_string(
		"OTHER"   "\0" /* 0:SCHED_OTHER */
		"FIFO"    "\0" /* 1:SCHED_FIFO */
		"RR"      "\0" /* 2:SCHED_RR */
		"BATCH"   "\0" /* 3:SCHED_BATCH */
		"ISO"     "\0" /* 4:SCHED_ISO */
		"IDLE"    "\0" /* 5:SCHED_IDLE */
		"DEADLINE",    /* 6:SCHED_DEADLINE */
		pol
	);
}

static void show_min_max(int pol)
{
	const char *fmt = "SCHED_%s min/max priority\t: %u/%u\n";
	int max, min;

	max = sched_get_priority_max(pol);
	min = sched_get_priority_min(pol);
	if ((max|min) < 0)
		fmt = "SCHED_%s not supported\n";
	printf(fmt, policy_name(pol), min, max);
}

#define OPT_m (1<<0)
#define OPT_p (1<<1)
#define OPT_r (1<<2)
#define OPT_f (1<<3)
#define OPT_o (1<<4)
#define OPT_b (1<<5)
#define OPT_i (1<<6)

int chrt_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int chrt_main(int argc UNUSED_PARAM, char **argv)
{
	pid_t pid = 0;
	unsigned opt;
	struct sched_param sp;
	char *pid_str;
	char *priority = priority; /* for compiler */
	const char *current_new;
	int policy = SCHED_RR;
	int ret;

	opt = getopt32(argv, "^"
			"+" "mprfobi"
			"\0"
			/* only one policy accepted: */
			"r--fobi:f--robi:o--rfbi:b--rfoi:i--rfob"
	);
	if (opt & OPT_m) { /* print min/max and exit */
		show_min_max(SCHED_OTHER);
		show_min_max(SCHED_FIFO);
		show_min_max(SCHED_RR);
		show_min_max(SCHED_BATCH);
		show_min_max(SCHED_IDLE);
		fflush_stdout_and_exit(EXIT_SUCCESS);
	}
	//if (opt & OPT_r)
	//	policy = SCHED_RR; - default, already set
	if (opt & OPT_f)
		policy = SCHED_FIFO;
	if (opt & OPT_o)
		policy = SCHED_OTHER;
	if (opt & OPT_b)
		policy = SCHED_BATCH;
	if (opt & OPT_i)
		policy = SCHED_IDLE;

	argv += optind;
	if (!argv[0])
		bb_show_usage();
	if (opt & OPT_p) {
		pid_str = *argv++;
		if (*argv) { /* "-p PRIO PID [...]" */
			priority = pid_str;
			pid_str = *argv;
		}
		/* else "-p PID", and *argv == NULL */
		pid = xatoul_range(pid_str, 1, ((unsigned)(pid_t)ULONG_MAX) >> 1);
	} else {
		priority = *argv++;
		if (!*argv)
			bb_show_usage();
	}

	current_new = "current\0new";
	if (opt & OPT_p) {
		int pol;
 print_rt_info:
#if LIBC_IS_MUSL
		/* musl libc returns ENOSYS for its sched_getscheduler library
		 * function, because the sched_getscheduler Linux kernel system call
		 * does not conform to Posix; so we use the system call directly
		 */
		pol = syscall(SYS_sched_getscheduler, pid);
#else
		pol = sched_getscheduler(pid);
#endif
		if (pol < 0)
			bb_perror_msg_and_die("can't %cet pid %u's policy", 'g', (int)pid);
#ifdef SCHED_RESET_ON_FORK
		/* "Since Linux 2.6.32, the SCHED_RESET_ON_FORK flag
		 * can be ORed in policy when calling sched_setscheduler().
		 * As a result of including this flag, children created by
		 * fork(2) do not inherit privileged scheduling policies"
		 *
		 * This bit is also returned by sched_getscheduler()!
		 * (TODO: do we want to show it?)
		 */
		pol &= ~SCHED_RESET_ON_FORK;
#endif
		printf("pid %u's %s scheduling policy: SCHED_%s\n",
			pid, current_new, policy_name(pol)
		);
#if LIBC_IS_MUSL
		ret = syscall(SYS_sched_getparam, pid, &sp);
#else
		ret = sched_getparam(pid, &sp);
#endif
		if (ret)
			bb_perror_msg_and_die("can't get pid %u's attributes", (int)pid);
		printf("pid %u's %s scheduling priority: %d\n",
			(int)pid, current_new, sp.sched_priority
		);
		if (!*argv) {
			/* Either it was just "-p PID",
			 * or it was "-p PRIO PID" and we came here
			 * for the second time (see goto below) */
			return EXIT_SUCCESS;
		}
		*argv = NULL;
		current_new += 8;
	}

	sp.sched_priority = xstrtou_range(priority, 0,
		sched_get_priority_min(policy), sched_get_priority_max(policy)
	);

#if LIBC_IS_MUSL
	ret = syscall(SYS_sched_setscheduler, pid, policy, &sp);
#else
	ret = sched_setscheduler(pid, policy, &sp);
#endif
	if (ret)
		bb_perror_msg_and_die("can't %cet pid %u's policy", 's', (int)pid);

	if (!argv[0]) /* "-p PRIO PID [...]" */
		goto print_rt_info;

	BB_EXECVP_or_die(argv);
}