From 687e820c846a528cbb50a3e599e9890ebfb001a5 Mon Sep 17 00:00:00 2001 From: Cedric Hombourger Date: Mon, 11 Mar 2024 13:56:35 +0100 Subject: [PATCH] add --prefer-only option to only kill processes matching --prefer While --avoid exists to list processes that should not be killed, it may be desirable to have earlyoom simply ignore any processes that do not match the regex specified in --prefer. It should be noted that the Linux kernel may still opt to kill other processes if earlyoom did not free up enough memory to prevent an oom. Signed-off-by: Murat Boyar Signed-off-by: Cedric Hombourger --- kill.c | 14 +++++++++----- kill.h | 2 ++ main.c | 15 +++++++++++++++ testsuite_cli_test.go | 4 ++++ 4 files changed, 30 insertions(+), 5 deletions(-) diff --git a/kill.c b/kill.c index e8f283e..30c0ed3 100644 --- a/kill.c +++ b/kill.c @@ -309,11 +309,15 @@ bool is_larger(const poll_loop_args_t* args, const procinfo_t* victim, procinfo_ debug("pid %d: error reading process name: %s\n", cur->pid, strerror(-res)); return false; } - if (args->prefer_regex && regexec(args->prefer_regex, cur->name, (size_t)0, NULL, 0) == 0) { - if (args->sort_by_rss) { - cur->VmRSSkiB += VMRSS_PREFER; - } else { - cur->badness += BADNESS_PREFER; + if (args->prefer_regex) { + if (regexec(args->prefer_regex, cur->name, (size_t)0, NULL, 0) == 0) { + if (args->sort_by_rss) { + cur->VmRSSkiB += VMRSS_PREFER; + } else { + cur->badness += BADNESS_PREFER; + } + } else if (args->prefer_only) { + return false; } } if (args->avoid_regex && regexec(args->avoid_regex, cur->name, (size_t)0, NULL, 0) == 0) { diff --git a/kill.h b/kill.h index a8ddfc3..f0ca117 100644 --- a/kill.h +++ b/kill.h @@ -33,6 +33,8 @@ typedef struct { int report_interval_ms; /* Flag --dryrun was passed */ bool dryrun; + /* Flag --prefer-only was passed */ + bool prefer_only; } poll_loop_args_t; void kill_process(const poll_loop_args_t* args, int sig, const procinfo_t* victim); diff --git a/main.c b/main.c index 8540654..5fd08ee 100644 --- a/main.c +++ b/main.c @@ -43,6 +43,7 @@ enum { LONG_OPT_IGNORE_ROOT, LONG_OPT_USE_SYSLOG, LONG_OPT_SORT_BY_RSS, + LONG_OPT_PREFER_ONLY, }; static int set_oom_score_adj(int); @@ -129,6 +130,7 @@ int main(int argc, char* argv[]) const char* short_opt = "m:s:M:S:kingN:dvr:ph"; struct option long_opt[] = { { "prefer", required_argument, NULL, LONG_OPT_PREFER }, + { "prefer-only", no_argument, NULL, LONG_OPT_PREFER_ONLY }, { "avoid", required_argument, NULL, LONG_OPT_AVOID }, { "ignore", required_argument, NULL, LONG_OPT_IGNORE }, { "dryrun", no_argument, NULL, LONG_OPT_DRYRUN }, @@ -234,6 +236,10 @@ int main(int argc, char* argv[]) case LONG_OPT_PREFER: prefer_cmds = optarg; break; + case LONG_OPT_PREFER_ONLY: + warn("prefer-only mode enabled, will only kill processes matching --prefer\n"); + args.prefer_only = 1; + break; case LONG_OPT_AVOID: avoid_cmds = optarg; break; @@ -273,6 +279,7 @@ int main(int argc, char* argv[]) " --ignore-root-user do not kill processes owned by root\n" " --sort-by-rss find process with the largest rss (default oom_score)\n" " --prefer REGEX prefer to kill processes matching REGEX\n" + " --prefer-only only kill processes matched by --prefer\n" " --avoid REGEX avoid killing processes matching REGEX\n" " --ignore REGEX ignore processes matching REGEX\n" " --dryrun dry run (do not kill any processes)\n" @@ -324,6 +331,14 @@ int main(int argc, char* argv[]) } fprintf(stderr, "Preferring to kill process names that match regex '%s'\n", prefer_cmds); } + if (args.prefer_only) { + if (!prefer_cmds) { + fatal(EINVAL, "prefer-only requires --prefer\n"); + } else if (avoid_cmds) { + avoid_cmds = NULL; + warn("prefer-only and --avoid are mutually exclusive, ignoring --avoid\n"); + } + } if (avoid_cmds) { args.avoid_regex = &_avoid_regex; if (regcomp(args.avoid_regex, avoid_cmds, REG_EXTENDED | REG_NOSUB) != 0) { diff --git a/testsuite_cli_test.go b/testsuite_cli_test.go index a3320b2..c5f165e 100644 --- a/testsuite_cli_test.go +++ b/testsuite_cli_test.go @@ -105,6 +105,10 @@ func TestCli(t *testing.T) { // Test --avoid, --prefer, --ignore-root-user and --sort-by-rss {args: []string{"--avoid", "MyProcess1"}, code: -1, stderrContains: "Will avoid killing", stdoutContains: memReport}, {args: []string{"--prefer", "MyProcess2"}, code: -1, stderrContains: "Preferring to kill", stdoutContains: memReport}, + {args: []string{"--prefer", "MyProcess1", "--prefer-only", "--avoid", "MyProcess2"}, code: -1, stderrContains: "prefer-only and --avoid are mutually", stdoutContains: memReport}, + {args: []string{"--prefer", "MyProcess1", "--prefer-only"}, code: -1, stderrContains: "prefer-only mode enabled", stdoutContains: memReport}, + {args: []string{"--avoid", "MyProcess2", "--prefer-only"}, code: 22, stderrContains: "fatal", stdoutEmpty: true}, + {args: []string{"--prefer-only"}, code: 22, stderrContains: "fatal", stdoutEmpty: true}, {args: []string{"--ignore-root-user"}, code: -1, stderrContains: "Processes owned by root will not be killed", stdoutContains: memReport}, {args: []string{"--sort-by-rss"}, code: -1, stderrContains: "Find process with the largest rss", stdoutContains: memReport}, {args: []string{"-i"}, code: -1, stderrContains: "Option -i is ignored"},