Skip site navigation (1)Skip section navigation (2)

  
 
  

home | help
nbdkit-sh-plugin(3)		     NBDKIT		     nbdkit-sh-plugin(3)

NAME
     nbdkit-sh-plugin - nbdkit shell, script or executable plugin

SYNOPSIS
      nbdkit sh /path/to/script [arguments...]

      nbdkit sh - <<'EOF'
      ... shell script ...
      EOF

DESCRIPTION
     "nbdkit-sh-plugin" allows you to write plugins for nbdkit(1) using arbi-
     trary scripting languages, including shells like bash(1), dash(1), csh(1),
     zsh(1) etc., other scripting environments, or any executable.

     Note if you want to use an established scripting language like Perl or
     Python, then nbdkit has specific plugins to handle those languages and
     those will be more efficient (see nbdkit(1) for a complete list).

     To use shell script fragments from the nbdkit command line (rather than a
     separate script) see nbdkit-eval-plugin(1).

   If you have been given an nbdkit sh plugin
     Assuming you have a shell script which is an nbdkit plugin, you run it like
     this:

      nbdkit sh /path/to/script

     You may have to add further "key=value" arguments to the command line.  The
     script must be executable ("chmod +x").

   Inline shell scripts
     It is also possible to write a shell script plugin "inline" using "-" as
     the name of the script, like this:

      nbdkit sh - <<'EOF'
	case "$1" in
	  get_size) echo 1M ;;
	  pread) dd if=/dev/zero count=$3 iflag=count_bytes ;;
	  *) exit 2 ;;
	esac
      EOF

     By default the inline script runs under /bin/sh.  You can add a shebang
     ("#!") to use other scripting languages.  Of course, reading an inline
     script from stdin is incompatible with the -s (--single) mode of nbdkit
     that connects a client on stdin.

