Update TODO file.
[nbdkit/ericb.git] / src / plugins.c
blob5ca89aaf166ec653a69aa4ae479b301e33310799
1 /* nbdkit
2 * Copyright (C) 2013 Red Hat Inc.
3 * All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * * Neither the name of Red Hat nor the names of its contributors may be
17 * used to endorse or promote products derived from this software without
18 * specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
22 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
23 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR
24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
27 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
28 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
30 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
34 #include <config.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <stdint.h>
39 #include <string.h>
40 #include <inttypes.h>
41 #include <assert.h>
43 #include <dlfcn.h>
45 #include "nbdkit-plugin.h"
46 #include "internal.h"
48 static pthread_mutex_t connection_lock = PTHREAD_MUTEX_INITIALIZER;
49 static pthread_mutex_t all_requests_lock = PTHREAD_MUTEX_INITIALIZER;
51 /* Currently the server can only load one plugin (see TODO). Hence we
52 * can just use globals to store these.
54 static const char *filename;
55 static void *dl;
56 static struct nbdkit_plugin plugin;
58 void
59 plugin_register (const char *_filename,
60 void *_dl, struct nbdkit_plugin *(*plugin_init) (void))
62 const struct nbdkit_plugin *_plugin;
63 size_t i, len, size;
65 filename = _filename;
66 dl = _dl;
68 debug ("registering %s", filename);
70 /* Call the initialization function which returns the address of the
71 * plugin's own 'struct nbdkit_plugin'.
73 _plugin = plugin_init ();
74 if (!_plugin) {
75 fprintf (stderr, "%s: %s: plugin registration function failed\n",
76 program_name, filename);
77 exit (EXIT_FAILURE);
80 /* Check for incompatible future versions. */
81 if (_plugin->_api_version != 1) {
82 fprintf (stderr, "%s: %s: plugin is incompatible with this version of nbdkit (_api_version = %d)\n",
83 program_name, filename, _plugin->_api_version);
84 exit (EXIT_FAILURE);
87 /* Since the plugin might be much older than the current version of
88 * nbdkit, only copy up to the self-declared _struct_size of the
89 * plugin and zero out the rest. If the plugin is much newer then
90 * we'll only call the "old" fields.
92 size = sizeof plugin; /* our struct */
93 memset (&plugin, 0, size);
94 if (size > _plugin->_struct_size)
95 size = _plugin->_struct_size;
96 memcpy (&plugin, _plugin, size);
98 /* Check for the minimum fields which must exist in the
99 * plugin struct.
101 if (plugin.name == NULL) {
102 fprintf (stderr, "%s: %s: plugin must have a .name field\n",
103 program_name, filename);
104 exit (EXIT_FAILURE);
106 if (plugin.open == NULL) {
107 fprintf (stderr, "%s: %s: plugin must have a .open callback\n",
108 program_name, filename);
109 exit (EXIT_FAILURE);
111 if (plugin.get_size == NULL) {
112 fprintf (stderr, "%s: %s: plugin must have a .get_size callback\n",
113 program_name, filename);
114 exit (EXIT_FAILURE);
116 if (plugin.pread == NULL) {
117 fprintf (stderr, "%s: %s: plugin must have a .pread callback\n",
118 program_name, filename);
119 exit (EXIT_FAILURE);
122 len = strlen (plugin.name);
123 if (len == 0) {
124 fprintf (stderr, "%s: %s: plugin.name field must not be empty\n",
125 program_name, filename);
126 exit (EXIT_FAILURE);
128 for (i = 0; i < len; ++i) {
129 if (!((plugin.name[i] >= '0' && plugin.name[i] <= '9') ||
130 (plugin.name[i] >= 'a' && plugin.name[i] <= 'z') ||
131 (plugin.name[i] >= 'A' && plugin.name[i] <= 'Z'))) {
132 fprintf (stderr, "%s: %s: plugin.name ('%s') field must contain only ASCII alphanumeric characters\n",
133 program_name, filename, plugin.name);
134 exit (EXIT_FAILURE);
138 debug ("registered %s (name %s)", filename, plugin.name);
140 /* Call the on-load callback if it exists. */
141 debug ("%s: load", filename);
142 if (plugin.load)
143 plugin.load ();
146 void
147 plugin_cleanup (void)
149 if (dl) {
150 debug ("%s: unload", filename);
151 if (plugin.unload)
152 plugin.unload ();
154 dlclose (dl);
155 dl = NULL;
159 const char *
160 plugin_name (void)
162 assert (dl);
164 return plugin.name;
167 void
168 plugin_usage (void)
170 assert (dl);
172 printf ("%s", plugin.name);
173 if (plugin.longname)
174 printf (" (%s)", plugin.longname);
175 printf ("\n");
176 if (plugin.description) {
177 printf ("\n");
178 printf ("%s\n", plugin.description);
180 if (plugin.config_help) {
181 printf ("\n");
182 printf ("%s\n", plugin.config_help);
186 void
187 plugin_version (void)
189 assert (dl);
191 printf ("%s", plugin.name);
192 if (plugin.version)
193 printf (" %s", plugin.version);
194 printf ("\n");
197 void
198 plugin_config (const char *key, const char *value)
200 assert (dl);
202 debug ("%s: config key=%s, value=%s",
203 filename, key, value);
205 if (plugin.config == NULL) {
206 fprintf (stderr, "%s: %s: this plugin does not need command line configuration\n"
207 "Try using: %s --help %s\n",
208 program_name, filename,
209 program_name, filename);
210 exit (EXIT_FAILURE);
213 if (plugin.config (key, value) == -1)
214 exit (EXIT_FAILURE);
217 void
218 plugin_config_complete (void)
220 assert (dl);
222 debug ("%s: config_complete", filename);
224 if (!plugin.config_complete)
225 return;
227 if (plugin.config_complete () == -1)
228 exit (EXIT_FAILURE);
231 /* Handle the thread model. */
232 void
233 plugin_lock_connection (void)
235 assert (dl);
237 if (plugin._thread_model <= NBDKIT_THREAD_MODEL_SERIALIZE_CONNECTIONS) {
238 debug ("%s: acquire connection lock", filename);
239 pthread_mutex_lock (&connection_lock);
243 void
244 plugin_unlock_connection (void)
246 assert (dl);
248 if (plugin._thread_model <= NBDKIT_THREAD_MODEL_SERIALIZE_CONNECTIONS) {
249 debug ("%s: release connection lock", filename);
250 pthread_mutex_unlock (&connection_lock);
254 void
255 plugin_lock_request (struct connection *conn)
257 assert (dl);
259 if (plugin._thread_model <= NBDKIT_THREAD_MODEL_SERIALIZE_ALL_REQUESTS) {
260 debug ("acquire global request lock");
261 pthread_mutex_lock (&all_requests_lock);
264 if (plugin._thread_model <= NBDKIT_THREAD_MODEL_SERIALIZE_REQUESTS) {
265 debug ("acquire per-connection request lock");
266 pthread_mutex_lock (&conn->request_lock);
270 void
271 plugin_unlock_request (struct connection *conn)
273 assert (dl);
275 if (plugin._thread_model <= NBDKIT_THREAD_MODEL_SERIALIZE_REQUESTS) {
276 debug ("release per-connection request lock");
277 pthread_mutex_unlock (&conn->request_lock);
280 if (plugin._thread_model <= NBDKIT_THREAD_MODEL_SERIALIZE_ALL_REQUESTS) {
281 debug ("release global request lock");
282 pthread_mutex_unlock (&all_requests_lock);
287 plugin_open (struct connection *conn, int readonly)
289 void *handle;
291 assert (dl);
292 assert (conn->handle == NULL);
293 assert (plugin.open != NULL);
295 debug ("%s: open readonly=%d", filename, readonly);
297 handle = plugin.open (readonly);
298 if (!handle)
299 return -1;
301 conn->handle = handle;
302 return 0;
305 void
306 plugin_close (struct connection *conn)
308 assert (dl);
309 assert (conn->handle);
311 debug ("close");
313 if (plugin.close)
314 plugin.close (conn->handle);
316 conn->handle = NULL;
319 int64_t
320 plugin_get_size (struct connection *conn)
322 assert (dl);
323 assert (conn->handle);
324 assert (plugin.get_size != NULL);
326 debug ("get_size");
328 return plugin.get_size (conn->handle);
332 plugin_can_write (struct connection *conn)
334 assert (dl);
335 assert (conn->handle);
337 debug ("can_write");
339 if (plugin.can_write)
340 return plugin.can_write (conn->handle);
341 else
342 return plugin.pwrite != NULL;
346 plugin_can_flush (struct connection *conn)
348 assert (dl);
349 assert (conn->handle);
351 debug ("can_flush");
353 if (plugin.can_flush)
354 return plugin.can_flush (conn->handle);
355 else
356 return plugin.flush != NULL;
360 plugin_is_rotational (struct connection *conn)
362 assert (dl);
363 assert (conn->handle);
365 debug ("is_rotational");
367 if (plugin.is_rotational)
368 return plugin.is_rotational (conn->handle);
369 else
370 return 0; /* assume false */
374 plugin_can_trim (struct connection *conn)
376 assert (dl);
377 assert (conn->handle);
379 debug ("can_trim");
381 if (plugin.can_trim)
382 return plugin.can_trim (conn->handle);
383 else
384 return plugin.trim != NULL;
388 plugin_pread (struct connection *conn,
389 void *buf, uint32_t count, uint64_t offset)
391 assert (dl);
392 assert (conn->handle);
393 assert (plugin.pread != NULL);
395 debug ("pread count=%" PRIu32 " offset=%" PRIu64, count, offset);
397 return plugin.pread (conn->handle, buf, count, offset);
401 plugin_pwrite (struct connection *conn,
402 void *buf, uint32_t count, uint64_t offset)
404 assert (dl);
405 assert (conn->handle);
407 debug ("pwrite count=%" PRIu32 " offset=%" PRIu64, count, offset);
409 if (plugin.pwrite != NULL)
410 return plugin.pwrite (conn->handle, buf, count, offset);
411 else {
412 errno = EROFS;
413 return -1;
418 plugin_flush (struct connection *conn)
420 assert (dl);
421 assert (conn->handle);
423 debug ("flush");
425 if (plugin.flush != NULL)
426 return plugin.flush (conn->handle);
427 else {
428 errno = EINVAL;
429 return -1;
434 plugin_trim (struct connection *conn, uint32_t count, uint64_t offset)
436 assert (dl);
437 assert (conn->handle);
439 debug ("trim count=%" PRIu32 " offset=%" PRIu64, count, offset);
441 if (plugin.trim != NULL)
442 return plugin.trim (conn->handle, count, offset);
443 else {
444 errno = EINVAL;
445 return -1;