From: Andrea Bolognani Date: Thu, 4 Mar 2021 10:37:36 +0000 (+0100) Subject: util: Try to get limits from /proc X-Git-Tag: v7.2.0-rc1~56 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=90fe839f8a0b8301dc72a396be428db397ddf2c0;p=thirdparty%2Flibvirt.git util: Try to get limits from /proc Calling prlimit() requires elevated privileges, specifically CAP_SYS_RESOURCE, and getrlimit() only works for the current process which is too limiting for our needs; /proc/$pid/limits, on the other hand, can be read by any process, so implement parsing that file as a fallback for when prlimit() fails. This is useful in containerized environments. Signed-off-by: Andrea Bolognani Reviewed-by: Michal Privoznik --- diff --git a/src/util/virprocess.c b/src/util/virprocess.c index 8428c91182..ecb0a53296 100644 --- a/src/util/virprocess.c +++ b/src/util/virprocess.c @@ -757,6 +757,107 @@ virProcessSetRLimit(int resource, #endif /* WITH_SETRLIMIT */ #if WITH_GETRLIMIT +static const char* +virProcessLimitResourceToLabel(int resource) +{ + switch (resource) { +# if defined(RLIMIT_MEMLOCK) + case RLIMIT_MEMLOCK: + return "Max locked memory"; +# endif /* defined(RLIMIT_MEMLOCK) */ + +# if defined(RLIMIT_NPROC) + case RLIMIT_NPROC: + return "Max processes"; +# endif /* defined(RLIMIT_NPROC) */ + +# if defined(RLIMIT_NOFILE) + case RLIMIT_NOFILE: + return "Max open files"; +# endif /* defined(RLIMIT_NOFILE) */ + +# if defined(RLIMIT_CORE) + case RLIMIT_CORE: + return "Max core file size"; +# endif /* defined(RLIMIT_CORE) */ + + default: + return NULL; + } +} + +# if defined(__linux__) +static int +virProcessGetLimitFromProc(pid_t pid, + int resource, + struct rlimit *limit) +{ + g_autofree char *procfile = NULL; + g_autofree char *buf = NULL; + g_auto(GStrv) lines = NULL; + const char *label; + size_t i; + + if (!(label = virProcessLimitResourceToLabel(resource))) { + errno = EINVAL; + return -1; + } + + procfile = g_strdup_printf("/proc/%lld/limits", (long long)pid); + + if (virFileReadAllQuiet(procfile, 2048, &buf) < 0) { + /* virFileReadAllQuiet() already sets errno, so don't overwrite + * that and return immediately instead */ + return -1; + } + + lines = g_strsplit(buf, "\n", 0); + + for (i = 0; lines[i]; i++) { + g_autofree char *softLimit = NULL; + g_autofree char *hardLimit = NULL; + char *line = lines[i]; + unsigned long long tmp; + + if (!(line = STRSKIP(line, label))) + continue; + + if (sscanf(line, "%ms %ms %*s", &softLimit, &hardLimit) < 2) + goto error; + + if (STREQ(softLimit, "unlimited")) { + limit->rlim_cur = RLIM_INFINITY; + } else { + if (virStrToLong_ull(softLimit, NULL, 10, &tmp) < 0) + goto error; + limit->rlim_cur = tmp; + } + if (STREQ(hardLimit, "unlimited")) { + limit->rlim_max = RLIM_INFINITY; + } else { + if (virStrToLong_ull(hardLimit, NULL, 10, &tmp) < 0) + goto error; + limit->rlim_max = tmp; + } + } + + return 0; + + error: + errno = EIO; + return -1; +} +# else /* !defined(__linux__) */ +static int +virProcessGetLimitFromProc(pid_t pid G_GNUC_UNUSED, + int resource G_GNUC_UNUSED, + struct rlimit *limit G_GNUC_UNUSED) +{ + errno = ENOSYS; + return -1; +} +# endif /* !defined(__linux__) */ + static int virProcessGetLimit(pid_t pid, int resource, @@ -768,6 +869,15 @@ virProcessGetLimit(pid_t pid, if (virProcessPrLimit(pid, resource, NULL, old_limit) == 0) return 0; + /* For whatever reason, using prlimit() on another process - even + * when it's just to obtain the current limit rather than changing + * it - requires CAP_SYS_RESOURCE, which we might not have in a + * containerized environment; on the other hand, no particular + * permission is needed to poke around /proc, so try that if going + * through the syscall didn't work */ + if (virProcessGetLimitFromProc(pid, resource, old_limit) == 0) + return 0; + if (same_process && virProcessGetRLimit(resource, old_limit) == 0) return 0;