---
docs/nbdkit-service.pod | 1 +
filters/exitlast/nbdkit-exitlast-filter.pod | 58 +++++++++++++++
filters/ip/nbdkit-ip-filter.pod | 1 +
filters/rate/nbdkit-rate-filter.pod | 1 +
configure.ac | 3 +
filters/exitlast/Makefile.am | 60 ++++++++++++++++
tests/Makefile.am | 3 +
filters/exitlast/exitlast.c | 80 +++++++++++++++++++++
tests/test-exitlast.sh | 66 +++++++++++++++++
TODO | 6 +-
10 files changed, 276 insertions(+), 3 deletions(-)
diff --git a/docs/nbdkit-service.pod b/docs/nbdkit-service.pod
index 44bc111f..ecc8e94b 100644
--- a/docs/nbdkit-service.pod
+++ b/docs/nbdkit-service.pod
@@ -143,6 +143,7 @@ L</SOCKET ACTIVATION>.
=head1 SEE ALSO
L<nbdkit(1)>,
+L<nbdkit-exitlast-filter(1)>,
L<nbdkit-ip-filter(1)>,
L<systemd(1)>,
L<systemd.socket(5)>,
diff --git a/filters/exitlast/nbdkit-exitlast-filter.pod
b/filters/exitlast/nbdkit-exitlast-filter.pod
new file mode 100644
index 00000000..e0fbd6ea
--- /dev/null
+++ b/filters/exitlast/nbdkit-exitlast-filter.pod
@@ -0,0 +1,58 @@
+=head1 NAME
+
+nbdkit-exitlast-filter - exit on last client connection
+
+=head1 SYNOPSIS
+
+ nbdkit --filter=exitlast PLUGIN
+
+=head1 DESCRIPTION
+
+C<nbdkit-exitlast-filter> is an nbdkit filter that causes nbdkit to
+exit when there are no more client connections. Note that it doesn't
+exit before the first client connection.
+
+One use for this is in combination with a superserver, to save
+resources when nbdkit is not in use (see L<nbdkit-service(1)>).
+Another use is to ensure nbdkit exits after the client has finished
+(but see also nbdkit-captive(1) for other ways to do this).
+
+=head1 PARAMETERS
+
+There are no parameters specific to nbdkit-exitlast-filter. Any
+parameters are passed through to and processed by the underlying
+plugin in the normal way.
+
+=head1 FILES
+
+=over 4
+
+=item F<$filterdir/nbdkit-exitlast-filter.so>
+
+The filter.
+
+Use C<nbdkit --dump-config> to find the location of C<$filterdir>.
+
+=back
+
+=head1 VERSION
+
+C<nbdkit-exitlast-filter> first appeared in nbdkit 1.20.
+
+=head1 SEE ALSO
+
+L<nbdkit(1)>,
+L<nbdkit-ip-filter(1)>,
+L<nbdkit-rate-filter(1)>,
+L<nbdkit-captive(1)>,
+L<nbdkit-service(1)>,
+L<nbdkit-filter(3)>,
+L<nbdkit-plugin(3)>.
+
+=head1 AUTHORS
+
+Richard W.M. Jones
+
+=head1 COPYRIGHT
+
+Copyright (C) 2020 Red Hat Inc.
diff --git a/filters/ip/nbdkit-ip-filter.pod b/filters/ip/nbdkit-ip-filter.pod
index b91633c6..1621ebec 100644
--- a/filters/ip/nbdkit-ip-filter.pod
+++ b/filters/ip/nbdkit-ip-filter.pod
@@ -145,6 +145,7 @@ C<nbdkit-ip-filter> first appeared in nbdkit 1.18.
=head1 SEE ALSO
L<nbdkit(1)>,
+L<nbdkit-exitlast-filter(1)>,
L<nbdkit-filter(3)>.
=head1 AUTHORS
diff --git a/filters/rate/nbdkit-rate-filter.pod b/filters/rate/nbdkit-rate-filter.pod
index e8bfa42b..acf7d8b0 100644
--- a/filters/rate/nbdkit-rate-filter.pod
+++ b/filters/rate/nbdkit-rate-filter.pod
@@ -129,6 +129,7 @@ C<nbdkit-rate-filter> first appeared in nbdkit 1.12.
L<nbdkit(1)>,
L<nbdkit-blocksize-filter(1)>,
L<nbdkit-delay-filter(1)>,
+L<nbdkit-exitlast-filter(1)>,
L<nbdkit-filter(3)>,
L<iptables(8)>,
L<tc(8)>.
diff --git a/configure.ac b/configure.ac
index 0b7d8b7c..0b980238 100644
--- a/configure.ac
+++ b/configure.ac
@@ -97,6 +97,7 @@ filters="\
cow \
delay \
error \
+ exitlast \
ext2 \
extentlist \
fua \
@@ -267,6 +268,7 @@ AC_CHECK_HEADERS([\
alloca.h \
byteswap.h \
endian.h \
+ stdatomic.h \
sys/endian.h \
sys/mman.h \
sys/prctl.h \
@@ -1022,6 +1024,7 @@ AC_CONFIG_FILES([Makefile
filters/cow/Makefile
filters/delay/Makefile
filters/error/Makefile
+ filters/exitlast/Makefile
filters/ext2/Makefile
filters/extentlist/Makefile
filters/fua/Makefile
diff --git a/filters/exitlast/Makefile.am b/filters/exitlast/Makefile.am
new file mode 100644
index 00000000..690008f1
--- /dev/null
+++ b/filters/exitlast/Makefile.am
@@ -0,0 +1,60 @@
+# nbdkit
+# Copyright (C) 2019-2020 Red Hat Inc.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# * Neither the name of Red Hat nor the names of its contributors may be
+# used to endorse or promote products derived from this software without
+# specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+include $(top_srcdir)/common-rules.mk
+
+EXTRA_DIST = nbdkit-exitlast-filter.pod
+
+filter_LTLIBRARIES = nbdkit-exitlast-filter.la
+
+nbdkit_exitlast_filter_la_SOURCES = \
+ exitlast.c \
+ $(top_srcdir)/include/nbdkit-filter.h \
+ $(NULL)
+
+nbdkit_exitlast_filter_la_CPPFLAGS = -I$(top_srcdir)/include
+nbdkit_exitlast_filter_la_CFLAGS = $(WARNINGS_CFLAGS)
+nbdkit_exitlast_filter_la_LDFLAGS = \
+ -module -avoid-version -shared \
+ -Wl,--version-script=$(top_srcdir)/filters/filters.syms \
+ $(NULL)
+
+if HAVE_POD
+
+man_MANS = nbdkit-exitlast-filter.1
+CLEANFILES += $(man_MANS)
+
+nbdkit-exitlast-filter.1: nbdkit-exitlast-filter.pod
+ $(PODWRAPPER) --section=1 --man $@ \
+ --html $(top_builddir)/html/$@.html \
+ $<
+
+endif HAVE_POD
diff --git a/tests/Makefile.am b/tests/Makefile.am
index eb51757a..2ac8fb31 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -1052,6 +1052,9 @@ TESTS += \
test-error-triggered.sh \
$(NULL)
+# exitlast filter test.
+TESTS += test-exitlast.sh
+
# extentlist filter test.
TESTS += test-extentlist.sh
diff --git a/filters/exitlast/exitlast.c b/filters/exitlast/exitlast.c
new file mode 100644
index 00000000..51efda03
--- /dev/null
+++ b/filters/exitlast/exitlast.c
@@ -0,0 +1,80 @@
+/* nbdkit
+ * Copyright (C) 2019-2020 Red Hat Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of Red Hat nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <nbdkit-filter.h>
+
+#ifdef HAVE_STDATOMIC_H
+#include <stdatomic.h>
+#else
+/* Some old platforms lack atomic types, but 32 bit ints are usually
+ * "atomic enough".
+ */
+#define _Atomic /**/
+#endif
+
+/* Counts client connections. When this drops to zero we exit. */
+static _Atomic unsigned connections;
+
+static void *
+exitlast_open (nbdkit_next_open *next, nbdkit_backend *nxdata,
+ int readonly)
+{
+ if (next (nxdata, readonly) == -1)
+ return NULL;
+
+ connections++;
+
+ return NBDKIT_HANDLE_NOT_NEEDED;
+}
+
+static void
+exitlast_close (void *handle)
+{
+ if (--connections == 0) {
+ nbdkit_debug ("exitlast: exiting on last client connection");
+ nbdkit_shutdown ();
+ }
+}
+
+static struct nbdkit_filter filter = {
+ .name = "exitlast",
+ .longname = "nbdkit exitlast filter",
+ .open = exitlast_open,
+ .close = exitlast_close,
+};
+
+NBDKIT_REGISTER_FILTER(filter)
diff --git a/tests/test-exitlast.sh b/tests/test-exitlast.sh
new file mode 100755
index 00000000..d24a177f
--- /dev/null
+++ b/tests/test-exitlast.sh
@@ -0,0 +1,66 @@
+#!/usr/bin/env bash
+# nbdkit
+# Copyright (C) 2019-2020 Red Hat Inc.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# * Neither the name of Red Hat nor the names of its contributors may be
+# used to endorse or promote products derived from this software without
+# specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+source ./functions.sh
+set -x
+
+requires qemu-img --version
+
+sock=`mktemp -u`
+files="exitlast.pid $sock"
+cleanup_fn rm -f $files
+
+# Start nbdkit with the exitlast filter.
+start_nbdkit -P exitlast.pid -U $sock --filter=exitlast memory size=1M
+
+pid=`cat exitlast.pid`
+
+# nbdkit should start up even though there are no client connections.
+if ! kill -s 0 $pid; then
+ echo "$0: nbdkit exited before first connection"
+ exit 1
+fi
+
+# Connect with a client.
+qemu-img info "nbd:unix:$sock" || exit 1
+
+# Now nbdkit should exit. Wait for it to do so.
+for i in {1..60}; do
+ if ! kill -s 0 $pid; then
+ break
+ fi
+ sleep 1
+done
+if kill -s 0 $pid; then
+ echo "$0: nbdkit did not exit after last client connection"
+ exit 1
+fi
diff --git a/TODO b/TODO
index 21cbfe5b..cd2e47a5 100644
--- a/TODO
+++ b/TODO
@@ -10,9 +10,6 @@ General ideas for improvements
sizes and threads, as that should make it easier to identify
systematic issues.
-* Exit on last connection (the default behaviour of qemu-nbd unless
- you use -t).
-
* Limit number of incoming connections (like qemu-nbd -e).
* For parallel plugins, only create threads on demand from parallel
@@ -173,6 +170,9 @@ Suggestions for filters
MBs of extra data)
https://github.com/facebook/zstd/issues/395#issuecomment-535875379
+* nbdkit-exitlast-filter could probably use a configurable timeout so
+ that it hangs around in case another connection comes along quickly.
+
nbdkit-rate-filter:
* allow other kinds of traffic shaping such as VBR
--
2.25.0