This method can be used for plugins to get control after the server
has forked and changed user but before it accepts a connection. This
is very late and the only real use for this is for a plugin to create
background threads for its own use.
---
docs/nbdkit-filter.pod | 20 +++++++++++++++-----
docs/nbdkit-plugin.pod | 27 ++++++++++++++++++++++++++-
include/nbdkit-filter.h | 2 ++
include/nbdkit-plugin.h | 2 ++
server/filters.c | 24 ++++++++++++++++++++++++
server/internal.h | 1 +
server/main.c | 3 +++
server/plugins.c | 16 ++++++++++++++++
tests/test-layers-filter.c | 11 ++++++++++-
tests/test-layers-plugin.c | 8 ++++++++
tests/test-layers.c | 12 ++++++++++++
11 files changed, 119 insertions(+), 7 deletions(-)
diff --git a/docs/nbdkit-filter.pod b/docs/nbdkit-filter.pod
index 07ede47..4f10494 100644
--- a/docs/nbdkit-filter.pod
+++ b/docs/nbdkit-filter.pod
@@ -126,8 +126,8 @@ which is required.
=head1 NEXT PLUGIN
-F<nbdkit-filter.h> defines three function types
-(C<nbdkit_next_config>, C<nbdkit_next_config_complete>,
+F<nbdkit-filter.h> defines four function types (C<nbdkit_next_config>,
+C<nbdkit_next_config_complete>, C<nbdkit_ready_to_serve>,
C<nbdkit_next_open>) and a structure called C<struct nbdkit_next_ops>.
These abstract the next plugin or filter in the chain. There is also
an opaque pointer C<nxdata> which must be passed along when calling
@@ -135,9 +135,10 @@ these functions.
=head2 Next config, open and close
-The filter’s C<.config>, C<.config_complete> and C<.open> methods may
-only call the next C<.config>, C<.config_complete> and C<.open> method
-in the chain (optionally for C<.config>).
+The filter’s C<.config>, C<.config_complete>, C<.ready_to_serve> and
+C<.open> methods may only call the next C<.config>,
+C<.config_complete>, C<.ready_to_serve> and C<.open> method in the
+chain (optionally for C<.config>).
The filter’s C<.close> method is called when an old connection closed,
and this has no C<next> parameter because it cannot be
@@ -284,6 +285,15 @@ filter doesn't slow down other filters or plugins.
If there is an error, C<.thread_model> should call C<nbdkit_error>
with an error message and return C<-1>.
+=head2 C<.ready_to_serve>
+
+ int (*ready_to_serve) (nbdkit_next_ready_to_serve *next, void *nxdata);
+
+This intercepts the plugin C<.ready_to_serve> method.
+
+If there is an error, C<.ready_to_serve> should call C<nbdkit_error>
+with an error message and return C<-1>.
+
=head2 C<.open>
void * (*open) (nbdkit_next_open *next, void *nxdata,
diff --git a/docs/nbdkit-plugin.pod b/docs/nbdkit-plugin.pod
index e34ffd1..3ae1303 100644
--- a/docs/nbdkit-plugin.pod
+++ b/docs/nbdkit-plugin.pod
@@ -142,6 +142,13 @@ before any connections are accepted. However, during C<nbdkit
C<.config_complete> (so a plugin which determines the results from a
script must be prepared for a missing script).
+=item C<.ready_to_serve>
+
+This callback is called after nbdkit has forked into the background
+and changed user, and before it accepts any client connection. If the
+plugin needs to create its own background threads then this is a good
+place to do that.
+
=item C<.open>
A new client has connected.
@@ -408,7 +415,8 @@ Any other bare parameters give errors.
This optional callback is called after all the configuration has been
passed to the plugin. It is a good place to do checks, for example
-that the user has passed the required parameters to the plugin.
+that the user has passed the required parameters to the plugin. It is
+also a good place to do general start-up work.
If there is an error, C<.config_complete> should call C<nbdkit_error>
with an error message and return C<-1>.
@@ -437,6 +445,23 @@ are silently ignored.
If there is an error, C<.thread_model> should call C<nbdkit_error>
with an error message and return C<-1>.
+=head2 C<.ready_to_serve>
+
+ int ready_to_serve (void);
+
+This optional callback is called after nbdkit has forked into the
+background and changed user, and before it accepts any client
+connection.
+
+If the plugin needs to create its own background threads then this is
+a good place to do that. However because nbdkit may have already
+forked into the background and so it is difficult to communicate
+errors back to the user, this is B<not> a good place to do general
+start-up work (use C<.config_complete> instead).
+
+If there is an error, C<.ready_to_serve> should call C<nbdkit_error>
+with an error message and return C<-1>.
+
=head2 C<.open>
void *open (int readonly);
diff --git a/include/nbdkit-filter.h b/include/nbdkit-filter.h
index c1930c1..7b3a64f 100644
--- a/include/nbdkit-filter.h
+++ b/include/nbdkit-filter.h
@@ -64,6 +64,7 @@ extern struct nbdkit_extent nbdkit_get_extent (const struct
nbdkit_extents *,
typedef int nbdkit_next_config (void *nxdata,
const char *key, const char *value);
typedef int nbdkit_next_config_complete (void *nxdata);
+typedef int nbdkit_next_ready_to_serve (void *nxdata);
typedef int nbdkit_next_open (void *nxdata,
int readonly);
@@ -129,6 +130,7 @@ struct nbdkit_filter {
int (*config_complete) (nbdkit_next_config_complete *next, void *nxdata);
const char *config_help;
int (*thread_model) (void);
+ int (*ready_to_serve) (nbdkit_next_ready_to_serve *next, void *nxdata);
void * (*open) (nbdkit_next_open *next, void *nxdata,
int readonly);
diff --git a/include/nbdkit-plugin.h b/include/nbdkit-plugin.h
index 45ae705..746d9a1 100644
--- a/include/nbdkit-plugin.h
+++ b/include/nbdkit-plugin.h
@@ -134,6 +134,8 @@ struct nbdkit_plugin {
int (*thread_model) (void);
int (*can_fast_zero) (void *handle);
+
+ int (*ready_to_serve) (void);
};
extern void nbdkit_set_error (int err);
diff --git a/server/filters.c b/server/filters.c
index 1ac4a5f..ed00d11 100644
--- a/server/filters.c
+++ b/server/filters.c
@@ -190,6 +190,29 @@ plugin_magic_config_key (struct backend *b)
return b->next->magic_config_key (b->next);
}
+static int
+next_ready_to_serve (void *nxdata)
+{
+ struct backend *b = nxdata;
+ b->ready_to_serve (b);
+ return 0;
+}
+
+static void
+filter_ready_to_serve (struct backend *b)
+{
+ struct backend_filter *f = container_of (b, struct backend_filter, backend);
+
+ debug ("%s: ready_to_serve", b->name);
+
+ if (f->filter.ready_to_serve) {
+ if (f->filter.ready_to_serve (next_ready_to_serve, b->next) == -1)
+ exit (EXIT_FAILURE);
+ }
+ else
+ b->next->ready_to_serve (b->next);
+}
+
static int
next_open (void *nxdata, int readonly)
{
@@ -668,6 +691,7 @@ static struct backend filter_functions = {
.config = filter_config,
.config_complete = filter_config_complete,
.magic_config_key = plugin_magic_config_key,
+ .ready_to_serve = filter_ready_to_serve,
.open = filter_open,
.prepare = filter_prepare,
.finalize = filter_finalize,
diff --git a/server/internal.h b/server/internal.h
index 167da59..4c9419d 100644
--- a/server/internal.h
+++ b/server/internal.h
@@ -312,6 +312,7 @@ struct backend {
void (*config) (struct backend *, const char *key, const char *value);
void (*config_complete) (struct backend *);
const char *(*magic_config_key) (struct backend *);
+ void (*ready_to_serve) (struct backend *);
void *(*open) (struct backend *, struct connection *conn, int readonly);
int (*prepare) (struct backend *, struct connection *conn, void *handle,
int readonly);
diff --git a/server/main.c b/server/main.c
index 5623149..935d422 100644
--- a/server/main.c
+++ b/server/main.c
@@ -857,6 +857,7 @@ start_serving (void)
for (i = 0; i < nr_socks; ++i)
socks[i] = FIRST_SOCKET_ACTIVATION_FD + i;
change_user ();
+ backend->ready_to_serve (backend);
write_pidfile ();
accept_incoming_connections (socks, nr_socks);
free_listening_sockets (socks, nr_socks); /* also closes them */
@@ -866,6 +867,7 @@ start_serving (void)
/* Handling a single connection on stdin/stdout. */
if (listen_stdin) {
change_user ();
+ backend->ready_to_serve (backend);
write_pidfile ();
threadlocal_new_server_thread ();
if (handle_single_connection (0, 1) == -1)
@@ -882,6 +884,7 @@ start_serving (void)
run_command ();
change_user ();
fork_into_background ();
+ backend->ready_to_serve (backend);
write_pidfile ();
accept_incoming_connections (socks, nr_socks);
free_listening_sockets (socks, nr_socks);
diff --git a/server/plugins.c b/server/plugins.c
index 87daaf2..bb0269d 100644
--- a/server/plugins.c
+++ b/server/plugins.c
@@ -158,6 +158,7 @@ plugin_dump_fields (struct backend *b)
HAS (config);
HAS (config_complete);
HAS (config_help);
+ HAS (ready_to_serve);
HAS (open);
HAS (close);
HAS (get_size);
@@ -233,6 +234,20 @@ plugin_magic_config_key (struct backend *b)
return p->plugin.magic_config_key;
}
+static void
+plugin_ready_to_serve (struct backend *b)
+{
+ struct backend_plugin *p = container_of (b, struct backend_plugin, backend);
+
+ debug ("%s: ready_to_serve", b->name);
+
+ if (!p->plugin.ready_to_serve)
+ return;
+
+ if (p->plugin.ready_to_serve () == -1)
+ exit (EXIT_FAILURE);
+}
+
static void *
plugin_open (struct backend *b, struct connection *conn, int readonly)
{
@@ -656,6 +671,7 @@ static struct backend plugin_functions = {
.config = plugin_config,
.config_complete = plugin_config_complete,
.magic_config_key = plugin_magic_config_key,
+ .ready_to_serve = plugin_ready_to_serve,
.open = plugin_open,
.prepare = plugin_prepare,
.finalize = plugin_finalize,
diff --git a/tests/test-layers-filter.c b/tests/test-layers-filter.c
index d590cf9..315fff3 100644
--- a/tests/test-layers-filter.c
+++ b/tests/test-layers-filter.c
@@ -65,7 +65,7 @@ test_layers_filter_config (nbdkit_next_config *next, void *nxdata,
static int
test_layers_filter_config_complete (nbdkit_next_config_complete *next,
- void *nxdata)
+ void *nxdata)
{
DEBUG_FUNCTION;
return next (nxdata);
@@ -74,6 +74,14 @@ test_layers_filter_config_complete (nbdkit_next_config_complete *next,
#define test_layers_filter_config_help \
"test_layers_" layer "_config_help"
+static int
+test_layers_filter_ready_to_serve (nbdkit_next_ready_to_serve *next,
+ void *nxdata)
+{
+ DEBUG_FUNCTION;
+ return next (nxdata);
+}
+
static void *
test_layers_filter_open (nbdkit_next_open *next, void *nxdata, int readonly)
{
@@ -267,6 +275,7 @@ static struct nbdkit_filter filter = {
.config = test_layers_filter_config,
.config_complete = test_layers_filter_config_complete,
.config_help = test_layers_filter_config_help,
+ .ready_to_serve = test_layers_filter_ready_to_serve,
.open = test_layers_filter_open,
.close = test_layers_filter_close,
.prepare = test_layers_filter_prepare,
diff --git a/tests/test-layers-plugin.c b/tests/test-layers-plugin.c
index e9ffd3b..62c0066 100644
--- a/tests/test-layers-plugin.c
+++ b/tests/test-layers-plugin.c
@@ -72,6 +72,13 @@ test_layers_plugin_config_complete (void)
#define test_layers_plugin_config_help "test_layers_plugin_config_help"
+static int
+test_layers_plugin_ready_to_serve (void)
+{
+ DEBUG_FUNCTION;
+ return 0;
+}
+
static void *
test_layers_plugin_open (int readonly)
{
@@ -224,6 +231,7 @@ static struct nbdkit_plugin plugin = {
.config = test_layers_plugin_config,
.config_complete = test_layers_plugin_config_complete,
.config_help = test_layers_plugin_config_help,
+ .ready_to_serve = test_layers_plugin_ready_to_serve,
.open = test_layers_plugin_open,
.close = test_layers_plugin_close,
.get_size = test_layers_plugin_get_size,
diff --git a/tests/test-layers.c b/tests/test-layers.c
index eac7f15..c12af47 100644
--- a/tests/test-layers.c
+++ b/tests/test-layers.c
@@ -282,6 +282,18 @@ main (int argc, char *argv[])
"test_layers_plugin_config_complete",
NULL);
+ /* ready_to_serve methods called in order. */
+ log_verify_seen_in_order
+ ("testlayersfilter3: ready_to_serve",
+ "filter3: test_layers_filter_ready_to_serve",
+ "testlayersfilter2: ready_to_serve",
+ "filter2: test_layers_filter_ready_to_serve",
+ "testlayersfilter1: ready_to_serve",
+ "filter1: test_layers_filter_ready_to_serve",
+ "testlayersplugin: ready_to_serve",
+ "test_layers_plugin_ready_to_serve",
+ NULL);
+
/* open methods called in outer-to-inner order, but thanks to next
* pointer, complete in inner-to-outer order. */
log_verify_seen_in_order
--
2.23.0