Update Red Hat Copyright Notices
[nbdkit.git] / plugins / tcl / tcl.c
blob141eb86c3362348ed11041412c93f001ca96ead5
1 /* nbdkit
2 * Copyright Red Hat
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * * Neither the name of Red Hat nor the names of its contributors may be
16 * used to endorse or promote products derived from this software without
17 * specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
22 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR
23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
26 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
27 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
29 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
33 #include <config.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <unistd.h>
39 #include <errno.h>
40 #include <assert.h>
42 #include <tcl.h>
44 #include <nbdkit-plugin.h>
46 static Tcl_Interp *interp;
47 static const char *script;
49 static void
50 tcl_load (void)
52 //Tcl_FindExecutable ("nbdkit");
53 interp = Tcl_CreateInterp ();
54 if (Tcl_Init (interp) != TCL_OK) {
55 nbdkit_error ("cannot initialize Tcl interpreter: %s",
56 Tcl_GetStringResult (interp));
57 exit (EXIT_FAILURE);
61 static void
62 tcl_unload (void)
64 if (interp)
65 Tcl_DeleteInterp (interp);
66 Tcl_Finalize ();
69 /* Test if proc was defined by the Tcl code. */
70 static int
71 proc_defined (const char *name)
73 int r;
74 Tcl_Obj *cmd;
76 cmd = Tcl_NewObj ();
77 Tcl_IncrRefCount (cmd);
78 Tcl_ListObjAppendElement (0, cmd, Tcl_NewStringObj ("info", -1));
79 Tcl_ListObjAppendElement (0, cmd, Tcl_NewStringObj ("procs", -1));
80 Tcl_ListObjAppendElement (0, cmd, Tcl_NewStringObj (name, -1));
81 r = Tcl_EvalObjEx (interp, cmd, TCL_EVAL_DIRECT);
82 Tcl_DecrRefCount (cmd);
83 if (r != TCL_OK) {
84 nbdkit_error ("info procs: %s", Tcl_GetStringResult (interp));
85 return 0; /* We can't return an error here, just return false. */
88 /* 'info procs name' returns the proc name if it exists, else empty
89 * string, so we can just check if the result is not empty.
91 return strcmp (Tcl_GetStringResult (interp), "") != 0;
94 static void
95 tcl_dump_plugin (void)
97 #ifdef TCL_VERSION
98 printf ("tcl_version=%s\n", TCL_VERSION);
99 #endif
101 #ifdef TCL_PATCH_LEVEL
102 printf ("tcl_patch_level=%s\n", TCL_PATCH_LEVEL);
103 #endif
105 if (script && proc_defined ("dump_plugin")) {
106 int r;
107 Tcl_Obj *cmd;
109 cmd = Tcl_NewObj ();
110 Tcl_IncrRefCount (cmd);
111 Tcl_ListObjAppendElement (0, cmd, Tcl_NewStringObj ("dump_plugin", -1));
112 r = Tcl_EvalObjEx (interp, cmd, TCL_EVAL_DIRECT);
113 Tcl_DecrRefCount (cmd);
114 if (r != TCL_OK)
115 nbdkit_error ("dump_plugin: %s", Tcl_GetStringResult (interp));
119 static int
120 tcl_config (const char *key, const char *value)
122 int r;
124 if (!script) {
125 /* The first parameter MUST be "script". */
126 if (strcmp (key, "script") != 0) {
127 nbdkit_error ("the first parameter must be script=/path/to/script.tcl");
128 return -1;
130 script = value;
132 assert (interp);
134 /* Load the Tcl file. */
135 r = Tcl_EvalFile (interp, script);
136 if (r != TCL_OK) {
137 if (r == TCL_ERROR)
138 nbdkit_error ("could not load Tcl script: %s: line %d: %s",
139 script, Tcl_GetErrorLine (interp),
140 Tcl_GetStringResult (interp));
141 else
142 nbdkit_error ("could not load Tcl script: %s: %s",
143 script, Tcl_GetStringResult (interp));
144 return -1;
147 /* Minimal set of callbacks which are required (by nbdkit itself). */
148 if (!proc_defined ("plugin_open") ||
149 !proc_defined ("get_size") ||
150 !proc_defined ("pread")) {
151 nbdkit_error ("%s: one of the required callbacks "
152 "'plugin_open', 'get_size' or 'pread' "
153 "is not defined by this Tcl script. "
154 "nbdkit requires these callbacks.", script);
155 return -1;
158 else if (proc_defined ("config")) {
159 Tcl_Obj *cmd;
161 cmd = Tcl_NewObj ();
162 Tcl_IncrRefCount (cmd);
163 Tcl_ListObjAppendElement (0, cmd, Tcl_NewStringObj ("config", -1));
164 Tcl_ListObjAppendElement (0, cmd, Tcl_NewStringObj (key, -1));
165 Tcl_ListObjAppendElement (0, cmd, Tcl_NewStringObj (value, -1));
166 r = Tcl_EvalObjEx (interp, cmd, TCL_EVAL_DIRECT);
167 Tcl_DecrRefCount (cmd);
168 if (r != TCL_OK) {
169 nbdkit_error ("config: %s", Tcl_GetStringResult (interp));
170 return -1;
173 else {
174 /* Emulate what core nbdkit does if a config callback is NULL. */
175 nbdkit_error ("%s: this plugin does not need command line configuration",
176 script);
177 return -1;
180 return 0;
183 static int
184 tcl_config_complete (void)
186 int r;
187 Tcl_Obj *cmd;
189 if (proc_defined ("config_complete")) {
190 cmd = Tcl_NewObj ();
191 Tcl_IncrRefCount (cmd);
192 Tcl_ListObjAppendElement (0, cmd, Tcl_NewStringObj ("config_complete", -1));
193 r = Tcl_EvalObjEx (interp, cmd, TCL_EVAL_DIRECT);
194 Tcl_DecrRefCount (cmd);
195 if (r != TCL_OK) {
196 nbdkit_error ("config_complete: %s", Tcl_GetStringResult (interp));
197 return -1;
201 return 0;
204 static void *
205 tcl_open (int readonly)
207 int r;
208 Tcl_Obj *cmd, *res;
210 cmd = Tcl_NewObj ();
211 Tcl_IncrRefCount (cmd);
212 Tcl_ListObjAppendElement (0, cmd, Tcl_NewStringObj ("plugin_open", -1));
213 Tcl_ListObjAppendElement (0, cmd, Tcl_NewBooleanObj (readonly));
214 r = Tcl_EvalObjEx (interp, cmd, TCL_EVAL_DIRECT);
215 Tcl_DecrRefCount (cmd);
216 if (r != TCL_OK) {
217 nbdkit_error ("plugin_open: %s", Tcl_GetStringResult (interp));
218 return NULL;
221 res = Tcl_GetObjResult (interp);
222 Tcl_IncrRefCount (res);
223 return res;
226 static void
227 tcl_close (void *handle)
229 int r;
230 Tcl_Obj *h = handle, *cmd;
232 if (proc_defined ("plugin_close")) {
233 cmd = Tcl_NewObj ();
234 Tcl_IncrRefCount (cmd);
235 Tcl_ListObjAppendElement (0, cmd, Tcl_NewStringObj ("plugin_close", -1));
236 Tcl_ListObjAppendElement (0, cmd, h);
237 r = Tcl_EvalObjEx (interp, cmd, TCL_EVAL_DIRECT);
238 Tcl_DecrRefCount (cmd);
239 if (r != TCL_OK)
240 nbdkit_error ("plugin_close: %s", Tcl_GetStringResult (interp));
243 /* Ensure that the handle is freed. */
244 Tcl_DecrRefCount (h);
247 static int64_t
248 tcl_get_size (void *handle)
250 int r;
251 Tcl_Obj *h = handle, *cmd, *res;
252 Tcl_WideInt size;
254 cmd = Tcl_NewObj ();
255 Tcl_IncrRefCount (cmd);
256 Tcl_ListObjAppendElement (0, cmd, Tcl_NewStringObj ("get_size", -1));
257 Tcl_ListObjAppendElement (0, cmd, h);
258 r = Tcl_EvalObjEx (interp, cmd, TCL_EVAL_DIRECT);
259 Tcl_DecrRefCount (cmd);
260 if (r != TCL_OK) {
261 nbdkit_error ("get_size: %s", Tcl_GetStringResult (interp));
262 return -1;
265 res = Tcl_GetObjResult (interp);
266 if (Tcl_GetWideIntFromObj (interp, res, &size) != TCL_OK) {
267 nbdkit_error ("get_size: Tcl_GetWideIntFromObj: %s",
268 Tcl_GetStringResult (interp));
269 return -1;
271 return size;
274 static int
275 tcl_pread (void *handle, void *buf, uint32_t count, uint64_t offset)
277 int r;
278 Tcl_Obj *h = handle, *cmd, *res;
279 unsigned char *res_bin;
280 int res_len;
282 cmd = Tcl_NewObj ();
283 Tcl_IncrRefCount (cmd);
284 Tcl_ListObjAppendElement (0, cmd, Tcl_NewStringObj ("pread", -1));
285 Tcl_ListObjAppendElement (0, cmd, h);
286 Tcl_ListObjAppendElement (0, cmd, Tcl_NewIntObj (count));
287 Tcl_ListObjAppendElement (0, cmd, Tcl_NewWideIntObj (offset));
288 r = Tcl_EvalObjEx (interp, cmd, TCL_EVAL_DIRECT);
289 Tcl_DecrRefCount (cmd);
290 if (r != TCL_OK) {
291 nbdkit_error ("pread: %s", Tcl_GetStringResult (interp));
292 return -1;
295 res = Tcl_GetObjResult (interp);
296 res_bin = Tcl_GetByteArrayFromObj (res, &res_len);
297 if (res_len < count) {
298 nbdkit_error ("pread: buffer returned from pread is too small");
299 return -1;
302 memcpy (buf, res_bin, count);
303 return 0;
306 static int
307 tcl_pwrite (void *handle, const void *buf, uint32_t count, uint64_t offset)
309 if (proc_defined ("pwrite")) {
310 int r;
311 Tcl_Obj *h = handle, *cmd;
313 cmd = Tcl_NewObj ();
314 Tcl_IncrRefCount (cmd);
315 Tcl_ListObjAppendElement (0, cmd, Tcl_NewStringObj ("pwrite", -1));
316 Tcl_ListObjAppendElement (0, cmd, h);
317 Tcl_ListObjAppendElement (0, cmd, Tcl_NewByteArrayObj (buf, count));
318 Tcl_ListObjAppendElement (0, cmd, Tcl_NewWideIntObj (offset));
319 r = Tcl_EvalObjEx (interp, cmd, TCL_EVAL_DIRECT);
320 Tcl_DecrRefCount (cmd);
321 if (r != TCL_OK) {
322 nbdkit_error ("pwrite: %s", Tcl_GetStringResult (interp));
323 return -1;
325 return 0;
328 nbdkit_error ("pwrite not implemented");
329 return -1;
332 static int
333 tcl_can_write (void *handle)
335 if (proc_defined ("can_write")) {
336 int r;
337 Tcl_Obj *h = handle, *cmd, *res;
339 cmd = Tcl_NewObj ();
340 Tcl_IncrRefCount (cmd);
341 Tcl_ListObjAppendElement (0, cmd, Tcl_NewStringObj ("can_write", -1));
342 Tcl_ListObjAppendElement (0, cmd, h);
343 r = Tcl_EvalObjEx (interp, cmd, TCL_EVAL_DIRECT);
344 Tcl_DecrRefCount (cmd);
345 if (r != TCL_OK) {
346 nbdkit_error ("can_write: %s", Tcl_GetStringResult (interp));
347 return -1;
349 res = Tcl_GetObjResult (interp);
350 Tcl_GetBooleanFromObj (interp, res, &r);
351 return r;
353 /* No can_write callback, but there's a pwrite callback defined, so
354 * return 1. (In C modules, nbdkit would do this).
356 else if (proc_defined ("pwrite"))
357 return 1;
358 else
359 return 0;
362 static int
363 tcl_can_flush (void *handle)
365 if (proc_defined ("can_flush")) {
366 int r;
367 Tcl_Obj *h = handle, *cmd, *res;
369 cmd = Tcl_NewObj ();
370 Tcl_IncrRefCount (cmd);
371 Tcl_ListObjAppendElement (0, cmd, Tcl_NewStringObj ("can_flush", -1));
372 Tcl_ListObjAppendElement (0, cmd, h);
373 r = Tcl_EvalObjEx (interp, cmd, TCL_EVAL_DIRECT);
374 Tcl_DecrRefCount (cmd);
375 if (r != TCL_OK) {
376 nbdkit_error ("can_flush: %s", Tcl_GetStringResult (interp));
377 return -1;
379 res = Tcl_GetObjResult (interp);
380 Tcl_GetBooleanFromObj (interp, res, &r);
381 return r;
383 /* No can_flush callback, but there's a plugin_flush callback
384 * defined, so return 1. (In C modules, nbdkit would do this).
386 else if (proc_defined ("plugin_flush"))
387 return 1;
388 else
389 return 0;
392 static int
393 tcl_can_trim (void *handle)
395 if (proc_defined ("can_trim")) {
396 int r;
397 Tcl_Obj *h = handle, *cmd, *res;
399 cmd = Tcl_NewObj ();
400 Tcl_IncrRefCount (cmd);
401 Tcl_ListObjAppendElement (0, cmd, Tcl_NewStringObj ("can_trim", -1));
402 Tcl_ListObjAppendElement (0, cmd, h);
403 r = Tcl_EvalObjEx (interp, cmd, TCL_EVAL_DIRECT);
404 Tcl_DecrRefCount (cmd);
405 if (r != TCL_OK) {
406 nbdkit_error ("can_trim: %s", Tcl_GetStringResult (interp));
407 return -1;
409 res = Tcl_GetObjResult (interp);
410 Tcl_GetBooleanFromObj (interp, res, &r);
411 return r;
413 /* No can_trim callback, but there's a trim callback defined, so
414 * return 1. (In C modules, nbdkit would do this).
416 else if (proc_defined ("trim"))
417 return 1;
418 else
419 return 0;
422 static int
423 tcl_zero (void *handle, uint32_t count, uint64_t offset, int may_trim)
425 if (proc_defined ("zero")) {
426 int r;
427 Tcl_Obj *h = handle, *cmd;
429 cmd = Tcl_NewObj ();
430 Tcl_IncrRefCount (cmd);
431 Tcl_ListObjAppendElement (0, cmd, Tcl_NewStringObj ("zero", -1));
432 Tcl_ListObjAppendElement (0, cmd, h);
433 Tcl_ListObjAppendElement (0, cmd, Tcl_NewIntObj (count));
434 Tcl_ListObjAppendElement (0, cmd, Tcl_NewWideIntObj (offset));
435 Tcl_ListObjAppendElement (0, cmd, Tcl_NewBooleanObj (may_trim));
436 r = Tcl_EvalObjEx (interp, cmd, TCL_EVAL_DIRECT);
437 Tcl_DecrRefCount (cmd);
438 if (r != TCL_OK) {
439 nbdkit_error ("zero: %s", Tcl_GetStringResult (interp));
440 return -1;
442 return 0;
445 nbdkit_debug ("zero falling back to pwrite");
446 nbdkit_set_error (EOPNOTSUPP);
447 return -1;
450 static int
451 tcl_is_rotational (void *handle)
453 if (proc_defined ("is_rotational")) {
454 int r;
455 Tcl_Obj *h = handle, *cmd, *res;
457 cmd = Tcl_NewObj ();
458 Tcl_IncrRefCount (cmd);
459 Tcl_ListObjAppendElement (0, cmd, Tcl_NewStringObj ("is_rotational", -1));
460 Tcl_ListObjAppendElement (0, cmd, h);
461 r = Tcl_EvalObjEx (interp, cmd, TCL_EVAL_DIRECT);
462 Tcl_DecrRefCount (cmd);
463 if (r != TCL_OK) {
464 nbdkit_error ("is_rotational: %s", Tcl_GetStringResult (interp));
465 return -1;
467 res = Tcl_GetObjResult (interp);
468 Tcl_GetBooleanFromObj (interp, res, &r);
469 return r;
471 else
472 return 0;
475 static int
476 tcl_flush (void *handle)
478 if (proc_defined ("plugin_flush")) {
479 int r;
480 Tcl_Obj *h = handle, *cmd;
482 cmd = Tcl_NewObj ();
483 Tcl_IncrRefCount (cmd);
484 Tcl_ListObjAppendElement (0, cmd, Tcl_NewStringObj ("plugin_flush", -1));
485 Tcl_ListObjAppendElement (0, cmd, h);
486 r = Tcl_EvalObjEx (interp, cmd, TCL_EVAL_DIRECT);
487 Tcl_DecrRefCount (cmd);
488 if (r != TCL_OK) {
489 nbdkit_error ("plugin_flush: %s", Tcl_GetStringResult (interp));
490 return -1;
492 return 0;
495 /* Ignore lack of flush callback, although probably nbdkit will
496 * never call this since .can_flush returns false.
498 return 0;
501 static int
502 tcl_trim (void *handle, uint32_t count, uint64_t offset)
504 if (proc_defined ("trim")) {
505 int r;
506 Tcl_Obj *h = handle, *cmd;
508 cmd = Tcl_NewObj ();
509 Tcl_IncrRefCount (cmd);
510 Tcl_ListObjAppendElement (0, cmd, Tcl_NewStringObj ("trim", -1));
511 Tcl_ListObjAppendElement (0, cmd, h);
512 Tcl_ListObjAppendElement (0, cmd, Tcl_NewIntObj (count));
513 Tcl_ListObjAppendElement (0, cmd, Tcl_NewWideIntObj (offset));
514 r = Tcl_EvalObjEx (interp, cmd, TCL_EVAL_DIRECT);
515 Tcl_DecrRefCount (cmd);
516 if (r != TCL_OK) {
517 nbdkit_error ("trim: %s", Tcl_GetStringResult (interp));
518 return -1;
520 return 0;
523 /* Ignore lack of trim callback, although probably nbdkit will never
524 * call this since .can_trim returns false.
526 return 0;
529 #define tcl_config_help \
530 "script=<FILENAME> (required) The Tcl script to run.\n" \
531 "[other arguments may be used by the plugin that you load]"
533 #define THREAD_MODEL NBDKIT_THREAD_MODEL_SERIALIZE_ALL_REQUESTS
535 static struct nbdkit_plugin plugin = {
536 .name = "tcl",
537 .version = PACKAGE_VERSION,
539 .load = tcl_load,
540 .unload = tcl_unload,
541 .dump_plugin = tcl_dump_plugin,
543 .config = tcl_config,
544 .config_complete = tcl_config_complete,
545 .config_help = tcl_config_help,
547 .open = tcl_open,
548 .close = tcl_close,
550 .get_size = tcl_get_size,
551 .can_write = tcl_can_write,
552 .can_flush = tcl_can_flush,
553 .is_rotational = tcl_is_rotational,
554 .can_trim = tcl_can_trim,
556 .pread = tcl_pread,
557 .pwrite = tcl_pwrite,
558 .flush = tcl_flush,
559 .trim = tcl_trim,
560 .zero = tcl_zero,
563 NBDKIT_REGISTER_PLUGIN (plugin)