Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions kill.c
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,23 @@ bool is_larger(const poll_loop_args_t* args, const procinfo_t* victim, procinfo_
}
}

// Ignore processes owned by specific users?
if (args->ignore_uids_count > 0) {
if (cur->uid == PROCINFO_FIELD_NOT_SET) {
int res = get_uid(cur->pid);
if (res < 0) {
debug("%s: pid %d: error reading uid: %s\n", __func__, cur->pid, strerror(-res));
return false;
}
cur->uid = res;
}
for (int i = 0; i < args->ignore_uids_count; i++) {
if (cur->uid == args->ignore_uids[i]) {
return false;
}
}
}

{
bool res = parse_proc_pid_stat(&cur->stat, cur->pid);
if (!res) {
Expand Down
3 changes: 3 additions & 0 deletions kill.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ typedef struct {
bool kill_process_group;
/* do not kill processes owned by root */
bool ignore_root_user;
/* UIDs of users whose processes should be ignored */
int ignore_uids[64];
int ignore_uids_count;
/* find process with the largest rss */
bool sort_by_rss;
/* prefer/avoid killing these processes. NULL = no-op. */
Expand Down
61 changes: 61 additions & 0 deletions main.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ enum {
LONG_OPT_DRYRUN,
LONG_OPT_IGNORE,
LONG_OPT_IGNORE_ROOT,
LONG_OPT_IGNORE_USER,
LONG_OPT_USE_SYSLOG,
LONG_OPT_SORT_BY_RSS,
LONG_OPT_USE_KERNEL_OOM,
Expand All @@ -49,6 +50,40 @@ enum {
static int set_oom_score_adj(int);
static void poll_loop(const poll_loop_args_t* args);

// Resolve a username to a UID by reading /etc/passwd directly.
// Returns the UID on success, -1 on failure.
// This avoids getpwnam() which depends on NSS dynamic modules
// and does not work reliably in statically linked binaries.
static int lookup_user_uid(const char* name)
{
FILE* f = fopen("/etc/passwd", "r");
if (!f) {
return -1;
}
char line[1024];
int uid = -1;
while (fgets(line, sizeof(line), f)) {
// Format: name:password:uid:gid:gecos:home:shell
char* colon = strchr(line, ':');
if (!colon)
continue;
*colon = '\0';
if (strcmp(line, name) != 0)
continue;
// Found matching username, parse UID (third field)
char* rest = colon + 1;
// skip password field
colon = strchr(rest, ':');
if (!colon)
continue;
rest = colon + 1;
uid = (int)strtol(rest, NULL, 10);
break;
}
fclose(f);
return uid;
}

extern int trigger_kernel_oom(const poll_loop_args_t* args);

// Prevent Golang / Cgo name collision when the test suite runs -
Expand Down Expand Up @@ -196,6 +231,7 @@ int main(int argc, char* argv[])
{ "ignore", required_argument, NULL, LONG_OPT_IGNORE },
{ "dryrun", no_argument, NULL, LONG_OPT_DRYRUN },
{ "ignore-root-user", no_argument, NULL, LONG_OPT_IGNORE_ROOT },
{ "ignore-user", required_argument, NULL, LONG_OPT_IGNORE_USER },
{ "sort-by-rss", no_argument, NULL, LONG_OPT_SORT_BY_RSS },
{ "syslog", no_argument, NULL, LONG_OPT_USE_SYSLOG },
{ "kernel-oom", no_argument, NULL, LONG_OPT_USE_KERNEL_OOM },
Expand Down Expand Up @@ -295,6 +331,29 @@ int main(int argc, char* argv[])
args.ignore_root_user = true;
fprintf(stderr, "Processes owned by root will not be killed\n");
break;
case LONG_OPT_IGNORE_USER: {
if (args.ignore_uids_count >= (int)(sizeof(args.ignore_uids) / sizeof(args.ignore_uids[0]))) {
fatal(17, "--ignore-user: too many users specified (max %zu)\n",
sizeof(args.ignore_uids) / sizeof(args.ignore_uids[0]));
}
char* endptr = NULL;
long uid_val = strtol(optarg, &endptr, 10);
if (endptr != optarg && *endptr == '\0') {
// Numeric UID
args.ignore_uids[args.ignore_uids_count++] = (int)uid_val;
fprintf(stderr, "Processes owned by uid %ld will not be killed\n", uid_val);
} else {
// Username - read /etc/passwd directly (works with static builds)
int uid = lookup_user_uid(optarg);
if (uid < 0) {
fatal(17, "--ignore-user: user '%s' not found in /etc/passwd\n", optarg);
}
args.ignore_uids[args.ignore_uids_count++] = uid;
fprintf(stderr, "Processes owned by user '%s' (uid %d) will not be killed\n",
optarg, uid);
}
break;
}
case LONG_OPT_SORT_BY_RSS:
args.sort_by_rss = true;
fprintf(stderr, "Find process with the largest rss\n");
Expand Down Expand Up @@ -343,6 +402,8 @@ int main(int argc, char* argv[])
" -p set niceness of earlyoom to -20 and oom_score_adj to\n"
" -100\n"
" --ignore-root-user do not kill processes owned by root\n"
" --ignore-user USER do not kill processes owned by USER\n"
" (can be a username or numeric UID, repeatable)\n"
" --sort-by-rss find process with the largest rss (default oom_score)\n"
" --prefer REGEX prefer to kill processes matching REGEX\n"
" --avoid REGEX avoid killing processes matching REGEX\n"
Expand Down
Loading