From 922a907926d1cb02a8a10c7cdb9917755c934c84 Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Thu, 22 Sep 2022 16:08:47 +0200 Subject: [PATCH] MINOR: fd: add a new function to only raise RLIMIT_NOFILE In issue #1866 an issue was reported under docker, by which a user cannot lower the number of FD needed. It looks like a restriction imposed in this environment, but it results in an error while it ought not have to in the case of shrinking. This patch adds a new function raise_rlim_nofile() that takes the desired new setting, compares it to the current one, and only calls setrlimit() if one of the values in the new setting is larger than the older one. As such it will continue to emit warnings and errors in case of failure to raise the limit but will never shrink it. This patch is only preliminary to another one, but will have to be backported where relevant (likely only 2.6). --- include/haproxy/fd.h | 3 +++ src/fd.c | 27 +++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/include/haproxy/fd.h b/include/haproxy/fd.h index 8925efb66e..2a5bcad28e 100644 --- a/include/haproxy/fd.h +++ b/include/haproxy/fd.h @@ -78,6 +78,9 @@ ssize_t fd_write_frag_line(int fd, size_t maxlen, const struct ist pfx[], size_t /* close all FDs starting from */ void my_closefrom(int start); +struct rlimit; +int raise_rlim_nofile(struct rlimit *old_limit, struct rlimit *new_limit); + int compute_poll_timeout(int next); void fd_leaving_poll(int wait_time, int status); diff --git a/src/fd.c b/src/fd.c index eb386a830d..f4f1bae81e 100644 --- a/src/fd.c +++ b/src/fd.c @@ -885,6 +885,33 @@ void my_closefrom(int start) } #endif // defined(USE_POLL) +/* Sets the RLIMIT_NOFILE setting to and returns the previous one + * in if the pointer is not NULL, even if set_rlimit() fails. The + * two pointers may point to the same variable as the copy happens after + * setting the new value. The value is only changed if at least one of the new + * limits is strictly higher than the current one, otherwise returns 0 without + * changing anything. The getrlimit() or setrlimit() syscall return value is + * returned and errno is preserved. + */ +int raise_rlim_nofile(struct rlimit *old_limit, struct rlimit *new_limit) +{ + struct rlimit limit = { }; + int ret = 0; + + ret = getrlimit(RLIMIT_NOFILE, &limit); + + if (ret == 0 && + (limit.rlim_max < new_limit->rlim_max || + limit.rlim_cur < new_limit->rlim_cur)) { + ret = setrlimit(RLIMIT_NOFILE, new_limit); + } + + if (old_limit) + *old_limit = limit; + + return ret; +} + /* Computes the bounded poll() timeout based on the next expiration timer * by bounding it to MAX_DELAY_MS. may equal TICK_ETERNITY. The pollers * just needs to call this function right before polling to get their timeout