4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
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
44 #include <nbdkit-plugin.h>
46 static Tcl_Interp
*interp
;
47 static const char *script
;
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
));
65 Tcl_DeleteInterp (interp
);
69 /* Test if proc was defined by the Tcl code. */
71 proc_defined (const char *name
)
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
);
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;
95 tcl_dump_plugin (void)
98 printf ("tcl_version=%s\n", TCL_VERSION
);
101 #ifdef TCL_PATCH_LEVEL
102 printf ("tcl_patch_level=%s\n", TCL_PATCH_LEVEL
);
105 if (script
&& proc_defined ("dump_plugin")) {
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
);
115 nbdkit_error ("dump_plugin: %s", Tcl_GetStringResult (interp
));
120 tcl_config (const char *key
, const char *value
)
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");
134 /* Load the Tcl file. */
135 r
= Tcl_EvalFile (interp
, script
);
138 nbdkit_error ("could not load Tcl script: %s: line %d: %s",
139 script
, Tcl_GetErrorLine (interp
),
140 Tcl_GetStringResult (interp
));
142 nbdkit_error ("could not load Tcl script: %s: %s",
143 script
, Tcl_GetStringResult (interp
));
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
);
158 else if (proc_defined ("config")) {
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
);
169 nbdkit_error ("config: %s", Tcl_GetStringResult (interp
));
174 /* Emulate what core nbdkit does if a config callback is NULL. */
175 nbdkit_error ("%s: this plugin does not need command line configuration",
184 tcl_config_complete (void)
189 if (proc_defined ("config_complete")) {
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
);
196 nbdkit_error ("config_complete: %s", Tcl_GetStringResult (interp
));
205 tcl_open (int readonly
)
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
);
217 nbdkit_error ("plugin_open: %s", Tcl_GetStringResult (interp
));
221 res
= Tcl_GetObjResult (interp
);
222 Tcl_IncrRefCount (res
);
227 tcl_close (void *handle
)
230 Tcl_Obj
*h
= handle
, *cmd
;
232 if (proc_defined ("plugin_close")) {
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
);
240 nbdkit_error ("plugin_close: %s", Tcl_GetStringResult (interp
));
243 /* Ensure that the handle is freed. */
244 Tcl_DecrRefCount (h
);
248 tcl_get_size (void *handle
)
251 Tcl_Obj
*h
= handle
, *cmd
, *res
;
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
);
261 nbdkit_error ("get_size: %s", Tcl_GetStringResult (interp
));
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
));
275 tcl_pread (void *handle
, void *buf
, uint32_t count
, uint64_t offset
)
278 Tcl_Obj
*h
= handle
, *cmd
, *res
;
279 unsigned char *res_bin
;
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
);
291 nbdkit_error ("pread: %s", Tcl_GetStringResult (interp
));
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");
302 memcpy (buf
, res_bin
, count
);
307 tcl_pwrite (void *handle
, const void *buf
, uint32_t count
, uint64_t offset
)
309 if (proc_defined ("pwrite")) {
311 Tcl_Obj
*h
= handle
, *cmd
;
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
);
322 nbdkit_error ("pwrite: %s", Tcl_GetStringResult (interp
));
328 nbdkit_error ("pwrite not implemented");
333 tcl_can_write (void *handle
)
335 if (proc_defined ("can_write")) {
337 Tcl_Obj
*h
= handle
, *cmd
, *res
;
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
);
346 nbdkit_error ("can_write: %s", Tcl_GetStringResult (interp
));
349 res
= Tcl_GetObjResult (interp
);
350 Tcl_GetBooleanFromObj (interp
, res
, &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"))
363 tcl_can_flush (void *handle
)
365 if (proc_defined ("can_flush")) {
367 Tcl_Obj
*h
= handle
, *cmd
, *res
;
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
);
376 nbdkit_error ("can_flush: %s", Tcl_GetStringResult (interp
));
379 res
= Tcl_GetObjResult (interp
);
380 Tcl_GetBooleanFromObj (interp
, res
, &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"))
393 tcl_can_trim (void *handle
)
395 if (proc_defined ("can_trim")) {
397 Tcl_Obj
*h
= handle
, *cmd
, *res
;
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
);
406 nbdkit_error ("can_trim: %s", Tcl_GetStringResult (interp
));
409 res
= Tcl_GetObjResult (interp
);
410 Tcl_GetBooleanFromObj (interp
, res
, &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"))
423 tcl_zero (void *handle
, uint32_t count
, uint64_t offset
, int may_trim
)
425 if (proc_defined ("zero")) {
427 Tcl_Obj
*h
= handle
, *cmd
;
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
);
439 nbdkit_error ("zero: %s", Tcl_GetStringResult (interp
));
445 nbdkit_debug ("zero falling back to pwrite");
446 nbdkit_set_error (EOPNOTSUPP
);
451 tcl_is_rotational (void *handle
)
453 if (proc_defined ("is_rotational")) {
455 Tcl_Obj
*h
= handle
, *cmd
, *res
;
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
);
464 nbdkit_error ("is_rotational: %s", Tcl_GetStringResult (interp
));
467 res
= Tcl_GetObjResult (interp
);
468 Tcl_GetBooleanFromObj (interp
, res
, &r
);
476 tcl_flush (void *handle
)
478 if (proc_defined ("plugin_flush")) {
480 Tcl_Obj
*h
= handle
, *cmd
;
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
);
489 nbdkit_error ("plugin_flush: %s", Tcl_GetStringResult (interp
));
495 /* Ignore lack of flush callback, although probably nbdkit will
496 * never call this since .can_flush returns false.
502 tcl_trim (void *handle
, uint32_t count
, uint64_t offset
)
504 if (proc_defined ("trim")) {
506 Tcl_Obj
*h
= handle
, *cmd
;
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
);
517 nbdkit_error ("trim: %s", Tcl_GetStringResult (interp
));
523 /* Ignore lack of trim callback, although probably nbdkit will never
524 * call this since .can_trim returns false.
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
= {
537 .version
= PACKAGE_VERSION
,
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
,
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
,
557 .pwrite
= tcl_pwrite
,
563 NBDKIT_REGISTER_PLUGIN (plugin
)