Plugins and filters may call this to initiate an asynchronous shutdown
of the server. This would only be used in the connected phase —
plugins should still call exit(3) directly for configuration failure.
It is equivalent to sending a kill signal to self, but it's cleaner to
have an API for this and better for potential future non-POSIX platforms.
---
docs/nbdkit-plugin.pod | 19 +++++++-
include/nbdkit-common.h | 1 +
include/nbdkit-plugin.h | 2 +-
tests/Makefile.am | 20 ++++++++
server/nbdkit.syms | 3 +-
server/quit.c | 8 +++-
tests/test-shutdown.sh | 71 ++++++++++++++++++++++++++++
tests/test-shutdown-plugin.c | 89 ++++++++++++++++++++++++++++++++++++
8 files changed, 209 insertions(+), 4 deletions(-)
diff --git a/docs/nbdkit-plugin.pod b/docs/nbdkit-plugin.pod
index 77f1a098..494d77f8 100644
--- a/docs/nbdkit-plugin.pod
+++ b/docs/nbdkit-plugin.pod
@@ -1058,6 +1058,23 @@ before unloading the plugin.
Note that it's not guaranteed this can always happen (eg. the server
might be killed by C<SIGKILL> or segfault).
+=head2 Requesting asynchronous shutdown
+
+Plugins and filters can call L<exit(3)> in the configuration phase
+(before and including C<.get_ready>, but not in connected callbacks).
+
+Once nbdkit has started serving connections, plugins and filters
+should not call L<exit(3)>. However they may instruct nbdkit to shut
+down by calling C<nbdkit_shutdown>:
+
+ void nbdkit_shutdown (void);
+
+This function requests an asynchronous shutdown and returns (I<note>
+that it does I<not> exit the process immediately). It ensures that
+the plugin and all filters are unloaded cleanly which may take some
+time. Further callbacks from nbdkit into the plugin or filter may
+occur after you have called this.
+
=head1 PARSING COMMAND LINE PARAMETERS
=head2 Parsing numbers
@@ -1443,4 +1460,4 @@ Pino Toscano
=head1 COPYRIGHT
-Copyright (C) 2013-2018 Red Hat Inc.
+Copyright (C) 2013-2020 Red Hat Inc.
diff --git a/include/nbdkit-common.h b/include/nbdkit-common.h
index 50f3dd4f..9d1d89d0 100644
--- a/include/nbdkit-common.h
+++ b/include/nbdkit-common.h
@@ -111,6 +111,7 @@ extern char *nbdkit_realpath (const char *path);
extern int nbdkit_nanosleep (unsigned sec, unsigned nsec);
extern const char *nbdkit_export_name (void);
extern int nbdkit_peer_name (struct sockaddr *addr, socklen_t *addrlen);
+extern void nbdkit_shutdown (void);
struct nbdkit_extents;
extern int nbdkit_add_extent (struct nbdkit_extents *,
diff --git a/include/nbdkit-plugin.h b/include/nbdkit-plugin.h
index 7e06a4b1..7ff7bcee 100644
--- a/include/nbdkit-plugin.h
+++ b/include/nbdkit-plugin.h
@@ -1,5 +1,5 @@
/* nbdkit
- * Copyright (C) 2013-2019 Red Hat Inc.
+ * Copyright (C) 2013-2020 Red Hat Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 2dc35309..eb51757a 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -265,6 +265,7 @@ TESTS += \
test-debug-flags.sh \
test-long-name.sh \
test-swap.sh \
+ test-shutdown.sh \
$(NULL)
check_PROGRAMS += \
@@ -278,6 +279,25 @@ test_socket_activation_CPPFLAGS = \
$(NULL)
test_socket_activation_CFLAGS = $(WARNINGS_CFLAGS)
+# check_LTLIBRARIES won't build a shared library (see automake manual).
+# So we have to do this and add a dependency.
+noinst_LTLIBRARIES += \
+ test-shutdown-plugin.la \
+ $(NULL)
+test-shutdown.sh: test-shutdown-plugin.la
+
+test_shutdown_plugin_la_SOURCES = \
+ test-shutdown-plugin.c \
+ $(top_srcdir)/include/nbdkit-plugin.h \
+ $(NULL)
+test_shutdown_plugin_la_CPPFLAGS = -I$(top_srcdir)/include
+test_shutdown_plugin_la_CFLAGS = $(WARNINGS_CFLAGS)
+# For use of the -rpath option, see:
+#
https://lists.gnu.org/archive/html/libtool/2007-07/msg00067.html
+test_shutdown_plugin_la_LDFLAGS = \
+ -module -avoid-version -shared -rpath /nowhere \
+ $(NULL)
+
endif HAVE_PLUGINS
# Test the header files can be included on their own.
diff --git a/server/nbdkit.syms b/server/nbdkit.syms
index 96c22c07..111223f2 100644
--- a/server/nbdkit.syms
+++ b/server/nbdkit.syms
@@ -1,5 +1,5 @@
# nbdkit
-# Copyright (C) 2018 Red Hat Inc.
+# Copyright (C) 2018-2020 Red Hat Inc.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
@@ -64,6 +64,7 @@
nbdkit_read_password;
nbdkit_realpath;
nbdkit_set_error;
+ nbdkit_shutdown;
nbdkit_vdebug;
nbdkit_verror;
diff --git a/server/quit.c b/server/quit.c
index bff8ac7c..13fef437 100644
--- a/server/quit.c
+++ b/server/quit.c
@@ -1,5 +1,5 @@
/* nbdkit
- * Copyright (C) 2019 Red Hat Inc.
+ * 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
@@ -104,3 +104,9 @@ handle_quit (int sig)
{
set_quit ();
}
+
+void
+nbdkit_shutdown (void)
+{
+ set_quit ();
+}
diff --git a/tests/test-shutdown.sh b/tests/test-shutdown.sh
new file mode 100755
index 00000000..fa09dce9
--- /dev/null
+++ b/tests/test-shutdown.sh
@@ -0,0 +1,71 @@
+#!/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
+requires qemu-io --version
+
+plugin=.libs/test-shutdown-plugin.so
+requires test -f $plugin
+
+sock=`mktemp -u`
+files="shutdown.pid $sock"
+cleanup_fn rm -f $files
+
+# Start nbdkit with the shutdown plugin.
+start_nbdkit -P shutdown.pid -U $sock $plugin
+
+pid=`cat shutdown.pid`
+
+# Reads are fine, nbdkit should still be running afterwards.
+for i in 1 2 3; do
+ qemu-img info "nbd:unix:$sock"
+ sleep 2
+ kill -s 0 $pid
+done
+
+# Writing should cause nbdkit to exit, although it may take time.
+qemu-io -f raw -c 'w -P 0x55 0 1024' "nbd:unix:$sock"
+
+# Wait for nbdkit process to exit
+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 $plugin did not exit as expected"
+ exit 1
+fi
diff --git a/tests/test-shutdown-plugin.c b/tests/test-shutdown-plugin.c
new file mode 100644
index 00000000..4ec98538
--- /dev/null
+++ b/tests/test-shutdown-plugin.c
@@ -0,0 +1,89 @@
+/* nbdkit
+ * Copyright (C) 2013-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 <string.h>
+
+#include <nbdkit-plugin.h>
+
+static void
+shutdown_unload (void)
+{
+ nbdkit_debug ("clean shutdown");
+}
+
+static void *
+shutdown_open (int readonly)
+{
+ return NBDKIT_HANDLE_NOT_NEEDED;
+}
+
+static int64_t
+shutdown_get_size (void *handle)
+{
+ return INT64_C (1024*1024);
+}
+
+#define THREAD_MODEL NBDKIT_THREAD_MODEL_PARALLEL
+
+static int
+shutdown_pread (void *handle, void *buf, uint32_t count, uint64_t offset)
+{
+ memset (buf, 0, count);
+ return 0;
+}
+
+/* Writing 0x55 to any location causes a shutdown. */
+static int
+shutdown_pwrite (void *handle, const void *buf, uint32_t count, uint64_t offset)
+{
+ if (memchr (buf, 0x55, count) != NULL) {
+ nbdkit_debug ("shutdown triggered!");
+ nbdkit_shutdown ();
+ }
+ return 0;
+}
+
+static struct nbdkit_plugin plugin = {
+ .name = "shutdown",
+ .version = PACKAGE_VERSION,
+ .unload = shutdown_unload,
+ .open = shutdown_open,
+ .get_size = shutdown_get_size,
+ .pread = shutdown_pread,
+ .pwrite = shutdown_pwrite,
+};
+
+NBDKIT_REGISTER_PLUGIN(plugin)
--
2.25.0