This extends the concept of the magic script parameter. In existing nbdkit plugins if the first parameter is "bare" (does not contain '='), then it is parsed magically to allow scripting languages to work: nbdkit perl foo.pl is parsed as: nbdkit perl script=foo.pl This generalises the concept, allowing a plugin.magic_config_key to be defined. If this is non-NULL then any bare parameters on the command line: nbdkit file foo where file_plugin.magic_config_key = "file" is parsed as: nbdkit file file=foo If magic_config_key == NULL then the previous behaviour is used. There is a small (but hopefully not noticable) change to the behaviour of ‘--dump-plugin’. It is now handled after all command line parameters have been parsed but before the .config_complete method is called (whereas previously it would be called after the first command line parameter was parsed). Note this change only applies to plugin parameters. --- docs/nbdkit-plugin.pod | 17 +++++++++++ docs/nbdkit.pod | 8 +++--- include/nbdkit-plugin.h | 2 ++ src/filters.c | 12 ++++++++ src/internal.h | 1 + src/main.c | 62 ++++++++++++++++++++++++----------------- src/plugins.c | 9 ++++++ 7 files changed, 81 insertions(+), 30 deletions(-) diff --git a/docs/nbdkit-plugin.pod b/docs/nbdkit-plugin.pod index 515c032..570a142 100644 --- a/docs/nbdkit-plugin.pod +++ b/docs/nbdkit-plugin.pod @@ -396,6 +396,23 @@ with an error. If there is an error, C<.config> should call C<nbdkit_error> with an error message and return C<-1>. +=head2 C<.magic_config_key> + + const char *magic_config_key; + +This optional string can be used to set a "magic" key used when +parsing plugin parameters. It affects how "bare parameters" (those +which do not contain an C<=> character) are parsed on the command +line. + +If C<magic_config_key != NULL> then any bare parameters are passed to +the C<.config> method as: S<C<config (magic_config_key, argv[i]);>>. + +If C<magic_config_key> is not set then we behave as in nbdkit E<lt> +1.7: If the first parameter on the command line is bare then it is +passed to the C<.config> method as: S<C<config ("script", value);>>. +Any other bare parameters give errors. + =head2 C<.config_complete> int config_complete (void); diff --git a/docs/nbdkit.pod b/docs/nbdkit.pod index 981e571..cf1c5ca 100644 --- a/docs/nbdkit.pod +++ b/docs/nbdkit.pod @@ -404,11 +404,11 @@ To dump information about a plugin, do: =head2 Magic script parameter -As a special case, if the first plugin argument does not contain an -C<'='> character then it is assumed to be C<script=value>. +For some plugins, especially those supporting scripting languages like +Perl, if the first plugin argument does not contain an C<'='> +character then it is assumed to be C<script=value>. -That allows scripting language plugins like L<nbdkit-perl-plugin(1)> -to do: +That allows scripting language plugins to do: nbdkit perl foo.pl [args...] diff --git a/include/nbdkit-plugin.h b/include/nbdkit-plugin.h index c95e26c..31aa6c7 100644 --- a/include/nbdkit-plugin.h +++ b/include/nbdkit-plugin.h @@ -121,6 +121,8 @@ struct nbdkit_plugin { int (*trim) (void *handle, uint32_t count, uint64_t offset, uint32_t flags); int (*zero) (void *handle, uint32_t count, uint64_t offset, uint32_t flags); #endif + + const char *magic_config_key; }; extern void nbdkit_set_error (int err); diff --git a/src/filters.c b/src/filters.c index e6826f2..3626742 100644 --- a/src/filters.c +++ b/src/filters.c @@ -205,6 +205,17 @@ filter_config_complete (struct backend *b) f->backend.next->config_complete (f->backend.next); } +/* magic_config_key only applies to plugins, so this passes the + * request through to the plugin (hence the name). + */ +static const char * +plugin_magic_config_key (struct backend *b) +{ + struct backend_filter *f = container_of (b, struct backend_filter, backend); + + return f->backend.next->magic_config_key (f->backend.next); +} + static int next_open (void *nxdata, int readonly) { @@ -619,6 +630,7 @@ static struct backend filter_functions = { .dump_fields = filter_dump_fields, .config = filter_config, .config_complete = filter_config_complete, + .magic_config_key = plugin_magic_config_key, .open = filter_open, .prepare = filter_prepare, .finalize = filter_finalize, diff --git a/src/internal.h b/src/internal.h index 1c5aa72..d4fc534 100644 --- a/src/internal.h +++ b/src/internal.h @@ -171,6 +171,7 @@ struct backend { void (*dump_fields) (struct backend *); void (*config) (struct backend *, const char *key, const char *value); void (*config_complete) (struct backend *); + const char *(*magic_config_key) (struct backend *); int (*open) (struct backend *, struct connection *conn, int readonly); int (*prepare) (struct backend *, struct connection *conn); int (*finalize) (struct backend *, struct connection *conn); diff --git a/src/main.c b/src/main.c index 8a83a32..9c18d6f 100644 --- a/src/main.c +++ b/src/main.c @@ -231,6 +231,7 @@ main (int argc, char *argv[]) const char *filename; } *filter_filenames = NULL; size_t i; + const char *magic_config_key; threadlocal_init (); @@ -680,37 +681,46 @@ main (int argc, char *argv[]) exit (EXIT_SUCCESS); } - /* Find key=value configuration parameters for this plugin. - * The first one is magical in that if it doesn't contain '=' then - * we assume it is 'script=...'. + /* Call config and config_complete to parse the parameters. + * + * If the plugin provides magic_config_key then any "bare" values + * (ones not containing "=") are prefixed with this key. + * + * For backwards compatibility with old plugins, and to support + * scripting languages, if magic_config_key == NULL then if the + * first parameter is bare it is prefixed with the key "script", and + * any other bare parameters are errors. */ - if (optind < argc && (p = strchr (argv[optind], '=')) == NULL) { - backend->config (backend, "script", argv[optind]); - ++optind; - } - - /* This must run after parsing the possible script parameter so that - * the script can be loaded for scripting languages. Note that all - * scripting languages load the script as soon as they see the - * script=... parameter (and do not wait for config_complete). - */ - if (dump_plugin) { - backend->dump_fields (backend); - exit (EXIT_SUCCESS); - } - - while (optind < argc) { - if ((p = strchr (argv[optind], '=')) != NULL) { + magic_config_key = backend->magic_config_key (backend); + for (i = 0; optind < argc; ++i, ++optind) { + p = strchr (argv[optind], '='); + if (p) { /* key=value */ *p = '\0'; backend->config (backend, argv[optind], p+1); - ++optind; } - else { - fprintf (stderr, - "%s: expecting key=value on the command line but got: %s\n", - program_name, argv[optind]); - exit (EXIT_FAILURE); + else if (magic_config_key == NULL) { + if (i == 0) /* magic script parameter */ + backend->config (backend, "script", argv[optind]); + else { + fprintf (stderr, + "%s: expecting key=value on the command line but got: %s\n", + program_name, argv[optind]); + exit (EXIT_FAILURE); + } } + else { /* magic config key */ + backend->config (backend, magic_config_key, argv[optind]); + } + } + + /* This must run after parsing the parameters so that the script can + * be loaded for scripting languages. But it must be called before + * config_complete so that the plugin doesn't check for missing + * parameters. + */ + if (dump_plugin) { + backend->dump_fields (backend); + exit (EXIT_SUCCESS); } backend->config_complete (backend); diff --git a/src/plugins.c b/src/plugins.c index 780bbd9..2bea6ac 100644 --- a/src/plugins.c +++ b/src/plugins.c @@ -232,6 +232,14 @@ plugin_config_complete (struct backend *b) exit (EXIT_FAILURE); } +static const char * +plugin_magic_config_key (struct backend *b) +{ + struct backend_plugin *p = container_of (b, struct backend_plugin, backend); + + return p->plugin.magic_config_key; +} + static int plugin_open (struct backend *b, struct connection *conn, int readonly) { @@ -630,6 +638,7 @@ static struct backend plugin_functions = { .dump_fields = plugin_dump_fields, .config = plugin_config, .config_complete = plugin_config_complete, + .magic_config_key = plugin_magic_config_key, .open = plugin_open, .prepare = plugin_prepare, .finalize = plugin_finalize, -- 2.18.0