File: nbdkit-ocaml-plugin.pod

package info (click to toggle)
nbdkit 1.46.2-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 15,504 kB
  • sloc: ansic: 63,658; sh: 18,717; makefile: 6,814; python: 1,848; cpp: 1,143; perl: 504; ml: 504; tcl: 62
file content (213 lines) | stat: -rw-r--r-- 5,885 bytes parent folder | download | duplicates (2)
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