[DTrace-devel] [PATCH v2 2/2] daemon: support libfuse 2

Nick Alcock nick.alcock at oracle.com
Tue Nov 8 13:12:11 UTC 2022


So it turns out that some people want to use libfuse 2 still, even
though it's been obsoleted by libfuse 3 for nearly a decade.  This turns
out to be not terribly difficult!

There are two major changess that impact us (ignoring the options-
parsing thing fixed in the last commit):

 - libfuse 2 has a 'channel' abstraction in which the fd to the kernel
   is nested.  libfuse 3 dropped it because it turned out there was only
   ever one of them so the abstraction was redundant, but that makes it
   easy for us to pick the fd out: we can grab the fuse_session_next_chan()
   without ever worrying that we might have to call that function more
   than once or deal with more than one channel.

   This changes the main read loop() enough that we can just implement
   it twice.

 - that's good because of another annoying difference: OL carries a
   NUMA-aware API which actually changes the public API of libfuse's
   fuse_session_receive_buf function.  This is hard to check for because
   the extra functions you need to call to actually turn this behaviour
   on were never added to the symbol version file, so you can't link
   with them.  So we detect these functions using the macros just added
   to do straight compilation checking, with no linking, and use their
   presence as a clue that fuse_session_receive_buf has the NUMA-aware
   API change.  (We don't use the NUMA stuff: it's only useful if you're
   using a multithreaded event loop, which we're not.)

We currently handle systems with both libfuse 2 and 3 by checking that
libfuse 3 is available, and if not, falling back to libfuse 2 (and if
that isn't present either you get a link failure).  Thus libfuse 3 is
preferred if both are present.  There is an additional libfuse2=yes
build option if you want to force the use of the old libfuse even when
the newer one is present.

One extra bit of complexity: we call pkg-config on whatever libfuse we
hope is present, and this emits very noisy multi-line error messages if
it isn't present: so suppress execution of all the dtprobed build rules
until the tree is configured and we have some idea of which libfuse is
there.

Signed-off-by: Nick Alcock <nick.alcock at oracle.com>
---
 Makeconfig          |  4 +++
 Makeoptions         |  9 ++++-
 dtprobed/Build      | 20 +++++++++--
 dtprobed/dtprobed.c | 88 ++++++++++++++++++++++++++++++++++++++++++++-
 dtrace.spec         | 15 ++++++--
 5 files changed, 129 insertions(+), 7 deletions(-)

diff --git a/Makeconfig b/Makeconfig
index 57d1d97988e6..0161d5f9f012 100644
--- a/Makeconfig
+++ b/Makeconfig
@@ -97,5 +97,9 @@ $(eval $(call check-symbol-rule,LIBCTF,ctf_open,ctf))
 $(eval $(call check-symbol-rule,STRRSTR,strrstr,c))
 $(eval $(call check-symbol-rule,WAITFD,waitfd,c))
 $(eval $(call check-symbol-rule,LIBSYSTEMD,sd_notify,systemd))
+ifndef WANTS_LIBFUSE2
 $(eval $(call check-symbol-rule,FUSE_LOG,fuse_set_log_func,fuse3))
+$(eval $(call check-symbol-rule,LIBFUSE3,fuse_session_receive_buf,fuse3))
+endif
+$(eval $(call check-header-rule,FUSE_NUMA,fuse_set_numa,fuse/fuse_lowlevel))
 $(eval $(call check-header-symbol-rule,CLOSE_RANGE,close_range(3,~0U,0),c,unistd))
diff --git a/Makeoptions b/Makeoptions
index 715d0a2e54b6..cec68a0af2bc 100644
--- a/Makeoptions
+++ b/Makeoptions
@@ -9,13 +9,16 @@ debugging ?= no
 dof_dbg ?= no
 coverage ?= no
 verbose ?= no
+libfuse2 ?= no
 
 help::
 	@printf "Options:\n\n" >&2
 	@printf "make debugging=yes [targets]   Disable optimization to make debugger use easier\n" >&2
 	@printf "make coverage=yes [targets]    Turn on coverage support in the testsuite\n" >&2
 	@printf "make verbose=yes [target]      Enable verbose building\n" >&2
-	@printf "make dof_dbg=yes [targets]     Turn on especially noisy DOF parser debugging\n" >&2
+	@printf "make dof_dbg=yes [targets]     Turn on especially noisy DOF parser debugging\n\n" >&2
+	@printf "Dependencies:\n\n" >&2
+	@printf "make libfuse2=yes [targets]    Build against libfuse 2 even if libfuse 3 is found\n" >&2
 	@printf "\n" >&2
 
 ifneq ($(debugging),no)
@@ -34,3 +37,7 @@ endif
 ifeq ($(verbose),no)
 override MAKEFLAGS += --silent
 endif
+
+ifneq ($(libfuse2),no)
+override WANTS_LIBFUSE2 = yes
+endif
diff --git a/dtprobed/Build b/dtprobed/Build
index 6775267ec77f..7e3113fdd0b6 100644
--- a/dtprobed/Build
+++ b/dtprobed/Build
@@ -3,13 +3,28 @@
 # Licensed under the Universal Permissive License v 1.0 as shown at
 # http://oss.oracle.com/licenses/upl.
 
+# Temporarily suppress dtprobed builds before configury is done: we don't know
+# which fuse is safe to even look up with pkg-config without ugly messages.
+ifdef CONFIGURED
 CMDS += dtprobed
 
+# Support FUSE 2 as well as FUSE 3: use if explicitly requested or if
+# FUSE 3 is not found.
+ifdef WANTS_LIBFUSE2
+LIBFUSE := fuse
+else
+ifndef HAVE_LIBFUSE3
+LIBFUSE := fuse
+else
+LIBFUSE := fuse3
+endif
+endif
+
 dtprobed_DIR := $(current-dir)
 dtprobed_TARGET = dtprobed
 dtprobed_CPPFLAGS := -I. -Idtprobed -Ilibproc -Ilibcommon -Ilibport
-dtprobed_CFLAGS := $(shell pkg-config --cflags fuse3)
-dtprobed_LIBS := -lcommon -lproc -lcommon -lport -lelf $(shell pkg-config --libs fuse3)
+dtprobed_CFLAGS := $(shell pkg-config --cflags $(LIBFUSE))
+dtprobed_LIBS := -lcommon -lproc -lcommon -lport -lelf $(shell pkg-config --libs $(LIBFUSE))
 dtprobed_DEPS := libproc.a libcommon.a libport.a
 dtprobed_SOURCES := dtprobed.c
 dtprobed_LIBSOURCES := libproc libcommon
@@ -24,6 +39,7 @@ dtprobed_SOURCES += rpl_fuse_log.c
 endif
 
 dtprobed.c_CFLAGS := -Wno-pedantic
+endif
 
 install::
 	mkdir -p $(INSTSBINDIR)
diff --git a/dtprobed/dtprobed.c b/dtprobed/dtprobed.c
index b76bc213e650..47dc1d3dc87a 100644
--- a/dtprobed/dtprobed.c
+++ b/dtprobed/dtprobed.c
@@ -21,7 +21,18 @@
 #include <linux/seccomp.h>
 #include <sys/syscall.h>
 
+/*
+ * Compatibility.  With libfuse 3, we use a few newer features that need at
+ * least version 31 (3.2.0): with libfuse 2, the default will suffice.  We also
+ * use the logging infrastructure added in libfuse 3.7, but provide an alternate
+ * implementation if not available.
+ */
+
+#if HAVE_LIBFUSE3
 #define FUSE_USE_VERSION 31
+#else /* libfuse 2 */
+/* Use the default (21).  */
+#endif
 
 #include <cuse_lowlevel.h>
 #include <fuse_lowlevel.h>
@@ -93,6 +104,28 @@ dt_debug_printf(const char *subsys, const char *fmt, va_list ap)
 	}
 }
 