WRITING AN NBDKIT SH PLUGIN
     For example plugins written in Bash, see: https://gitlab.com/nbdkit/nbd-
     kit/blob/master/plugins/sh/examples/

     Broadly speaking, nbdkit shell plugins work like C ones, so you should read
     nbdkit-plugin(3) first.

   Programming model
     This plugin has a simple programming model: For every plugin method that
     needs to be called, the external script is invoked with parameters describ-
     ing the method and its arguments.	The first parameter is always the method
     name.  For example:

      /path/to/script config file disk.img
			a      a   a
			a      a   aa value ($3)
			a      aaa key ($2)
		    method ($1)

      /path/to/script pread <handle> <count> <offset>
			a	a	a	a
			a	a	a	aa offset in bytes ($4)
			a	a	aaa request size in bytes ($3)
		    method ($1) aaa handle ($2) a see "Handles" below

     Scripts should ignore extra parameters that they don't understand since we
     may add new parameters in future.

   Exit codes
     The script should exit with specific exit codes:

     0	 The method was executed successfully.

     1 and 16-255
	 There was an error.  The script may print on stderr an errno name, op-
	 tionally followed by whitespace and a message, for example:

	  echo 'ENOSPC Out of space' >&2
	  exit 1

	 or if you don't need the log message:

	  echo ENOSPC >&2
	  exit 1

	 If the script doesn't print anything or the output cannot be parsed
	 then nbdkit assumes error "EIO".  Note that output to stderr is ignored
	 if the command succeeds, so it is acceptable to output a potential er-
	 ror message prefix prior to attempting a command which will add further
	 details if a failure occurs.

     2	 The requested method is not supported by the script.

     3	 For methods which return booleans, this code indicates false.

     4 and 5
	 Triggers a call to the C function nbdkit_shutdown(3), which requests an
	 asynchronous exit of the nbdkit server (disconnecting all clients).
	 The client will usually get a response before shutdown is complete (al-
	 though this is racy); so once the shutdown is requested, code 4 then
	 behaves like code 0 (stderr is ignored, and the server tries to return
	 success), and code 5 behaves like code 1 (the server tries to return an
	 error to the client parsed from stderr, although a missing error de-
	 faults to "ESHUTDOWN" instead of "EIO").

     6	 Triggers a call to the C function nbdkit_disconnect(3) with "force" set
	 to true, which requests an abrupt disconnect of the current client.
	 The contents of stderr are irrelevant with this status, since the
	 client will not get a response.

     7 and 8
	 Triggers a call to the C function nbdkit_disconnect(3) with "force" set
	 to false, which requests a soft disconnect of the current client (fu-
	 ture client requests are rejected with "ESHUTDOWN" without calling into
	 the plugin, but current requests may complete).  Since the client will
	 likely get the response to this command, code 7 then behaves like code
	 0 (stderr is ignored, and the server tries to return success), and code
	 8 behaves like code 1 (the server tries to return an error to the
	 client parsed from stderr, although a missing error defaults to "ESHUT-
	 DOWN" instead of "EIO").

     9-15These exit codes are reserved for future use.	Note that versions of
	 nbdkit < 1.34 documented that codes 8 through 15 behaved like code 1;
	 although it is unlikely that many scripts relied on this similarity in
	 practice.

     In nbdkit > 1.34, it is possible to probe whether additional exit codes
     have been assigned meaning, by looking for the line max_known_status= in
     the output of nbdkit --dump-plugin sh.  If this line is not present, exit
     codes 4 and above behave like status 1.

   Temporary directory
     A fresh script is invoked for each method call (ie. scripts are stateless),
     so if the script needs to store state it has to store it somewhere in the
     filesystem in a format and location which is left up to the author of the
     script.

     However nbdkit helps by creating a randomly named, empty directory for the
     script.  This directory persists for the lifetime of nbdkit and is deleted
     when nbdkit exits.  The name of the directory is passed to each script in-
     vocation in the $tmpdir environment variable.

   Handles
     Handles are arbitrary strings, but it is best to limit them to short al-
     phanumeric strings.

     Per-connection state

     The temporary directory described above can be used for state for the life-
     time of the nbdkit instance (across multiple connections).  If you want to
     store state per connection then one way to do it is to create a randomly
     named subdirectory under the temporary directory:

      case "$1" in
	...
	open)
	  mktemp -d $tmpdir/handle-XXXXXX ;;

     The handle will be the subdirectory name, returned to the script as $2 in
     all connected calls (eg. "pread", "get_size").  You can delete the subdi-
     rectory explicitly in "close":

      case "$1" in
	...
	close)
	  rm -rf "$2" ;;

     or rely on nbdkit deleting the whole temporary directory including all
     per-handle subdirectories when it exits.

   Performance
     This plugin has to fork on every request, so performance will never be
     great.  For best performance, consider using the nbdkit-plugin(3) API di-
     rectly.  Having said that, if you have a sh plugin and want to improve per-
     formance then the following tips may help:

     Relax the thread model.
	 The default "thread_model" is "serialize_all_requests" meaning that two
	 instances of the script can never be running at the same time.  This is
	 safe but slow.  If your script is safe to be called in parallel, set
	 this to "parallel".

     Implement the "zero" method.
	 If the "zero" method is not implemented then nbdkit will fall back to
	 using "pwrite" which is considerably slower because nbdkit has to send
	 blocks of zeroes to the script.

     You don't have to write shell scripts.
	 This plugin can run any external binary, not only shell scripts.  You
	 should get more performance by rewriting the shell script as a program
	 in a compiled language.

   Methods
     This just documents the arguments to the script corresponding to each plu-
     gin method, and any way that they differ from the C callbacks.  In all
     other respects they work the same way as the C callbacks, so you should go
     and read nbdkit-plugin(3).

     "load"
	  /path/to/script load

     "unload"
	  /path/to/script unload

	 This is called just before nbdkit exits.  Errors from this method are
	 ignored.

     "dump_plugin"
	  /path/to/script dump_plugin

     "config"
	  /path/to/script config <key> <value>

     "config_complete"
	  /path/to/script config_complete

     "magic_config_key"
	  /path/to/script magic_config_key

	 If a magic config key is needed, this should echo it to stdout.  See
	 "Magic parameters" in nbdkit(1).

     "thread_model"
	  /path/to/script thread_model

	 On success this should print the desired thread model of the script,
	 one of "serialize_connections", "serialize_all_requests", "serial-
	 ize_requests", or "parallel".

	 This method is not required; if omitted, then the plugin will be exe-
	 cuted under the safe "serialize_all_requests" model.  However, this
	 means that this method must be provided if you want to use the "paral-
	 lel" or "serialize_requests" model.  Even then your request may be re-
	 stricted for other reasons; look for "thread_model" in the output of
	 "nbdkit --dump-plugin sh script" to see what actually gets selected.

	 If an error occurs, the script should output an error message and exit
	 with status 1; unrecognized output is ignored.

     "get_ready"
	  /path/to/script get_ready

     "after_fork"
	  /path/to/script after_fork

     "preconnect"
	  /path/to/script preconnect <readonly>

     "list_exports"
	  /path/to/script list_exports <readonly> <tls>

	 The "readonly" and "tls" parameters will be "true" or "false".

	 The first line of output informs nbdkit how to parse the rest of the
	 output, the remaining lines then supply the inputs of the C "nbd-
	 kit_add_export" function (see nbdkit-plugin(3)), as follows:

	 NAMES
	     The remaining output provides one export name per line, and no ex-
	     port will be given a description.	For convenience, this form is
	     also assumed if the first output line does not match one of the
	     recognized parse modes.

	 INTERLEAVED
	     The remaining output provides pairs of lines, the first line being
	     an export name, and the second the corresponding description.

	 NAMES+DESCRIPTIONS
	     The number of remaining lines is counted, with the first half being
	     used as export names, and the second half providing descriptions to
	     pair with names from the first half.

	     An example of using this form to list files in the current direc-
	     tory, followed by their ls(1) long description, would be:

	      echo NAMES+DESCRIPTIONS
	      ls
	      ls -l

	 Note that other output modes might be introduced in the future; in par-
	 ticular, none of the existing modes allow a literal newline in an ex-
	 port name or description, although this could be possible under a new
	 mode supporting escape sequences.

	 This method is not required; if it is absent, the list of exports ad-
	 vertised by nbdkit will be the single name result of "default_export"
	 and no description.

     "default_export"
	  /path/to/script default_export <readonly> <tls>

	 The "readonly" and "tls" parameters will be "true" or "false".

	 On success this should print a name on stdout to use in place of the
	 default export "", then exit with code 0.  For convenience, the output
	 can be any of the list forms recognized by "list_exports", in which
	 case the first listed export name is used, and where an empty list uses
	 "".  Given the current set of recognized export lists, it is not possi-
	 ble for the resulting name to include a newline.

	 This method is not required; if it is absent, the default export name
	 will be the empty string, "".

     "open"
	  /path/to/script open <readonly> <exportname> <tls>

	 The "readonly" parameter will be "true" or "false".  The "exportname"
	 parameter, if present, is the export name passed to the server from the
	 client.  The "tls" parameter, if present, will be "true" or "false" de-
	 pending on whether the client is using TLS.

	 On success this should print the handle (any string) on stdout and exit
	 with code 0.  If the handle ends with a newline character then the new-
	 line is removed.

	 Unlike C plugins, this method is not required.  If omitted then the
	 handle will be "" (empty string).

     "close"
	  /path/to/script close <handle>

     "export_description"
	  /path/to/script export_description <handle>

	 The script should print a human-readable description of the disk image
	 on stdout.  If the description ends with a newline character then the
	 newline is removed.

	 This method is not required; if it is absent, no export description
	 will be provided to the client.

     "get_size"
	  /path/to/script get_size <handle>

	 The script should print the size of the disk image on stdout.	You can
	 print the size in bytes, or use any format understood by nbd-
	 kit_parse_size(3) such as "1M".

	 This method is required.

     "block_size"
	  /path/to/script block_size <handle>

	 This script should print three numbers on stdout, separated by white-
	 space.  These are (in order) the minimum block size, the preferred
	 block size, and the maximum block size.  You can print the sizes in
	 bytes or use any format understood by nbdkit_parse_size(3) such as
	 "1M".

     "can_write"
     "can_flush"
     "can_trim"
     "can_zero"
     "can_extents"
	 Unlike in other languages, you must provide the "can_*" methods other-
	 wise they are assumed to all return false and your "pwrite", "flush",
	 "trim", "zero" and "extents" methods will never be called.  The reason
	 for this is obscure: In other languages we can detect if (eg) a
	 "pwrite" method is defined and synthesize an appropriate response if no
	 actual "can_write" method is defined.	However detecting if methods are
	 present without running them is not possible with this plugin.

	  /path/to/script can_write <handle>
	  /path/to/script can_flush <handle>
	  /path/to/script can_trim <handle>
	  /path/to/script can_zero <handle>
	  /path/to/script can_extents <handle>

	 The script should exit with code 0 for true or code 3 for false.

     "is_rotational"
     "can_fast_zero"
	  /path/to/script is_rotational <handle>
	  /path/to/script can_fast_zero <handle>

	 The script should exit with code 0 for true or code 3 for false.

     "can_fua"
     "can_cache"
	  /path/to/script can_fua <handle>
	  /path/to/script can_cache <handle>

	 These control Forced Unit Access (FUA) and caching behaviour of the
	 core server.

	 Unlike the other "can_*" callbacks, these two are not a boolean.  They
	 must print either "none", "emulate" or "native" to stdout.  The meaning
	 of these is described in nbdkit-plugin(3).  Furthermore, you must pro-
	 vide a "can_cache" method if you desire the "cache" callback to be uti-
	 lized, similar to the reasoning behind requiring "can_write" to utilize
	 "pwrite".

     "can_multi_conn"
	  /path/to/script can_multi_conn <handle>

	 The script should exit with code 0 for true or code 3 for false.

     "pread"
	  /path/to/script pread <handle> <count> <offset>

	 The script should print the requested binary data on stdout.  Exactly
	 "count" bytes must be printed.

	 This method is required.

     "pwrite"
	  /path/to/script pwrite <handle> <count> <offset> <flags>

	 The script should read the binary data to be written from stdin.

	 The "flags" parameter can be an empty string or "fua".  In the future,
	 a comma-separated list of flags may be present.

	 Unlike in other languages, if you provide a "pwrite" method you must
	 also provide a "can_write" method which exits with code 0 (true).

     "flush"
	  /path/to/script flush <handle>

	 Unlike in other languages, if you provide a "flush" method you must
	 also provide a "can_flush" method which exits with code 0 (true).

     "trim"
	  /path/to/script trim <handle> <count> <offset> <flags>

	 The "flags" parameter can be an empty string or "fua".  In the future,
	 a comma-separated list of flags may be present.

	 Unlike in other languages, if you provide a "trim" method you must also
	 provide a "can_trim" method which exits with code 0 (true).

     "zero"
	  /path/to/script zero <handle> <count> <offset> <flags>

	 The "flags" parameter can be an empty string or a comma-separated list
	 of the flags: "fua", "may_trim", and "fast" (eg. "", "fua",
	 "fua,may_trim,fast" are some of the 8 possible values).

	 Unlike in other languages, if you provide a "zero" method you must also
	 provide a "can_zero" method which exits with code 0 (true).

	 To trigger a fallback to "pwrite" on a normal zero request, or to re-
	 spond quickly to the "fast" flag that a specific zero request is no
	 faster than a corresponding write, the script must output "ENOTSUP" or
	 "EOPNOTSUPP" to stderr (possibly followed by a description of the prob-
	 lem) before exiting with code 1 (failure).

     "extents"
	  /path/to/script extents <handle> <count> <offset> <flags>

	 The "flags" parameter can be an empty string or "req_one".

	 This must print, one per line on stdout, a list of one or more extents
	 in the format:

	  offset length type

	 which correspond to the inputs of the C "nbdkit_add_extent" function
	 (see nbdkit-plugin(3)).  The "offset" and "length" fields may use any
	 format understood by nbdkit_parse_size(3).  The optional "type" field
	 may be an integer, missing (same as 0), or a comma-separated list of
	 the words "hole" and "zero".  Blank lines and lines beginning with "#"
	 are comments.	An example of a valid set of extents covering a "10M"
	 disk where the first megabyte only is allocated data:

	  0  1M
	  1M 9M  hole,zero

	 Unlike in other languages, if you provide an "extents" method you must
	 also provide a "can_extents" method which exits with code 0 (true).

     "cache"
	  /path/to/script cache <handle> <count> <offset>

	 Unlike in other languages, if you provide a "cache" method you must
	 also provide a "can_cache" method which prints "native" and exits with
	 code 0 (true).

   Missing callbacks
     Missing: "name", "version", "longname", "description", "config_help"
	 These are not yet supported.

