From 9699301fc3f16ef34570fa53e0863a2b91cdd3a6 Mon Sep 17 00:00:00 2001 From: Matt Whitlock Date: Sat, 4 Sep 2021 20:39:12 -0400 Subject: distccd: add --oom-score-adj option (Linux only) This new option sets /proc/self/oom_score_adj in prefork worker child processes. This can afford users a bit of protection against a scenario in which many concurrent compilation jobs cause an out-of-memory condition that prompts the kernel's OOM killer to kill a process that is not so "disposable" as a distccd worker. This commit adds a new Autoconf test to check whether the host system type (that is, the type of the system on which the compiled binaries are to be run) is Linux. The new --oom-score-adj option is implemented only if the host system type is Linux. --- configure.ac | 12 ++++++++++++ man/distccd.1 | 7 +++++++ src/dopt.c | 11 +++++++++++ src/dopt.h | 4 ++++ src/prefork.c | 11 +++++++++++ src/setuid.c | 9 +++++++++ 6 files changed, 54 insertions(+) diff --git a/configure.ac b/configure.ac index 948946b..4d709e0 100644 --- a/configure.ac +++ b/configure.ac @@ -15,6 +15,18 @@ AC_INIT(distcc, 3.4, distcc@lists.samba.org) AC_CONFIG_HEADERS(src/config.h) AC_CONFIG_MACRO_DIRS([m4]) +AC_CANONICAL_HOST +AC_MSG_CHECKING([if the host system type is Linux]) +case "${host_os}" in +linux*) + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_LINUX, 1, [Define to 1 if the host system type is Linux.]) + ;; +*) + AC_MSG_RESULT(no) + ;; +esac + # FreeBSD installs its version of libpopt into /usr/local/, but does # not put that on the default library and header path. # Solaris doesn't even ship libpopt. We used to add that path if diff --git a/man/distccd.1 b/man/distccd.1 index 4ff4e44..9cf8d1c 100644 --- a/man/distccd.1 +++ b/man/distccd.1 @@ -123,6 +123,13 @@ the machine. NICENESS is an increment to the current priority of the process. The range of priorities depends on the operating system but is typically 0 to 20. By default the niceness is increased by 5. .TP +.B --oom-score-adj ADJ +Alters the kernel's out-of-memory killer score adjustment on worker +processes. ADJ is an integer ranging from -1000 to 1000, with greater +values indicating a greater preference for killing these processes in an +out-of-memory scenario. By default the score adjustment is inherited +from the process that started the distccd daemon. (Linux only.) +.TP .B -p, --port PORT Set the TCP port to listen on, rather than the default of 3632. (Daemon mode only.) diff --git a/src/dopt.c b/src/dopt.c index d680ad5..1e92071 100644 --- a/src/dopt.c +++ b/src/dopt.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -49,6 +50,10 @@ int opt_niceness = 5; /* default */ +#ifdef HAVE_LINUX +int opt_oom_score_adj = INT_MIN; /* default is not to change */ +#endif + /** * Number of children running jobs on this machine. If zero (recommended), * then dynamically set from the number of CPUs. @@ -154,6 +159,9 @@ const struct poptOption options[] = { { "no-detach", 0, POPT_ARG_NONE, &opt_no_detach, 0, 0, 0 }, { "no-fifo", 0, POPT_ARG_NONE, &opt_no_fifo, 0, 0, 0 }, { "no-fork", 0, POPT_ARG_NONE, &opt_no_fork, 0, 0, 0 }, +#ifdef HAVE_LINUX + { "oom-score-adj",0, POPT_ARG_INT, &opt_oom_score_adj, 0, 0, 0 }, +#endif { "pid-file", 'P', POPT_ARG_STRING, &arg_pid_file, 0, 0, 0 }, { "port", 'p', POPT_ARG_INT, &arg_port, 0, 0, 0 }, #ifdef HAVE_GSSAPI @@ -191,6 +199,9 @@ static void distccd_show_usage(void) #endif " -P, --pid-file FILE save daemon process id to file\n" " -N, --nice LEVEL lower priority, 20=most nice\n" +#ifdef HAVE_LINUX +" --oom-score-adj ADJ set OOM score adjustment, -1000 to 1000\n" +#endif " --user USER if run by root, change to this persona\n" " --jobs, -j LIMIT maximum tasks at any time\n" " --job-lifetime SECONDS maximum lifetime of a compile request\n" diff --git a/src/dopt.h b/src/dopt.h index 70db3ca..139167b 100644 --- a/src/dopt.h +++ b/src/dopt.h @@ -46,6 +46,10 @@ extern int opt_lifetime; extern char *opt_listen_addr; extern int opt_niceness; +#ifdef HAVE_LINUX +extern int opt_oom_score_adj; +#endif + #ifdef HAVE_AVAHI extern int opt_zeroconf; #endif diff --git a/src/prefork.c b/src/prefork.c index d7e1d84..d4d70d5 100644 --- a/src/prefork.c +++ b/src/prefork.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include @@ -149,6 +150,16 @@ static int dcc_preforked_child(int listen_fd) const time_t child_lifetime = 60 /* seconds */; start = now = time(NULL); +#ifdef HAVE_LINUX + if (opt_oom_score_adj != INT_MIN) { + FILE *f = fopen("/proc/self/oom_score_adj", "w"); + if (!f || fprintf(f, "%d\n", opt_oom_score_adj) < 0) + rs_log_warning("failed to set oom_score_adj: %s", strerror(errno)); + if (f) + fclose(f); + } +#endif + for (ireq = 0; ireq < child_requests || now - start < child_lifetime; ireq++) { int acc_fd; struct dcc_sockaddr_storage cli_addr; diff --git a/src/setuid.c b/src/setuid.c index d664b84..44bc850 100644 --- a/src/setuid.c +++ b/src/setuid.c @@ -139,6 +139,15 @@ int dcc_discard_root(void) return EXIT_SETUID_FAILED; } +#ifdef HAVE_LINUX + /* On Linux changing the effective user or group ID clears the process's + * "dumpable" flag, which makes all files in the /proc/self/ directory + * owned by root and therefore unmodifiable by the process itself. + * Set the flag again here so we can, e.g., change oom_score_adj. */ + if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) < 0) + rs_log_warning("failed to restore dumpable process flag: %s", strerror(errno)); +#endif + #ifdef __linux__ if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == 0) rs_trace("successfully set no_new_privs"); -- cgit v1.2.1