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

_______________________________________________
Libguestfs mailing list
Libguestfs@redhat.com
https://www.redhat.com/mailman/listinfo/libguestfs

Reply via email to