mirror of
https://github.com/systemd/systemd-stable.git
synced 2025-01-11 05:17:44 +03:00
Merge pull request #14465 from poettering/setprio-rework
When Nice= is used, clamp to RLIMIT_NICE
This commit is contained in:
commit
06ae8800d0
@ -22,6 +22,7 @@
|
||||
#include "alloc-util.h"
|
||||
#include "architecture.h"
|
||||
#include "env-util.h"
|
||||
#include "errno-util.h"
|
||||
#include "escape.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
@ -1527,6 +1528,62 @@ int pidfd_get_pid(int fd, pid_t *ret) {
|
||||
return parse_pid(p, ret);
|
||||
}
|
||||
|
||||
static int rlimit_to_nice(rlim_t limit) {
|
||||
if (limit <= 1)
|
||||
return PRIO_MAX-1; /* i.e. 19 */
|
||||
|
||||
if (limit >= -PRIO_MIN + PRIO_MAX)
|
||||
return PRIO_MIN; /* i.e. -20 */
|
||||
|
||||
return PRIO_MAX - (int) limit;
|
||||
}
|
||||
|
||||
int setpriority_closest(int priority) {
|
||||
int current, limit, saved_errno;
|
||||
struct rlimit highest;
|
||||
|
||||
/* Try to set requested nice level */
|
||||
if (setpriority(PRIO_PROCESS, 0, priority) >= 0)
|
||||
return 1;
|
||||
|
||||
/* Permission failed */
|
||||
saved_errno = -errno;
|
||||
if (!ERRNO_IS_PRIVILEGE(saved_errno))
|
||||
return saved_errno;
|
||||
|
||||
errno = 0;
|
||||
current = getpriority(PRIO_PROCESS, 0);
|
||||
if (errno != 0)
|
||||
return -errno;
|
||||
|
||||
if (priority == current)
|
||||
return 1;
|
||||
|
||||
/* Hmm, we'd expect that raising the nice level from our status quo would always work. If it doesn't,
|
||||
* then the whole setpriority() system call is blocked to us, hence let's propagate the error
|
||||
* right-away */
|
||||
if (priority > current)
|
||||
return saved_errno;
|
||||
|
||||
if (getrlimit(RLIMIT_NICE, &highest) < 0)
|
||||
return -errno;
|
||||
|
||||
limit = rlimit_to_nice(highest.rlim_cur);
|
||||
|
||||
/* We are already less nice than limit allows us */
|
||||
if (current < limit) {
|
||||
log_debug("Cannot raise nice level, permissions and the resource limit do not allow it.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Push to the allowed limit */
|
||||
if (setpriority(PRIO_PROCESS, 0, limit) < 0)
|
||||
return -errno;
|
||||
|
||||
log_debug("Cannot set requested nice level (%i), used next best (%i).", priority, limit);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *const ioprio_class_table[] = {
|
||||
[IOPRIO_CLASS_NONE] = "none",
|
||||
[IOPRIO_CLASS_RT] = "realtime",
|
||||
|
@ -200,3 +200,5 @@ assert_cc(TASKS_MAX <= (unsigned long) PID_T_MAX);
|
||||
})
|
||||
|
||||
int pidfd_get_pid(int fd, pid_t *ret);
|
||||
|
||||
int setpriority_closest(int priority);
|
||||
|
@ -3236,11 +3236,11 @@ static int exec_child(
|
||||
}
|
||||
}
|
||||
|
||||
if (context->nice_set)
|
||||
if (setpriority(PRIO_PROCESS, 0, context->nice) < 0) {
|
||||
*exit_status = EXIT_NICE;
|
||||
return log_unit_error_errno(unit, errno, "Failed to set up process scheduling priority (nice level): %m");
|
||||
}
|
||||
if (context->nice_set) {
|
||||
r = setpriority_closest(context->nice);
|
||||
if (r < 0)
|
||||
return log_unit_error_errno(unit, r, "Failed to set up process scheduling priority (nice level): %m");
|
||||
}
|
||||
|
||||
if (context->cpu_sched_set) {
|
||||
struct sched_param param = {
|
||||
|
@ -14,6 +14,7 @@
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "architecture.h"
|
||||
#include "errno-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "log.h"
|
||||
#include "macro.h"
|
||||
@ -21,11 +22,13 @@
|
||||
#include "missing_syscall.h"
|
||||
#include "parse-util.h"
|
||||
#include "process-util.h"
|
||||
#include "rlimit-util.h"
|
||||
#include "signal-util.h"
|
||||
#include "stdio-util.h"
|
||||
#include "string-util.h"
|
||||
#include "terminal-util.h"
|
||||
#include "tests.h"
|
||||
#include "user-util.h"
|
||||
#include "util.h"
|
||||
#include "virt.h"
|
||||
|
||||
@ -601,6 +604,92 @@ static void test_ioprio_class_from_to_string(void) {
|
||||
test_ioprio_class_from_to_string_one("-1", -1);
|
||||
}
|
||||
|
||||
static void test_setpriority_closest(void) {
|
||||
int r;
|
||||
|
||||
r = safe_fork("(test-setprio)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_WAIT|FORK_LOG, NULL);
|
||||
assert_se(r >= 0);
|
||||
|
||||
if (r == 0) {
|
||||
bool full_test;
|
||||
int p, q;
|
||||
/* child */
|
||||
|
||||
/* rlimit of 30 equals nice level of -10 */
|
||||
if (setrlimit(RLIMIT_NICE, &RLIMIT_MAKE_CONST(30)) < 0) {
|
||||
/* If this fails we are probably unprivielged or in a userns of some kind, let's skip
|
||||
* the full test */
|
||||
assert_se(ERRNO_IS_PRIVILEGE(errno));
|
||||
full_test = false;
|
||||
} else {
|
||||
assert_se(setresgid(GID_NOBODY, GID_NOBODY, GID_NOBODY) >= 0);
|
||||
assert_se(setresuid(UID_NOBODY, UID_NOBODY, UID_NOBODY) >= 0);
|
||||
full_test = true;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
p = getpriority(PRIO_PROCESS, 0);
|
||||
assert_se(errno == 0);
|
||||
|
||||
/* It should always be possible to set our nice level to the current one */
|
||||
assert_se(setpriority_closest(p) > 0);
|
||||
|
||||
errno = 0;
|
||||
q = getpriority(PRIO_PROCESS, 0);
|
||||
assert_se(errno == 0 && p == q);
|
||||
|
||||
/* It should also be possible to to set the nice level to one higher */
|
||||
if (p < PRIO_MAX-1) {
|
||||
assert_se(setpriority_closest(++p) > 0);
|
||||
|
||||
errno = 0;
|
||||
q = getpriority(PRIO_PROCESS, 0);
|
||||
assert_se(errno == 0 && p == q);
|
||||
}
|
||||
|
||||
/* It should also be possible to to set the nice level to two higher */
|
||||
if (p < PRIO_MAX-1) {
|
||||
assert_se(setpriority_closest(++p) > 0);
|
||||
|
||||
errno = 0;
|
||||
q = getpriority(PRIO_PROCESS, 0);
|
||||
assert_se(errno == 0 && p == q);
|
||||
}
|
||||
|
||||
if (full_test) {
|
||||
/* These two should work, given the RLIMIT_NICE we set above */
|
||||
assert_se(setpriority_closest(-10) > 0);
|
||||
errno = 0;
|
||||
q = getpriority(PRIO_PROCESS, 0);
|
||||
assert_se(errno == 0 && q == -10);
|
||||
|
||||
assert_se(setpriority_closest(-9) > 0);
|
||||
errno = 0;
|
||||
q = getpriority(PRIO_PROCESS, 0);
|
||||
assert_se(errno == 0 && q == -9);
|
||||
|
||||
/* This should succeed but should be clamped to the limit */
|
||||
assert_se(setpriority_closest(-11) == 0);
|
||||
errno = 0;
|
||||
q = getpriority(PRIO_PROCESS, 0);
|
||||
assert_se(errno == 0 && q == -10);
|
||||
|
||||
assert_se(setpriority_closest(-8) > 0);
|
||||
errno = 0;
|
||||
q = getpriority(PRIO_PROCESS, 0);
|
||||
assert_se(errno == 0 && q == -8);
|
||||
|
||||
/* This should succeed but should be clamped to the limit */
|
||||
assert_se(setpriority_closest(-12) == 0);
|
||||
errno = 0;
|
||||
q = getpriority(PRIO_PROCESS, 0);
|
||||
assert_se(errno == 0 && q == -10);
|
||||
}
|
||||
|
||||
_exit(EXIT_SUCCESS);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
test_setup_logging(LOG_DEBUG);
|
||||
|
||||
@ -627,6 +716,7 @@ int main(int argc, char *argv[]) {
|
||||
test_safe_fork();
|
||||
test_pid_to_ptr();
|
||||
test_ioprio_class_from_to_string();
|
||||
test_setpriority_closest();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user