FILES
     $plugindir/nbdkit-sh-plugin.so
	 The plugin.

	 Use "nbdkit --dump-config" to find the location of $plugindir.

VERSION
     "nbdkit-sh-plugin" first appeared in nbdkit 1.8.

SEE ALSO
     nbdkit(1), nbdkit-plugin(3), nbdkit-eval-plugin(1), nbdkit-cc-plugin(1).

AUTHORS
     Richard W.M. Jones

COPYRIGHT
     Copyright Red Hat

LICENSE
     Redistribution and use in source and binary forms, with or without modifi-
     cation, are permitted provided that the following conditions are met:

     *	 Redistributions of source code must retain the above copyright notice,
	 this list of conditions and the following disclaimer.

     *	 Redistributions in binary form must reproduce the above copyright no-
	 tice, this list of conditions and the following disclaimer in the docu-
	 mentation and/or other materials provided with the distribution.

     *	 Neither the name of Red Hat nor the names of its contributors may be
	 used to endorse or promote products derived from this software without
	 specific prior written permission.

     THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND ANY EX-
     PRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DIS-
     CLAIMED. IN NO EVENT SHALL RED HAT OR CONTRIBUTORS BE LIABLE FOR ANY DI-
     RECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SER-
     VICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
     CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABIL-
     ITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
     THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAM-
     AGE.

nbdkit-1.46.2			   2026-08-30		     nbdkit-sh-plugin(3)

Want to link to this manual page? Use this URL:
<https://man.freebsd.org/cgi/man.cgi?query=nbdkit-sh-plugin&sektion=3&manpath=FreeBSD+Ports+15.1.quarterly>

home | help