python: New plugin that lets you write plugins as Python scripts.
[nbdkit/ericb.git] / src / plugins.c
blob083487410757a36deb7089e3f936468ba06526db
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 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 = strdup (_filename);
66 if (filename == NULL) {
67 perror ("strdup");
68 exit (EXIT_FAILURE);
70 dl = _dl;
72 debug ("registering %s", filename);
74 /* Call the initialization function which returns the address of the
75 * plugin's own 'struct nbdkit_plugin'.
77 _plugin = plugin_init ();
78 if (!_plugin) {
79 fprintf (stderr, "%s: %s: plugin registration function failed\n",
80 program_name, filename);
81 exit (EXIT_FAILURE);
84 /* Check for incompatible future versions. */
85 if (_plugin->_api_version != 1) {
86 fprintf (stderr, "%s: %s: plugin is incompatible with this version of nbdkit (_api_version = %d)\n",
87 program_name, filename, _plugin->_api_version);
88 exit (EXIT_FAILURE);
91 /* Since the plugin might be much older than the current version of
92 * nbdkit, only copy up to the self-declared _struct_size of the
93 * plugin and zero out the rest. If the plugin is much newer then
94 * we'll only call the "old" fields.
96 size = sizeof plugin; /* our struct */
97 memset (&plugin, 0, size);
98 if (size > _plugin->_struct_size)
99 size = _plugin->_struct_size;
100 memcpy (&plugin, _plugin, size);
102 /* Check for the minimum fields which must exist in the
103 * plugin struct.
105 if (plugin.name == NULL) {
106 fprintf (stderr, "%s: %s: plugin must have a .name field\n",
107 program_name, filename);
108 exit (EXIT_FAILURE);
110 if (plugin.open == NULL) {
111 fprintf (stderr, "%s: %s: plugin must have a .open callback\n",
112 program_name, filename);
113 exit (EXIT_FAILURE);
115 if (plugin.get_size == NULL) {
116 fprintf (stderr, "%s: %s: plugin must have a .get_size callback\n",
117 program_name, filename);
118 exit (EXIT_FAILURE);
120 if (plugin.pread == NULL) {
121 fprintf (stderr, "%s: %s: plugin must have a .pread callback\n",
122 program_name, filename);
123 exit (EXIT_FAILURE);
126 len = strlen (plugin.name);
127 if (len == 0) {
128 fprintf (stderr, "%s: %s: plugin.name field must not be empty\n",
129 program_name, filename);
130 exit (EXIT_FAILURE);
132 for (i = 0; i < len; ++i) {
133 if (!((plugin.name[i] >= '0' && plugin.name[i] <= '9') ||
134 (plugin.name[i] >= 'a' && plugin.name[i] <= 'z') ||
135 (plugin.name[i] >= 'A' && plugin.name[i] <= 'Z'))) {
136 fprintf (stderr, "%s: %s: plugin.name ('%s') field must contain only ASCII alphanumeric characters\n",
137 program_name, filename, plugin.name);
138 exit (EXIT_FAILURE);
142 debug ("registered %s (name %s)", filename, plugin.name);
144 /* Call the on-load callback if it exists. */
145 debug ("%s: load", filename);
146 if (plugin.load)
147 plugin.load ();
150 void
151 plugin_cleanup (void)
153 if (dl) {
154 debug ("%s: unload", filename);
155 if (plugin.unload)
156 plugin.unload ();
158 dlclose (dl);
159 dl = NULL;
160 free (filename);
161 filename = NULL;
165 const char *
166 plugin_name (void)
168 assert (dl);
170 return plugin.name;
173 void
174 plugin_usage (void)
176 assert (dl);
178 printf ("%s", plugin.name);
179 if (plugin.longname)
180 printf (" (%s)", plugin.longname);
181 printf ("\n");
182 if (plugin.description) {
183 printf ("\n");
184 printf ("%s\n", plugin.description);
186 if (plugin.config_help) {
187 printf ("\n");
188 printf ("%s\n", plugin.config_help);
192 void
193 plugin_version (void)
195 assert (dl);
197 printf ("%s", plugin.name);
198 if (plugin.version)
199 printf (" %s", plugin.version);
200 printf ("\n");
203 void
204 plugin_config (const char *key, const char *value)
206 assert (dl);
208 debug ("%s: config key=%s, value=%s",
209 filename, key, value);
211 if (plugin.config == NULL) {
212 fprintf (stderr, "%s: %s: this plugin does not need command line configuration\n"
213 "Try using: %s --help %s\n",
214 program_name, filename,
215 program_name, filename);
216 exit (EXIT_FAILURE);
219 if (plugin.config (key, value) == -1)
220 exit (EXIT_FAILURE);
223 void
224 plugin_config_complete (void)
226 assert (dl);
228 debug ("%s: config_complete", filename);
230 if (!plugin.config_complete)
231 return;
233 if (plugin.config_complete () == -1)
234 exit (EXIT_FAILURE);
237 /* Handle the thread model. */
238 void
239 plugin_lock_connection (void)
241 assert (dl);
243 if (plugin._thread_model <= NBDKIT_THREAD_MODEL_SERIALIZE_CONNECTIONS) {
244 debug ("%s: acquire connection lock", filename);
245 pthread_mutex_lock (&connection_lock);
249 void
250 plugin_unlock_connection (void)
252 assert (dl);
254 if (plugin._thread_model <= NBDKIT_THREAD_MODEL_SERIALIZE_CONNECTIONS) {
255 debug ("%s: release connection lock", filename);
256 pthread_mutex_unlock (&connection_lock);
260 void
261 plugin_lock_request (struct connection *conn)
263 assert (dl);
265 if (plugin._thread_model <= NBDKIT_THREAD_MODEL_SERIALIZE_ALL_REQUESTS) {
266 debug ("acquire global request lock");
267 pthread_mutex_lock (&all_requests_lock);
270 if (plugin._thread_model <= NBDKIT_THREAD_MODEL_SERIALIZE_REQUESTS) {
271 debug ("acquire per-connection request lock");
272 pthread_mutex_lock (&conn->request_lock);
276 void
277 plugin_unlock_request (struct connection *conn)
279 assert (dl);
281 if (plugin._thread_model <= NBDKIT_THREAD_MODEL_SERIALIZE_REQUESTS) {
282 debug ("release per-connection request lock");
283 pthread_mutex_unlock (&conn->request_lock);
286 if (plugin._thread_model <= NBDKIT_THREAD_MODEL_SERIALIZE_ALL_REQUESTS) {
287 debug ("release global request lock");
288 pthread_mutex_unlock (&all_requests_lock);
293 plugin_open (struct connection *conn, int readonly)
295 void *handle;
297 assert (dl);
298 assert (conn->handle == NULL);
299 assert (plugin.open != NULL);
301 debug ("%s: open readonly=%d", filename, readonly);
303 handle = plugin.open (readonly);
304 if (!handle)
305 return -1;
307 conn->handle = handle;
308 return 0;
311 void
312 plugin_close (struct connection *conn)
314 assert (dl);
315 assert (conn->handle);
317 debug ("close");
319 if (plugin.close)
320 plugin.close (conn->handle);
322 conn->handle = NULL;
325 int64_t
326 plugin_get_size (struct connection *conn)
328 assert (dl);
329 assert (conn->handle);
330 assert (plugin.get_size != NULL);
332 debug ("get_size");
334 return plugin.get_size (conn->handle);
338 plugin_can_write (struct connection *conn)
340 assert (dl);
341 assert (conn->handle);
343 debug ("can_write");
345 if (plugin.can_write)
346 return plugin.can_write (conn->handle);
347 else
348 return plugin.pwrite != NULL;
352 plugin_can_flush (struct connection *conn)
354 assert (dl);
355 assert (conn->handle);
357 debug ("can_flush");
359 if (plugin.can_flush)
360 return plugin.can_flush (conn->handle);
361 else
362 return plugin.flush != NULL;
366 plugin_is_rotational (struct connection *conn)
368 assert (dl);
369 assert (conn->handle);
371 debug ("is_rotational");
373 if (plugin.is_rotational)
374 return plugin.is_rotational (conn->handle);
375 else
376 return 0; /* assume false */
380 plugin_can_trim (struct connection *conn)
382 assert (dl);
383 assert (conn->handle);
385 debug ("can_trim");
387 if (plugin.can_trim)
388 return plugin.can_trim (conn->handle);
389 else
390 return plugin.trim != NULL;
394 plugin_pread (struct connection *conn,
395 void *buf, uint32_t count, uint64_t offset)
397 assert (dl);
398 assert (conn->handle);
399 assert (plugin.pread != NULL);
401 debug ("pread count=%" PRIu32 " offset=%" PRIu64, count, offset);
403 return plugin.pread (conn->handle, buf, count, offset);
407 plugin_pwrite (struct connection *conn,
408 void *buf, uint32_t count, uint64_t offset)
410 assert (dl);
411 assert (conn->handle);
413 debug ("pwrite count=%" PRIu32 " offset=%" PRIu64, count, offset);
415 if (plugin.pwrite != NULL)
416 return plugin.pwrite (conn->handle, buf, count, offset);
417 else {
418 errno = EROFS;
419 return -1;
424 plugin_flush (struct connection *conn)
426 assert (dl);
427 assert (conn->handle);
429 debug ("flush");
431 if (plugin.flush != NULL)
432 return plugin.flush (conn->handle);
433 else {
434 errno = EINVAL;
435 return -1;
440 plugin_trim (struct connection *conn, uint32_t count, uint64_t offset)
442 assert (dl);
443 assert (conn->handle);
445 debug ("trim count=%" PRIu32 " offset=%" PRIu64, count, offset);
447 if (plugin.trim != NULL)
448 return plugin.trim (conn->handle, count, offset);
449 else {
450 errno = EINVAL;
451 return -1;