+#if HAVE_LIBFUSE3
+static int
+session_fd(struct fuse_session *cuse_session)
+{
+	return fuse_session_fd(cuse_session);
+}
+#else /* libfuse 2 */
+static struct fuse_chan *cuse_chan;
+
+static void
+init_cuse_chan(struct fuse_session *cuse_session)
+{
+	cuse_chan = fuse_session_next_chan(cuse_session, NULL);
+}
+
+static int
+session_fd(struct fuse_session *cuse_session)
+{
+	return fuse_chan_fd(cuse_chan);
+}
+#endif
+
 /*
  * States for the ioctl processing loop, which gets repeatedly called due to the
  * request/reply nature of unrestricted FUSE ioctls.
@@ -141,6 +174,10 @@ setup_helper_device(int argc, char **argv, char *devname, dtprobed_userdata_t *u
 		return NULL;
 	}
 
+#ifndef HAVE_LIBFUSE3 /* libfuse 2 */
+	init_cuse_chan(cs);
+#endif
+
 	if (multithreaded) {
 		fprintf(stderr, "CUSE thinks dtprobed is multithreaded!\n");
 		fprintf(stderr, "This should never happen.\n");
@@ -202,7 +239,7 @@ dof_parser_start(int sync_fd)
 		 * Sandboxed parser child.  Close unwanted fds and nail into
 		 * seccomp jail.
 		 */
-		close(fuse_session_fd(cuse_session));
+		close(session_fd(cuse_session));
 		close(parser_in_pipe[1]);
 		close(parser_out_pipe[0]);
 		if (!foreground)
@@ -507,6 +544,7 @@ err:
 	return -1;
 }	
 
