From 3679b5cd03c63d8a011a2430c869ae0a28ff3715 Mon Sep 17 00:00:00 2001 From: q66 Date: Sun, 20 Oct 2024 04:44:34 +0200 Subject: [PATCH] Add fields for nice, ionice, oom-score-adj --- src/baseproc-service.cc | 5 +++ src/includes/load-service.h | 73 +++++++++++++++++++++++++++++++++++++ src/includes/proc-service.h | 24 ++++++++++++ src/load-service.cc | 15 ++++++++ src/proc-service.cc | 5 +++ src/run-child-proc.cc | 35 ++++++++++++++++++ src/settings.cc | 6 +++ 7 files changed, 163 insertions(+) diff --git a/src/baseproc-service.cc b/src/baseproc-service.cc index e25b30a2..806b3640 100644 --- a/src/baseproc-service.cc +++ b/src/baseproc-service.cc @@ -265,6 +265,11 @@ bool base_process_service::start_ps_process(const std::vector &cmd run_params.secbits = secbits; run_params.no_new_privs = onstart_flags.no_new_privs; #endif + #ifdef __linux__ + run_params.nice = nice; + run_params.ionice = ionice; + run_params.oom_adj = oom_adj; + #endif run_child_proc(run_params); } else { diff --git a/src/includes/load-service.h b/src/includes/load-service.h index 8d2fb001..8c9d34e9 100644 --- a/src/includes/load-service.h +++ b/src/includes/load-service.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -271,6 +272,11 @@ enum class setting_id_t { CAPABILITIES, SECURE_BITS, #endif +#ifdef __linux__ + NICE, + IONICE, + OOM_SCORE_ADJ, +#endif }; struct setting_details { @@ -834,6 +840,30 @@ inline unsigned long long parse_unum_param(file_pos_ref input_pos, const std::st } } +// Parse a signed numeric parameter value +inline long long parse_snum_param(file_pos_ref input_pos, const std::string ¶m, + const std::string &service_name, long long min = std::numeric_limits::min(), + long long max = std::numeric_limits::max()) +{ + const char * num_err_msg = "specified value contains invalid numeric characters or is outside " + "allowed range."; + + std::size_t ind = 0; + try { + long long v = std::stoll(param, &ind, 0); + if (v < min || v > max || ind != (param.length() - (v < 0))) { + throw service_description_exc(service_name, num_err_msg, input_pos); + } + return v; + } + catch (std::out_of_range &exc) { + throw service_description_exc(service_name, num_err_msg, input_pos); + } + catch (std::invalid_argument &exc) { + throw service_description_exc(service_name, num_err_msg, input_pos); + } +} + // In a vector, find or create rlimits for a particular resource type. inline service_rlimits &find_rlimits(std::vector &all_rlimits, int resource_id) { @@ -1379,6 +1409,12 @@ class service_settings_wrapper secure_bits_t secbits; #endif + #ifdef __linux__ + int nice = INT_MIN; + int ionice = INT_MIN; + int oom_adj = INT_MIN; + #endif + #if USE_UTMPX char inittab_id[sizeof(utmpx().ut_id)] = {0}; char inittab_line[sizeof(utmpx().ut_line)] = {0}; @@ -1640,6 +1676,43 @@ void process_service_line(settings_wrapper &settings, const char *name, const ch break; } #endif + #ifdef __linux__ + case setting_id_t::NICE: + { + string nice_str = read_setting_value(input_pos, i, end); + settings.nice = (int)parse_snum_param(input_pos, nice_str, name, -20, 19); + break; + } + case setting_id_t::IONICE: + { + string ionice_str = read_setting_value(input_pos, i, end); + if (ionice_str == "none") { + settings.ionice = 0; + } + else if (starts_with(ionice_str, "rt:")) { + auto nval = parse_unum_param(input_pos, ionice_str.substr(3 /* len 'rt:' */), name, 7); + settings.ionice = (1 << 13) | nval; + } + else if (starts_with(ionice_str, "be:")) { + auto nval = parse_unum_param(input_pos, ionice_str.substr(3 /* len 'be:' */), name, 7); + settings.ionice = (2 << 13) | nval; + } + else if (ionice_str == "idle") { + settings.ionice = 3 << 13; + } + else { + throw service_description_exc(name, "invalid value for ionice: " + ionice_str, + name, input_pos); + } + break; + } + case setting_id_t::OOM_SCORE_ADJ: + { + string oom_adj_str = read_setting_value(input_pos, i, end); + settings.oom_adj = (int)parse_snum_param(input_pos, oom_adj_str, name, -1000, 1000); + break; + } + #endif case setting_id_t::SOCKET_LISTEN: settings.socket_path = read_setting_value(input_pos, i, end, nullptr); break; diff --git a/src/includes/proc-service.h b/src/includes/proc-service.h index bb7ed428..f8acc9b9 100644 --- a/src/includes/proc-service.h +++ b/src/includes/proc-service.h @@ -43,6 +43,9 @@ struct run_proc_params bool on_console; // whether to run on console bool in_foreground; // if on console: whether to run in foreground bool unmask_sigint = false; // if in foreground: whether to unmask SIGINT + int nice = INT_MIN; + int ionice = INT_MIN; + int oom_adj = INT_MIN; int wpipefd; // pipe to which error status will be sent (if error occurs) int csfd; // control socket fd (or -1); may be moved int socket_fd; // pre-opened socket fd (or -1); may be moved @@ -203,6 +206,12 @@ class base_process_service : public service_record bool no_new_privs = false; #endif +#ifdef __linux__ + int nice = INT_MIN; + int ionice = INT_MIN; + int oom_adj = INT_MIN; +#endif + #if SUPPORT_CGROUPS string run_in_cgroup; #endif @@ -486,6 +495,21 @@ class base_process_service : public service_record } #endif + #ifdef __linux__ + void set_nice(int nice_v) noexcept + { + nice = nice_v; + } + void set_ionice(int ionice_v) noexcept + { + ionice = ionice_v; + } + void set_oom_adj(int oom_adj_v) noexcept + { + oom_adj = oom_adj_v; + } + #endif + void set_rlimits(std::vector &&rlimits_p) { rlimits = std::move(rlimits_p); diff --git a/src/load-service.cc b/src/load-service.cc index bedf9f87..aa34189d 100644 --- a/src/load-service.cc +++ b/src/load-service.cc @@ -747,6 +747,11 @@ service_record * dirload_service_set::load_reload_service(const char *fullname, #if SUPPORT_CAPABILITIES rvalps->set_cap(cap_iab, settings.secbits.get()); #endif + #ifdef __linux__ + rvalps->set_nice(settings.nice); + rvalps->set_ionice(settings.ionice); + rvalps->set_oom_adj(settings.oom_adj); + #endif rvalps->set_rlimits(std::move(settings.rlimits)); rvalps->set_restart_interval(settings.restart_interval, settings.max_restarts); rvalps->set_restart_delay(settings.restart_delay); @@ -793,6 +798,11 @@ service_record * dirload_service_set::load_reload_service(const char *fullname, #if SUPPORT_CAPABILITIES rvalps->set_cap(cap_iab, settings.secbits.get()); #endif + #ifdef __linux__ + rvalps->set_nice(settings.nice); + rvalps->set_ionice(settings.ionice); + rvalps->set_oom_adj(settings.oom_adj); + #endif rvalps->set_rlimits(std::move(settings.rlimits)); rvalps->set_pid_file(std::move(settings.pid_file)); rvalps->set_restart_interval(settings.restart_interval, settings.max_restarts); @@ -835,6 +845,11 @@ service_record * dirload_service_set::load_reload_service(const char *fullname, #if SUPPORT_CAPABILITIES rvalps->set_cap(cap_iab, settings.secbits.get()); #endif + #ifdef __linux__ + rvalps->set_nice(settings.nice); + rvalps->set_ionice(settings.ionice); + rvalps->set_oom_adj(settings.oom_adj); + #endif rvalps->set_rlimits(std::move(settings.rlimits)); rvalps->set_stop_timeout(settings.stop_timeout); rvalps->set_start_timeout(settings.start_timeout); diff --git a/src/proc-service.cc b/src/proc-service.cc index 4ba23dc3..5f6e5113 100644 --- a/src/proc-service.cc +++ b/src/proc-service.cc @@ -940,6 +940,11 @@ bool process_service::start_stop_process(const std::vector &cmd) n run_params.secbits = secbits; run_params.no_new_privs = onstart_flags.no_new_privs; #endif + #ifdef __linux__ + run_params.nice = nice; + run_params.ionice = ionice; + run_params.oom_adj = oom_adj; + #endif run_child_proc(run_params); } else { diff --git a/src/run-child-proc.cc b/src/run-child-proc.cc index b206b6d4..ef9392bb 100644 --- a/src/run-child-proc.cc +++ b/src/run-child-proc.cc @@ -65,6 +65,9 @@ void base_process_service::run_child_proc(run_proc_params params) noexcept const char *working_dir = params.working_dir; const char *logfile = params.logfile; bool on_console = params.on_console; + int nice = params.nice; + int ionice = params.ionice; + int oom_adj = params.oom_adj; int wpipefd = params.wpipefd; int csfd = params.csfd; int notify_fd = params.notify_fd; @@ -301,6 +304,38 @@ void base_process_service::run_child_proc(run_proc_params params) noexcept if (setrlimit(limit.resource_id, &setlimits) != 0) goto failure_out; } + // nice + if (nice != INT_MIN) { + if (setpriority(PRIO_PROCESS, getpid(), nice) != 0) goto failure_out; + // we usually create a new session leader; that makes nice not very + // useful as the Linux kernel will autogroup processes by session id + // except when disabled - so also work around this where enabled + // the r+ is used in order to avoid creating it where already disabled + errno = 0; + FILE *ag = std::fopen("/proc/self/autogroup", "r+"); + if (ag) { + std::fprintf(ag, "%d\n", nice); + std::fclose(ag); + } + else if (errno != ENOENT) goto failure_out; + } + + // ionice + if (ionice != INT_MIN) { + #ifdef __NR_ioprio_set + if (syscall(__NR_ioprio_set, 1, (int)getpid(), ionice) != 0) goto failure_out; + #endif + } + + // oom score adjustment + if (oom_adj != INT_MIN) { + errno = 0; + FILE *adj = std::fopen("/proc/self/oom_score_adj", "w"); + if (!adj) goto failure_out; + std::fprintf(adj, "%d\n", oom_adj); + std::fclose(adj); + } + #if SUPPORT_CGROUPS if (params.run_in_cgroup != nullptr && *params.run_in_cgroup != 0) { err.stage = exec_stage::ENTER_CGROUP; diff --git a/src/settings.cc b/src/settings.cc index 395c52c0..529bb94a 100644 --- a/src/settings.cc +++ b/src/settings.cc @@ -66,6 +66,12 @@ setting_details all_settings[] = { {"secure-bits", setting_id_t::SECURE_BITS, false, true, false}, #endif +#ifdef __linux__ + {"nice", setting_id_t::NICE, false, true, false}, + {"ionice", setting_id_t::IONICE, false, true, false}, + {"oom-score-adj", setting_id_t::OOM_SCORE_ADJ, false, true, false}, +#endif + {nullptr, setting_id_t::LAST, false, false, false} };