1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
|
=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_STD_INCLUDES__ __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
|