+#if HAVE_LIBFUSE3
 static int
 loop(void)
 {
@@ -538,6 +576,54 @@ loop(void)
 	fuse_session_reset(cuse_session);
 	return ret < 0 ? -1 : 0;
 }
+#else /* libfuse 2 */
+static int
+loop(void)
+{
+	struct pollfd fds[1];
+	void *buf;
+	size_t bufsize;
+	int ret = 0;
+
+	bufsize = fuse_chan_bufsize(cuse_chan);
+	buf = malloc(bufsize);
+	if (!buf) {
+		fprintf(stderr, "Cannot allocate memory for FUSE buffer\n");
+		return -1;
+	}
+
+	fds[0].fd = fuse_chan_fd(cuse_chan);
+	fds[0].events = POLLIN;
+
+	while (!fuse_session_exited(cuse_session)) {
+		struct fuse_buf fbuf = { .mem = buf, .size = bufsize };
+		struct fuse_chan *tmpch = cuse_chan;
+
+		if ((ret = poll(fds, 1, -1)) < 0)
+			break;
+
+		if (fds[0].revents != 0) {
+			if ((ret = fuse_session_receive_buf(cuse_session,
+							    &fbuf, &tmpch)) <= 0) {
+				if (ret == -EINTR)
+					continue;
+
+				break;
+			}
+
+#ifdef HAVE_FUSE_NUMA
+			fuse_session_process_buf(cuse_session, &fbuf, tmpch, 0);
+#else
+			fuse_session_process_buf(cuse_session, &fbuf, tmpch);
+#endif
+		}
+	}
+
+	free(buf);
+	fuse_session_reset(cuse_session);
+	return ret < 0 ? -1 : 0;
+}
+#endif
 
 static void
 usage(void)
diff --git a/dtrace.spec b/dtrace.spec
index 0d87591dddc6..f3be7c7db4d9 100644
--- a/dtrace.spec
+++ b/dtrace.spec
@@ -55,9 +55,18 @@ BuildRequires: rpm
 Name:         dtrace
 License:      Universal Permissive License (UPL), Version 1.0
 Group:        Development/Tools
-Requires:     cpp elfutils-libelf zlib libpcap fuse3 >= 3.2.0
-BuildRequires: glibc-headers bison flex zlib-devel elfutils-libelf-devel fuse3-devel >= 3.2.0 systemd systemd-devel
+Requires:     cpp elfutils-libelf zlib libpcap
+BuildRequires: glibc-headers bison flex zlib-devel elfutils-libelf-devel systemd systemd-devel
 BuildRequires: glibc-static %{glibc32} wireshark libpcap-devel valgrind-devel
+%if "%{?dist}" == ".el7"
+Requires:     fuse
+BuildRequires: fuse-devel
+%define maybe_use_fuse2 libfuse2=yes
+%else
+Requires:     fuse3 >= 3.2.0
+BuildRequires: fuse3-devel >= 3.2.0
+%define maybe_use_fuse2 %{nil}
+%endif
 %{?systemd_requires}
 BuildRequires: kernel%{variant}-devel = %{build_kernel}
 %if "%{?dist}" == ".el8"
@@ -156,7 +165,7 @@ it always tests the installed DTrace.
 %build
 make -j $(getconf _NPROCESSORS_ONLN) VERSION=%{version} \
 	KERNELDIRPREFIX=/usr/src/kernels KERNELDIRSUFFIX= \
-	KERNELS="%{kerneldirs}"
+	KERNELS="%{kerneldirs}" %{maybe_use_fuse2}
 
 # Force off debuginfo splitting.  We have no debuginfo in dtrace proper,
 # and the testsuite requires debuginfo for proper operation.
-- 
2.38.0.266.g481848f278




More information about the DTrace-devel mailing list