=head1 NAME

nbdkit-ocaml-plugin - writing nbdkit plugins in OCaml

=head1 SYNOPSIS

 nbdkit /path/to/plugin.so [arguments...]

=for paragraph

 nbdkit plugin [arguments...]

=head1 DESCRIPTION

This manual page describes how to write nbdkit plugins in natively
compiled OCaml code.  This requires OCaml E<ge> 4.03.

=head1 WRITING AN OCAML NBDKIT PLUGIN

For an example plugin written in OCaml, see:
L<https://gitlab.com/nbdkit/nbdkit/blob/master/plugins/ocaml/example.ml>

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

You should also look at L<NBDKit(3)> which describes the plugin
interface for OCaml plugins.

Your OCaml code should call C<NBDKit.register_plugin> like this when
the plugin is loaded:

 NBDKit.register_plugin
     ~name:            "myplugin"
     ~version:         "1.0"
     ~open_connection: myplugin_open
     ~get_size:        myplugin_get_size
     ~pread:           myplugin_pread
     ~thread_model:
         (fun () -> NBDKit.THREAD_MODEL_SERIALIZE_ALL_REQUESTS)
     (* etc *)
     ()

C<~name>, C<~open_connection>, C<~get_size> and C<~pread> are
required.  All other parameters are optional.

=head2 Compiling an OCaml nbdkit plugin

OCaml nbdkit plugins are natively compiled into shared object
(C<*.so>) files which nbdkit loads like regular C plugins.

After writing your OCaml plugin (C<myplugin.ml>), compile and link it
using this command:

 ocamlopt.opt -output-obj -runtime-variant _pic \
              -o nbdkit-myplugin-plugin.so \
              __OCAML_PLUGIN_LIBRARIES__ \
              NBDKit.cmx myplugin.ml \
              -cclib -lnbdkitocaml

You can then use C<nbdkit-myplugin-plugin.so> as an nbdkit plugin (see
L<nbdkit(1)>, L<nbdkit-plugin(3)>):

 nbdkit ./nbdkit-myplugin-plugin.so [args ...]

or if the C<.so> file is installed in the C<$plugindir> directory:

 nbdkit myplugin [args ...]

=head2 Handle

Your C<open_connection> callback can return an OCaml value of any
type.  The same value is passed back to the per-connection callbacks
like C<get_size> and C<pread>.

Typically (although this is not a requirement) you define your own
handle struct in your plugin:

 type handle = {
   (* any per-connection data you want to store goes here *)
   h_id : int; (* this is just an example field *)
   h_readonly : bool;
 }

 let id = ref 0
 let myplugin_open readonly =
   (* return a newly allocated handle *)
   incr id;
   { h_id = !id; h_readonly = readonly }

 let myplugin_get_size handle =
   printf "handle ID = %d\n" handle.h_id;
   (* ... *)

If you don't need to store per-connection data, C<open_connection> can
return C<()>.

=head2 Reading and writing

The C<pread> and C<pwrite> callbacks are declared:

 val pread: 'a -> buf -> int64 -> flags -> unit
 val pwrite: 'a -> buf -> int64 -> flags -> unit

C<NBDKit.buf> is an OCaml Bigarray wrapping the C buffer:

 type buf =
 (char, Bigarray.int8_unsigned_elt, Bigarray.c_layout) Bigarray.Array1.t

This allows direct, zero-copy access to the underlying buffer using
the functions in the OCaml stdlib L<Bigarray.Array1(3)> module
(L<https://v2.ocaml.org/api/Bigarray.Array1.html>).

There are also helper functions in the L<NBDKit(3)> module for fast
blitting between C<buf> and OCaml C<string> and C<bytes>.

This type is compatible with the
L<bigstring|https://v3.ocaml.org/p/bigstring/latest> and
L<bigstringaf|https://v3.ocaml.org/p/bigstringaf/latest> libraries.

Note that the length of the data to read or write is implicit in
C<buf>.  Use C<NBDKit.buf_len buf> to get this.

=head2 Errors

Plugins can return errors from methods by raising an exception.

If you need to control which errno is sent back to the client you have
to call C<NBDKit.set_error> before raising the exception.

Note if you call some function in the OCaml C<Unix> module or another
library which fails, then the errno of the failing system call will
not be returned to the client.  You have to catch the exception and
call C<NBDKit.set_error> before re-raising the exception if you need
to control this.

=head2 Threads

One of the optional parameters of C<NBDKit.register_plugin> is
C<~thread_model>, which must return one of the values in the table
below:

=over 4

=item C<NBDKit.THREAD_MODEL_SERIALIZE_CONNECTIONS>

=item C<NBDKit.THREAD_MODEL_SERIALIZE_ALL_REQUESTS>

=item C<NBDKit.THREAD_MODEL_SERIALIZE_REQUESTS>

=item C<NBDKit.THREAD_MODEL_PARALLEL>

=back

If this optional parameter is not provided, the thread model defaults
to C<NBDKit.THREAD_MODEL_PARALLEL>.  The garbage collector lock is
acquired when calling into OCaml code and because of this callbacks
are never truly concurrent.

For more information on thread models, see L<nbdkit-plugin(3)/THREADS>.

=head2 Debugging

You can add debugging messages which are printed only when nbdkit is
in verbose mode by calling:

 NBDKit.debug fs [...]

This function works like C<Printf.printf>.

=head2 Debug flags

You can use debug flags in OCaml plugins.  See
F<plugins/ocaml/example-debug-flag.c> in the nbdkit sources, and
L<nbdkit-plugin(3)/Debug Flags>.

=head2 --dump-plugin output

As with all nbdkit plugins, you can add a C<dump_plugin> callback in
order to print additional facts about your plugin.  The OCaml wrapper
also adds some additional fields to help identify which version of
OCaml was used:

 $ nbdkit your-ocaml-plugin.so --dump-plugin
 ...
 ocaml_version_major=4
 ocaml_version_minor=5
 ocaml_version=4.05.0

=head2 OCaml scripts

Using L<nbdkit-cc-plugin(1)> it is possible to write OCaml plugins
which are compiled just before use, and so appear to work more like
scripts.

=head1 VERSION

OCaml plugins first appeared in nbdkit 1.2.

=head1 SEE ALSO

L<NBDKit(3)>,
L<nbdkit(1)>,
L<nbdkit-plugin(3)>,
L<Bigarray.Array1(3)>,
L<ocamlopt(1)>.

=head1 AUTHORS

Richard W.M. Jones

=head1 COPYRIGHT

Copyright Red Hat
