On Wed, Jun 21, 2023 at 04:09:09PM +0200, Michal Privoznik wrote:
As of commit v5.9-rc1~160^2~3 the Linux kernel has close_range()
syscall, which closes not just one FD but whole range. Then, in
its commit glibc-2.34~115 glibc introduced closefrom() which is
just a wrapper over close_range(), but it allows us to use
FreeBSD-only implementation on Linux too, as both OS-es now have
the same function.
Signed-off-by: Michal Privoznik <mprivozn(a)redhat.com>
---
meson.build | 1 +
src/util/vircommand.c | 124 ++++++++++++++++++++++--------------------
2 files changed, 66 insertions(+), 59 deletions(-)
diff --git a/meson.build b/meson.build
index aa391e7178..a4b52b6156 100644
--- a/meson.build
+++ b/meson.build
@@ -573,6 +573,7 @@ libvirt_export_dynamic = cc.first_supported_link_argument([
# check availability of various common functions (non-fatal if missing)
functions = [
+ 'closefrom',
'elf_aux_info',
'explicit_bzero',
'fallocate',
diff --git a/src/util/vircommand.c b/src/util/vircommand.c
index 49abb53c28..b8b8d48f92 100644
--- a/src/util/vircommand.c
+++ b/src/util/vircommand.c
@@ -479,7 +479,68 @@ virExecCommon(virCommand *cmd, gid_t *groups, int ngroups)
return 0;
}
-# ifdef __linux__
+# ifdef WITH_CLOSEFROM
+# define USE_CLOSEFROM
+# else
+# define USE_GENERIC
+# endif
+
+
+# ifdef USE_CLOSEFROM
+static int
+virCommandMassClose(virCommand *cmd,
+ int childin,
+ int childout,
+ int childerr)
+{
+ int lastfd = -1;
+ int fd = -1;
+ size_t i;
+
+ /*
+ * Two phases of closing.
+ *
+ * The first (inefficient) phase iterates over FDs,
+ * preserving certain FDs we need to pass down, and
+ * closing others. The number of iterations is bounded
+ * to the number of the biggest FD we need to preserve.
+ *
+ * The second (speedy) phase uses closefrom() to cull
+ * all remaining FDs in the process.
+ *
+ * Usually the first phase will be fairly quick only
+ * processing a handful of low FD numbers, and thus using
+ * closefrom() is a massive win for high ulimit() NFILES
+ * values.
+ */
+ lastfd = MAX(lastfd, childin);
+ lastfd = MAX(lastfd, childout);
+ lastfd = MAX(lastfd, childerr);
+
+ for (i = 0; i < cmd->npassfd; i++)
+ lastfd = MAX(lastfd, cmd->passfd[i].fd);
+
+ for (fd = 0; fd <= lastfd; fd++) {
+ if (fd == childin || fd == childout || fd == childerr)
+ continue;
+ if (!virCommandFDIsSet(cmd, fd)) {
+ int tmpfd = fd;
+ VIR_MASS_CLOSE(tmpfd);
+ } else if (virSetInherit(fd, true) < 0) {
+ virReportSystemError(errno, _("failed to preserve fd %1$d"), fd);
+ return -1;
+ }
+ }
+
+ closefrom(lastfd + 1);
GLibC might have closefrom() but be in a container running on an
older kernel that lacks close_range syscall.
We could ignore return value on FreeBSD, but now on Linux we must
check the return value and fallback to the old codepath(s).
With regards,
Daniel
--
|:
https://berrange.com -o-
https://www.flickr.com/photos/dberrange :|
|:
https://libvirt.org -o-
https://fstop138.berrange.com :|
|:
https://entangle-photo.org -o-
https://www.instagram.com/dberrange :|