=head1 NAME

nbdkit-ocaml-plugin - writing nbdkit plugins in OCaml

=head1 SYNOPSIS

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

 nbdkit plugin [arguments...]

=head1 DESCRIPTION

This manual page describes how to write nbdkit plugins in natively
compiled OCaml code.

Note this requires OCaml E<ge> 4.02.2, which has support for shared
libraries.  See L<http://caml.inria.fr/mantis/view.php?id=6693>

=head1 WRITING AN OCAML NBDKIT PLUGIN

For an example plugin written in OCaml, see:
L<https://github.com/libguestfs/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 C<NBDKit.mli> which describes the plugin
interface for OCaml plugins.

Your OCaml code should call C<NBDKit.register_plugin> like this:

 let plugin = {
   NBDKit.default_callbacks with
     NBDKit.name     = "myplugin";
     version         = "1.0";
     open_connection = Some myplugin_open;
     get_size        = Some myplugin_get_size;
     pread           = Some myplugin_pread;
     thread_model    = Some (fun () -> NBDKit.THREAD_MODEL_SERIALIZE_CONNECTIONS);
     (* etc *)
 }
 
 let () = NBDKit.register_plugin plugin

Your plugin must call C<register_plugin> exactly once when the plugin
is loaded.

=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 \
              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;
   (* ... *)

=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 members in the plugin record passed to
C<NBDKit.register_plugin> is C<thread model>, which must return one of
the values in the table below.  For more information on thread models,
see L<nbdkit-plugin(3)/THREADS>.  If this optional function is not
provided, the thread model defaults to THREAD_MODEL_PARALLEL.  Note
that because of the garbage collector lock in OCaml, callbacks are
never truly concurrent.

=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

=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>.

=head1 VERSION

OCaml plugins first appeared in nbdkit 1.2.

=head1 SEE ALSO

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

=head1 AUTHORS

Richard W.M. Jones

=head1 COPYRIGHT

Copyright (C) 2014 Red Hat Inc.
