[Libguestfs] [PATCH nbdkit 2/6] plugins: Implement magic config key.

Richard W.M. Jones rjones at redhat.com
Fri Sep 7 11:51:05 UTC 2018


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 64a10dc..4137846 100644
--- a/docs/nbdkit-plugin.pod
+++ b/docs/nbdkit-plugin.pod
@@ -395,6 +395,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 L<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 ec23f26..4f72502 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




More information about the Libguestfs mailing list