This callback effectively splits config_complete into "complete the
config" and "get ready to start serving". There's no strict need for
this new callback, but it makes the API slightly less confusing.
---
docs/nbdkit-filter.pod | 38 +++++++++++++++++++++++++-------------
docs/nbdkit-plugin.pod | 20 ++++++++++++++++----
include/nbdkit-filter.h | 2 ++
include/nbdkit-plugin.h | 2 ++
server/internal.h | 1 +
server/filters.c | 23 +++++++++++++++++++++++
server/main.c | 5 +++++
server/plugins.c | 16 ++++++++++++++++
tests/test-layers-filter.c | 9 +++++++++
tests/test-layers-plugin.c | 8 ++++++++
tests/test-layers.c | 12 ++++++++++++
11 files changed, 119 insertions(+), 17 deletions(-)
diff --git a/docs/nbdkit-filter.pod b/docs/nbdkit-filter.pod
index 4105b8b7..a9dffb56 100644
--- a/docs/nbdkit-filter.pod
+++ b/docs/nbdkit-filter.pod
@@ -127,22 +127,24 @@ which is required.
=head1 NEXT PLUGIN
F<nbdkit-filter.h> defines some function types (C<nbdkit_next_config>,
-C<nbdkit_next_config_complete>, C<nbdkit_next_preconnect>,
-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
-these functions. The value of C<nxdata> passed to C<.open> has a
-stable lifetime that lasts to the corresponding C<.close>, with all
-intermediate functions (such as C<.pread>) receiving the same value
-for convenience; the only exceptions where C<nxdata> is not reused are
-C<.config>, C<.config_complete>, and C<.preconnect>, which are called
-outside the lifetime of a connection.
+C<nbdkit_next_config_complete>, C<nbdkit_next_get_ready>,
+C<nbdkit_next_preconnect>, 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 these functions. The value of C<nxdata>
+passed to C<.open> has a stable lifetime that lasts to the
+corresponding C<.close>, with all intermediate functions (such as
+C<.pread>) receiving the same value for convenience; the only
+exceptions where C<nxdata> is not reused are C<.config>,
+C<.config_complete>, C<.get_ready>, and C<.preconnect>, which are
+called outside the lifetime of a connection.
=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<.get_ready> and
+C<.open> methods may only call the next C<.config>,
+C<.config_complete>, C<.get_ready> 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
@@ -292,6 +294,16 @@ 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<.get_ready>
+
+ int (*get_ready) (nbdkit_next_get_ready *next, void *nxdata);
+
+This intercepts the plugin C<.get_ready> method and can be used by the
+filter to get ready to serve requests.
+
+If there is an error, C<.get_ready> should call C<nbdkit_error> with
+an error message and return C<-1>.
+
=head2 C<.preconnect>
int (*preconnect) (nbdkit_next_preconnect *next, void *nxdata,
diff --git a/docs/nbdkit-plugin.pod b/docs/nbdkit-plugin.pod
index 41bffb7f..77f1a098 100644
--- a/docs/nbdkit-plugin.pod
+++ b/docs/nbdkit-plugin.pod
@@ -443,6 +443,18 @@ 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<.get_ready>
+
+ int get_ready (void);
+
+This optional callback is called before the server starts serving. It
+is called before the server forks or changes directory. It is the
+last chance to do any global preparation that is needed to serve
+connections.
+
+If there is an error, C<.get_ready> should call C<nbdkit_error> with
+an error message and return C<-1>.
+
=head2 C<.preconnect>
int preconnect (int readonly);
@@ -1182,8 +1194,8 @@ absolute path: if it is relative, then all this function does is
prepend the current working directory to the path, with no extra
checks.
-Note that this function works I<only> when used in the C<.config>, and
-C<.config_complete> callbacks.
+Note that this function works I<only> when used in the C<.config>,
+C<.config_complete> and C<.get_ready> callbacks.
If conversion was not possible, this calls C<nbdkit_error> and returns
C<NULL>. Note that this function does not check that the file exists.
@@ -1199,8 +1211,8 @@ absolute path, resolving symlinks. Under the hood it uses the
C<realpath> function, and thus it fails if the path does not exist,
or it is not possible to access to any of the components of the path.
-Note that this function works I<only> when used in the C<.config>, and
-C<.config_complete> callbacks.
+Note that this function works I<only> when used in the C<.config>,
+C<.config_complete> and C<.get_ready> callbacks.
If the path resolution was not possible, this calls C<nbdkit_error>
and returns C<NULL>.
diff --git a/include/nbdkit-filter.h b/include/nbdkit-filter.h
index a44c689b..ca58e496 100644
--- a/include/nbdkit-filter.h
+++ b/include/nbdkit-filter.h
@@ -62,6 +62,7 @@ typedef void nbdkit_backend;
typedef int nbdkit_next_config (nbdkit_backend *nxdata,
const char *key, const char *value);
typedef int nbdkit_next_config_complete (nbdkit_backend *nxdata);
+typedef int nbdkit_next_get_ready (nbdkit_backend *nxdata);
typedef int nbdkit_next_preconnect (nbdkit_backend *nxdata, int readonly);
typedef int nbdkit_next_open (nbdkit_backend *nxdata, int readonly);
@@ -143,6 +144,7 @@ struct nbdkit_filter {
nbdkit_backend *nxdata);
const char *config_help;
int (*thread_model) (void);
+ int (*get_ready) (nbdkit_next_get_ready *next, nbdkit_backend *nxdata);
int (*preconnect) (nbdkit_next_preconnect *next, nbdkit_backend *nxdata,
int readonly);
diff --git a/include/nbdkit-plugin.h b/include/nbdkit-plugin.h
index b4ecf658..7e06a4b1 100644
--- a/include/nbdkit-plugin.h
+++ b/include/nbdkit-plugin.h
@@ -136,6 +136,8 @@ struct nbdkit_plugin {
int (*can_fast_zero) (void *handle);
int (*preconnect) (int readonly);
+
+ int (*get_ready) (void);
};
extern void nbdkit_set_error (int err);
diff --git a/server/internal.h b/server/internal.h
index eaec31ba..d8a589f2 100644
--- a/server/internal.h
+++ b/server/internal.h
@@ -344,6 +344,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 (*get_ready) (struct backend *);
int (*preconnect) (struct backend *, int readonly);
void *(*open) (struct backend *, int readonly);
int (*prepare) (struct backend *, void *handle, int readonly);
diff --git a/server/filters.c b/server/filters.c
index 8985ebeb..f0371066 100644
--- a/server/filters.c
+++ b/server/filters.c
@@ -171,6 +171,28 @@ filter_config_complete (struct backend *b)
b->next->config_complete (b->next);
}
+static int
+next_get_ready (struct backend *b)
+{
+ b->get_ready (b);
+ return 0;
+}
+
+static void
+filter_get_ready (struct backend *b)
+{
+ struct backend_filter *f = container_of (b, struct backend_filter, backend);
+
+ debug ("%s: get_ready", b->name);
+
+ if (f->filter.get_ready) {
+ if (f->filter.get_ready (next_get_ready, b->next) == -1)
+ exit (EXIT_FAILURE);
+ }
+ else
+ b->next->get_ready (b->next);
+}
+
static int
filter_preconnect (struct backend *b, int readonly)
{
@@ -493,6 +515,7 @@ static struct backend filter_functions = {
.config = filter_config,
.config_complete = filter_config_complete,
.magic_config_key = plugin_magic_config_key,
+ .get_ready = filter_get_ready,
.preconnect = filter_preconnect,
.open = filter_open,
.prepare = filter_prepare,
diff --git a/server/main.c b/server/main.c
index 3cfd1e2e..6350d110 100644
--- a/server/main.c
+++ b/server/main.c
@@ -694,6 +694,11 @@ main (int argc, char *argv[])
exit (EXIT_FAILURE);
}
+ /* Tell the plugin that we are about to start serving. This must be
+ * called before we change user, fork, or open any sockets.
+ */
+ top->get_ready (top);
+
start_serving ();
top->free (top);
diff --git a/server/plugins.c b/server/plugins.c
index 16b4099b..78ed6723 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 (get_ready);
HAS (preconnect);
HAS (open);
HAS (close);
@@ -234,6 +235,20 @@ plugin_magic_config_key (struct backend *b)
return p->plugin.magic_config_key;
}
+static void
+plugin_get_ready (struct backend *b)
+{
+ struct backend_plugin *p = container_of (b, struct backend_plugin, backend);
+
+ debug ("%s: get_ready", b->name);
+
+ if (!p->plugin.get_ready)
+ return;
+
+ if (p->plugin.get_ready () == -1)
+ exit (EXIT_FAILURE);
+}
+
static int
plugin_preconnect (struct backend *b, int readonly)
{
@@ -670,6 +685,7 @@ static struct backend plugin_functions = {
.config = plugin_config,
.config_complete = plugin_config_complete,
.magic_config_key = plugin_magic_config_key,
+ .get_ready = plugin_get_ready,
.preconnect = plugin_preconnect,
.open = plugin_open,
.prepare = plugin_prepare,
diff --git a/tests/test-layers-filter.c b/tests/test-layers-filter.c
index 44f62c6e..53427d2a 100644
--- a/tests/test-layers-filter.c
+++ b/tests/test-layers-filter.c
@@ -81,6 +81,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_get_ready (nbdkit_next_get_ready *next,
+ void *nxdata)
+{
+ DEBUG_FUNCTION;
+ return next (nxdata);
+}
+
static int
test_layers_filter_preconnect (nbdkit_next_preconnect *next,
void *nxdata, int readonly)
@@ -349,6 +357,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,
+ .get_ready = test_layers_filter_get_ready,
.preconnect = test_layers_filter_preconnect,
.open = test_layers_filter_open,
.close = test_layers_filter_close,
diff --git a/tests/test-layers-plugin.c b/tests/test-layers-plugin.c
index 10cc6efe..8858bede 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_get_ready (void)
+{
+ DEBUG_FUNCTION;
+ return 0;
+}
+
static int
test_layers_plugin_preconnect (int readonly)
{
@@ -231,6 +238,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,
+ .get_ready = test_layers_plugin_get_ready,
.preconnect = test_layers_plugin_preconnect,
.open = test_layers_plugin_open,
.close = test_layers_plugin_close,
diff --git a/tests/test-layers.c b/tests/test-layers.c
index fafe68c4..33ae5a75 100644
--- a/tests/test-layers.c
+++ b/tests/test-layers.c
@@ -288,6 +288,18 @@ main (int argc, char *argv[])
"test_layers_plugin_config_complete",
NULL);
+ /* get_ready methods called in order. */
+ log_verify_seen_in_order
+ ("testlayersfilter3: get_ready",
+ "filter3: test_layers_filter_get_ready",
+ "testlayersfilter2: get_ready",
+ "filter2: test_layers_filter_get_ready",
+ "testlayersfilter1: get_ready",
+ "filter1: test_layers_filter_get_ready",
+ "testlayersplugin: get_ready",
+ "test_layers_plugin_get_ready",
+ NULL);
+
/* preconnect methods called in outer-to-inner order, complete
* in inner-to-outer order.
*/
--
2.25.0