Richard W.M. Jones
2020-Feb-25 10:30 UTC
[Libguestfs] [PATCH nbdkit 0/5] server: Add .get_ready callback.
I like this change. I think we were overloading the config_complete method before to do two different things (complete configuration; do any allocation/housekeeping necessary before we can start serving). The only questions in my mind are whether we want this before 1.18, and whether the name ("get_ready") is a good one. Rich.
Richard W.M. Jones
2020-Feb-25 10:30 UTC
[Libguestfs] [PATCH nbdkit 1/5] server: Add .get_ready callback.
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
Richard W.M. Jones
2020-Feb-25 10:30 UTC
[Libguestfs] [PATCH nbdkit 2/5] Use .get_ready method in miscellaneous plugins and filters.
For plugins/filters where .config_complete was "overloaded" as a place to finish configuration and do some set up before we start serving, use the new .get_ready callback. --- plugins/floppy/floppy.c | 9 ++++++++- plugins/floppy/virtual-floppy.c | 8 ++++---- plugins/iso/iso.c | 10 +++++++--- plugins/linuxdisk/linuxdisk.c | 9 ++++++++- plugins/partitioning/partitioning.c | 9 ++++++++- plugins/streaming/streaming.c | 7 +++++++ plugins/vddk/vddk.c | 14 ++++++++++---- filters/log/log.c | 14 +++++++++++--- filters/rate/rate.c | 4 ++-- filters/stats/stats.c | 11 +++++++++-- 10 files changed, 74 insertions(+), 21 deletions(-) diff --git a/plugins/floppy/floppy.c b/plugins/floppy/floppy.c index 41a23644..1c2f645e 100644 --- a/plugins/floppy/floppy.c +++ b/plugins/floppy/floppy.c @@ -99,13 +99,19 @@ floppy_config_complete (void) return -1; } - return create_virtual_floppy (dir, label, &floppy); + return 0; } #define floppy_config_help \ "dir=<DIRECTORY> (required) The directory to serve.\n" \ "label=<LABEL> The volume label." \ +static int +floppy_get_ready (void) +{ + return create_virtual_floppy (dir, label, &floppy); +} + static void * floppy_open (int readonly) { @@ -204,6 +210,7 @@ static struct nbdkit_plugin plugin = { .config_complete = floppy_config_complete, .config_help = floppy_config_help, .magic_config_key = "dir", + .get_ready = floppy_get_ready, .open = floppy_open, .get_size = floppy_get_size, .can_multi_conn = floppy_can_multi_conn, diff --git a/plugins/floppy/virtual-floppy.c b/plugins/floppy/virtual-floppy.c index b5c29a46..fc0cafa8 100644 --- a/plugins/floppy/virtual-floppy.c +++ b/plugins/floppy/virtual-floppy.c @@ -256,10 +256,10 @@ visit (const char *dir, struct virtual_floppy *floppy) floppy->nr_dirs++; memset (&floppy->dirs[di], 0, sizeof (struct dir)); - /* Because this is called from config_complete, before nbdkit - * daemonizes or starts any threads, it's safe to use chdir here and - * greatly simplifies the code. However we must chdir back to the - * original directory at the end. + /* Because this is called from get_ready, before nbdkit daemonizes + * or starts any threads, it's safe to use chdir here and greatly + * simplifies the code. However we must chdir back to the original + * directory at the end. */ origdir = get_current_dir_name (); if (origdir == NULL) { diff --git a/plugins/iso/iso.c b/plugins/iso/iso.c index 5634bac9..92554ace 100644 --- a/plugins/iso/iso.c +++ b/plugins/iso/iso.c @@ -176,9 +176,6 @@ iso_config_complete (void) return -1; } - if (make_iso () == -1) - return -1; - return 0; } @@ -187,6 +184,12 @@ iso_config_complete (void) "params='<PARAMS>' Extra parameters to pass.\n" \ "prog=<ISOPROG> The program used to make ISOs." \ +static int +iso_get_ready (void) +{ + return make_iso (); +} + static void * iso_open (int readonly) { @@ -254,6 +257,7 @@ static struct nbdkit_plugin plugin = { .config_complete = iso_config_complete, .config_help = iso_config_help, .magic_config_key = "dir", + .get_ready = iso_get_ready, .open = iso_open, .get_size = iso_get_size, .can_multi_conn = iso_can_multi_conn, diff --git a/plugins/linuxdisk/linuxdisk.c b/plugins/linuxdisk/linuxdisk.c index 99dbc996..d7b52242 100644 --- a/plugins/linuxdisk/linuxdisk.c +++ b/plugins/linuxdisk/linuxdisk.c @@ -128,7 +128,7 @@ linuxdisk_config_complete (void) return -1; } - return create_virtual_disk (&disk); + return 0; } #define linuxdisk_config_help \ @@ -137,6 +137,12 @@ linuxdisk_config_complete (void) "type=ext2|ext3|ext4 The filesystem type.\n" \ "size=[+]<SIZE> The virtual filesystem size." +static int +linuxdisk_get_ready (void) +{ + return create_virtual_disk (&disk); +} + static void * linuxdisk_open (int readonly) { @@ -226,6 +232,7 @@ static struct nbdkit_plugin plugin = { .config_complete = linuxdisk_config_complete, .config_help = linuxdisk_config_help, .magic_config_key = "dir", + .get_ready = linuxdisk_get_ready, .open = linuxdisk_open, .get_size = linuxdisk_get_size, .can_multi_conn = linuxdisk_can_multi_conn, diff --git a/plugins/partitioning/partitioning.c b/plugins/partitioning/partitioning.c index 6e426b93..865acd28 100644 --- a/plugins/partitioning/partitioning.c +++ b/plugins/partitioning/partitioning.c @@ -269,13 +269,19 @@ partitioning_config_complete (void) return -1; } - return create_virtual_disk_layout (); + return 0; } #define partitioning_config_help \ "file=<FILENAME> (required) File(s) containing partitions\n" \ "partition-type=mbr|gpt Partition type" +static int +partitioning_get_ready (void) +{ + return create_virtual_disk_layout (); +} + /* Create the per-connection handle. */ static void * partitioning_open (int readonly) @@ -433,6 +439,7 @@ static struct nbdkit_plugin plugin = { .config_complete = partitioning_config_complete, .config_help = partitioning_config_help, .magic_config_key = "file", + .get_ready = partitioning_get_ready, .open = partitioning_open, .get_size = partitioning_get_size, .can_multi_conn = partitioning_can_multi_conn, diff --git a/plugins/streaming/streaming.c b/plugins/streaming/streaming.c index b2359540..71365c58 100644 --- a/plugins/streaming/streaming.c +++ b/plugins/streaming/streaming.c @@ -90,6 +90,12 @@ streaming_config_complete (void) return -1; } + return 0; +} + +static int +streaming_get_ready (void) +{ /* Open the file blindly. If this fails with ENOENT then we create a * FIFO and try again. */ @@ -248,6 +254,7 @@ static struct nbdkit_plugin plugin = { .config = streaming_config, .config_complete = streaming_config_complete, .config_help = streaming_config_help, + .get_ready = streaming_get_ready, .open = streaming_open, .close = streaming_close, .get_size = streaming_get_size, diff --git a/plugins/vddk/vddk.c b/plugins/vddk/vddk.c index 533c5605..7788a31b 100644 --- a/plugins/vddk/vddk.c +++ b/plugins/vddk/vddk.c @@ -60,8 +60,7 @@ int vddk_debug_extents; int vddk_debug_datapath = 1; /* For each VDDK API define a static global variable. These globals - * are initialized when the plugin is loaded (by - * vddk_config_complete). + * are initialized when the plugin is loaded (by vddk_get_ready). */ #define STUB(fn,ret,args) static ret (*fn) args #define OPTIONAL_STUB(fn,ret,args) static ret (*fn) args @@ -432,8 +431,6 @@ load_library (void) static int vddk_config_complete (void) { - VixError err; - if (filename == NULL) { nbdkit_error ("you must supply the file=<FILENAME> parameter " "after the plugin name on the command line"); @@ -506,6 +503,14 @@ vddk_config_complete (void) } } + return 0; +} + +static int +vddk_get_ready (void) +{ + VixError err; + load_library (); /* Initialize VDDK library. */ @@ -1017,6 +1022,7 @@ static struct nbdkit_plugin plugin = { .config_help = vddk_config_help, .magic_config_key = "file", .dump_plugin = vddk_dump_plugin, + .get_ready = vddk_get_ready, .open = vddk_open, .close = vddk_close, .get_size = vddk_get_size, diff --git a/filters/log/log.c b/filters/log/log.c index 7eb608c5..76d139eb 100644 --- a/filters/log/log.c +++ b/filters/log/log.c @@ -86,16 +86,23 @@ log_config (nbdkit_next_config *next, void *nxdata, return next (nxdata, key, value); } -/* Open the logfile. */ static int log_config_complete (nbdkit_next_config_complete *next, void *nxdata) { - int fd; - if (!logfilename) { nbdkit_error ("missing logfile= parameter for the log filter"); return -1; } + + return next (nxdata); +} + +/* Open the logfile. */ +static int +log_get_ready (nbdkit_next_get_ready *next, void *nxdata) +{ + int fd; + /* Using fopen("ae"/"we") would be more convenient, but as Haiku * still lacks that, use this instead. Atomicity is not essential * here since .config completes before threads that might fork, if @@ -444,6 +451,7 @@ static struct nbdkit_filter filter = { .config_complete = log_config_complete, .config_help = log_config_help, .unload = log_unload, + .get_ready = log_get_ready, .open = log_open, .close = log_close, .prepare = log_prepare, diff --git a/filters/rate/rate.c b/filters/rate/rate.c index 2b105f91..f71e9cf3 100644 --- a/filters/rate/rate.c +++ b/filters/rate/rate.c @@ -145,7 +145,7 @@ rate_config (nbdkit_next_config *next, void *nxdata, } static int -rate_config_complete (nbdkit_next_config_complete *next, void *nxdata) +rate_get_ready (nbdkit_next_get_ready *next, void *nxdata) { /* Initialize the global buckets. */ bucket_init (&read_bucket, rate, BUCKET_CAPACITY); @@ -312,8 +312,8 @@ static struct nbdkit_filter filter = { .longname = "nbdkit rate filter", .unload = rate_unload, .config = rate_config, - .config_complete = rate_config_complete, .config_help = rate_config_help, + .get_ready = rate_get_ready, .open = rate_open, .close = rate_close, .pread = rate_pread, diff --git a/filters/stats/stats.c b/filters/stats/stats.c index 0759cebe..05832561 100644 --- a/filters/stats/stats.c +++ b/filters/stats/stats.c @@ -201,13 +201,19 @@ stats_config (nbdkit_next_config *next, void *nxdata, static int stats_config_complete (nbdkit_next_config_complete *next, void *nxdata) { - int fd; - if (filename == NULL) { nbdkit_error ("stats filter requires statsfile parameter"); return -1; } + return next (nxdata); +} + +static int +stats_get_ready (nbdkit_next_get_ready *next, void *nxdata) +{ + int fd; + /* Using fopen("ae"/"we") would be more convenient, but as Haiku * still lacks that, use this instead. Atomicity is not essential * here since .config completes before threads that might fork, if @@ -372,6 +378,7 @@ static struct nbdkit_filter filter = { .config = stats_config, .config_complete = stats_config_complete, .config_help = stats_config_help, + .get_ready = stats_get_ready, .pread = stats_pread, .pwrite = stats_pwrite, .trim = stats_trim, -- 2.25.0
Richard W.M. Jones
2020-Feb-25 10:30 UTC
[Libguestfs] [PATCH nbdkit 3/5] ocaml: Implement get_ready method.
--- plugins/ocaml/ocaml.c | 25 +++++++++++++++++++++++++ plugins/ocaml/NBDKit.ml | 8 ++++++++ plugins/ocaml/NBDKit.mli | 2 ++ tests/test_ocaml_plugin.ml | 6 ++++++ 4 files changed, 41 insertions(+) diff --git a/plugins/ocaml/ocaml.c b/plugins/ocaml/ocaml.c index 619a678b..82003343 100644 --- a/plugins/ocaml/ocaml.c +++ b/plugins/ocaml/ocaml.c @@ -106,6 +106,8 @@ static value config_fn; static value config_complete_fn; static value thread_model_fn; +static value get_ready_fn; + static value preconnect_fn; static value open_fn; static value close_fn; @@ -238,6 +240,25 @@ thread_model_wrapper (void) CAMLreturnT (int, Int_val (rv)); } +static int +get_ready_wrapper (void) +{ + CAMLparam0 (); + CAMLlocal1 (rv); + + caml_leave_blocking_section (); + + rv = caml_callback_exn (get_ready_fn, Val_unit); + if (Is_exception_result (rv)) { + nbdkit_error ("%s", caml_format_exception (Extract_exception (rv))); + caml_enter_blocking_section (); + CAMLreturnT (int, -1); + } + + caml_enter_blocking_section (); + CAMLreturnT (int, 0); +} + static int preconnect_wrapper (int readonly) { @@ -799,6 +820,8 @@ SET(config) SET(config_complete) SET(thread_model) +SET(get_ready) + SET(preconnect) SET(open) SET(close) @@ -840,6 +863,8 @@ remove_roots (void) REMOVE (config_complete); REMOVE (thread_model); + REMOVE (get_ready); + REMOVE (preconnect); REMOVE (open); REMOVE (close); diff --git a/plugins/ocaml/NBDKit.ml b/plugins/ocaml/NBDKit.ml index e3b07ab5..ed636c72 100644 --- a/plugins/ocaml/NBDKit.ml +++ b/plugins/ocaml/NBDKit.ml @@ -69,6 +69,8 @@ type 'a plugin = { config_help : string; thread_model : (unit -> thread_model) option; + get_ready : (unit -> unit) option; + preconnect : (bool -> unit) option; open_connection : (bool -> 'a) option; close : ('a -> unit) option; @@ -111,6 +113,8 @@ let default_callbacks = { config_help = ""; thread_model = None; + get_ready = None; + preconnect = None; open_connection = None; close = None; @@ -152,6 +156,8 @@ external set_config_complete : (unit -> unit) -> unit = "ocaml_nbdkit_set_config external set_config_help : string -> unit = "ocaml_nbdkit_set_config_help" "noalloc" external set_thread_model : (unit -> thread_model) -> unit = "ocaml_nbdkit_set_thread_model" +external set_get_ready : (unit -> unit) -> unit = "ocaml_nbdkit_set_get_ready" + external set_preconnect : (bool -> unit) -> unit = "ocaml_nbdkit_set_preconnect" external set_open : (bool -> 'a) -> unit = "ocaml_nbdkit_set_open" external set_close : ('a -> unit) -> unit = "ocaml_nbdkit_set_close" @@ -214,6 +220,8 @@ let register_plugin plugin set_config_help plugin.config_help; may set_thread_model plugin.thread_model; + may set_get_ready plugin.get_ready; + may set_preconnect plugin.preconnect; may set_open plugin.open_connection; may set_close plugin.close; diff --git a/plugins/ocaml/NBDKit.mli b/plugins/ocaml/NBDKit.mli index 8cc4fed2..78019442 100644 --- a/plugins/ocaml/NBDKit.mli +++ b/plugins/ocaml/NBDKit.mli @@ -74,6 +74,8 @@ type 'a plugin = { config_help : string; thread_model : (unit -> thread_model) option; + get_ready : (unit -> unit) option; + preconnect : (bool -> unit) option; open_connection : (bool -> 'a) option; (* required *) close : ('a -> unit) option; diff --git a/tests/test_ocaml_plugin.ml b/tests/test_ocaml_plugin.ml index 3cf8fd90..753e51ab 100644 --- a/tests/test_ocaml_plugin.ml +++ b/tests/test_ocaml_plugin.ml @@ -19,6 +19,12 @@ let test_config_complete () let params = List.rev !params in assert (params = [ "a", "1"; "b", "2"; "c", "3" ]) +let test_get_ready () + (* We could allocate the disk here, but it's easier to allocate + * it statically above. + *) + NBDKit.debug "test ocaml plugin getting ready" + let test_open readonly NBDKit.debug "test ocaml plugin handle opened readonly=%b" readonly; () -- 2.25.0
Richard W.M. Jones
2020-Feb-25 10:30 UTC
[Libguestfs] [PATCH nbdkit 4/5] python: Implement get_ready method.
--- plugins/python/nbdkit-python-plugin.pod | 6 ++++++ plugins/python/python.c | 21 +++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/plugins/python/nbdkit-python-plugin.pod b/plugins/python/nbdkit-python-plugin.pod index 4065ec75..454a17ca 100644 --- a/plugins/python/nbdkit-python-plugin.pod +++ b/plugins/python/nbdkit-python-plugin.pod @@ -152,6 +152,12 @@ There are no arguments or return value. There are no arguments or return value. +=item C<get_ready> + +(Optional) + +There are no arguments or return value. + =item C<open> (Required) diff --git a/plugins/python/python.c b/plugins/python/python.c index 5e2e5269..72f37dd7 100644 --- a/plugins/python/python.c +++ b/plugins/python/python.c @@ -436,6 +436,25 @@ py_config_complete (void) return 0; } +static int +py_get_ready (void) +{ + PyObject *fn; + PyObject *r; + + if (callback_defined ("get_ready", &fn)) { + PyErr_Clear (); + + r = PyObject_CallObject (fn, NULL); + Py_DECREF (fn); + if (check_python_failure ("get_ready") == -1) + return -1; + Py_DECREF (r); + } + + return 0; +} + static void * py_open (int readonly) { @@ -889,6 +908,8 @@ static struct nbdkit_plugin plugin = { .config_complete = py_config_complete, .config_help = py_config_help, + .get_ready = py_get_ready, + .open = py_open, .close = py_close, -- 2.25.0
Richard W.M. Jones
2020-Feb-25 10:30 UTC
[Libguestfs] [PATCH nbdkit 5/5] eval, sh: Implement get_ready method.
--- plugins/eval/nbdkit-eval-plugin.pod | 2 ++ plugins/sh/nbdkit-sh-plugin.pod | 4 ++++ plugins/sh/methods.h | 1 + plugins/eval/eval.c | 2 ++ plugins/sh/methods.c | 25 +++++++++++++++++++++++++ plugins/sh/sh.c | 1 + tests/test-eval.sh | 1 + 7 files changed, 36 insertions(+) diff --git a/plugins/eval/nbdkit-eval-plugin.pod b/plugins/eval/nbdkit-eval-plugin.pod index a3601333..602fd3aa 100644 --- a/plugins/eval/nbdkit-eval-plugin.pod +++ b/plugins/eval/nbdkit-eval-plugin.pod @@ -100,6 +100,8 @@ features): =item B<flush=>SCRIPT +=item B<get_ready=>SCRIPT + =item B<get_size=>SCRIPT =item B<is_rotational=>SCRIPT diff --git a/plugins/sh/nbdkit-sh-plugin.pod b/plugins/sh/nbdkit-sh-plugin.pod index 2f265b05..ffd0310f 100644 --- a/plugins/sh/nbdkit-sh-plugin.pod +++ b/plugins/sh/nbdkit-sh-plugin.pod @@ -246,6 +246,10 @@ actually gets selected. If an error occurs, the script should output an error message and exit with status C<1>; unrecognized output is ignored. +=item C<get_ready> + + /path/to/script get_ready + =item C<preconnect> /path/to/script preconnect <readonly> <exportname> diff --git a/plugins/sh/methods.h b/plugins/sh/methods.h index 8a8f179c..f11e67e7 100644 --- a/plugins/sh/methods.h +++ b/plugins/sh/methods.h @@ -41,6 +41,7 @@ extern const char *get_script (const char *method); extern void sh_dump_plugin (void); extern int sh_thread_model (void); +extern int sh_get_ready (void); extern int sh_preconnect (int readonly); extern void *sh_open (int readonly); extern void sh_close (void *handle); diff --git a/plugins/eval/eval.c b/plugins/eval/eval.c index 9071b8c7..f09e49f3 100644 --- a/plugins/eval/eval.c +++ b/plugins/eval/eval.c @@ -70,6 +70,7 @@ static const char *known_methods[] = { "dump_plugin", "extents", "flush", + "get_ready", "get_size", "is_rotational", "missing", @@ -409,6 +410,7 @@ static struct nbdkit_plugin plugin = { .config_complete = eval_config_complete, .config_help = eval_config_help, .thread_model = sh_thread_model, + .get_ready = sh_get_ready, .preconnect = sh_preconnect, .open = sh_open, diff --git a/plugins/sh/methods.c b/plugins/sh/methods.c index e8011284..56e2d410 100644 --- a/plugins/sh/methods.c +++ b/plugins/sh/methods.c @@ -139,6 +139,31 @@ sh_thread_model (void) } } +int +sh_get_ready (void) +{ + const char *method = "get_ready"; + const char *script = get_script (method); + const char *args[] = { script, method, NULL }; + + switch (call (args)) { + case OK: + case MISSING: + return 0; + + case ERROR: + return -1; + + case RET_FALSE: + nbdkit_error ("%s: %s method returned unexpected code (3/false)", + script, method); + errno = EIO; + return -1; + + default: abort (); + } +} + int sh_preconnect (int readonly) { diff --git a/plugins/sh/sh.c b/plugins/sh/sh.c index 0d24edb4..736b8ef0 100644 --- a/plugins/sh/sh.c +++ b/plugins/sh/sh.c @@ -310,6 +310,7 @@ static struct nbdkit_plugin plugin = { .config_help = sh_config_help, .magic_config_key = "script", .thread_model = sh_thread_model, + .get_ready = sh_get_ready, .preconnect = sh_preconnect, .open = sh_open, diff --git a/tests/test-eval.sh b/tests/test-eval.sh index 868452f3..da7b8861 100755 --- a/tests/test-eval.sh +++ b/tests/test-eval.sh @@ -59,6 +59,7 @@ grep 'in missing: config_complete' eval.missing grep 'in missing: thread_model' eval.missing grep 'in missing: can_write' eval.missing grep 'in missing: is_rotational' eval.missing +grep 'in missing: get_ready' eval.missing grep 'in missing: preconnect' eval.missing grep 'in missing: open' eval.missing grep 'in missing: close' eval.missing -- 2.25.0
Eric Blake
2020-Feb-26 12:36 UTC
Re: [Libguestfs] [PATCH nbdkit 0/5] server: Add .get_ready callback.
On 2/25/20 4:30 AM, Richard W.M. Jones wrote:> I like this change. I think we were overloading the config_complete > method before to do two different things (complete configuration; do > any allocation/housekeeping necessary before we can start serving). > > The only questions in my mind are whether we want this before 1.18, > and whether the name ("get_ready") is a good one.I'm not coming up with any better name. Once we release, we're stuck with the name, but I don't see it as too much of a problem. My idea about enhancing filters to get access to next_ops for all filter connections to multiplex on a single plugin is still doable on top (since we don't promise filter API stability), so I don't see any problem with this going in 1.18, if you're ready to include it now. -- Eric Blake, Principal Software Engineer Red Hat, Inc. +1-919-301-3226 Virtualization: qemu.org | libvirt.org