2 * Copyright (C) 2013-2018 Red Hat Inc.
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
49 #include <nbdkit-plugin.h>
53 /* Use of perl.h insists on shadowing my_perl during XS(). */
54 #pragma GCC diagnostic ignored "-Wshadow"
56 static PerlInterpreter
*my_perl
;
57 static const char *script
;
63 const char *argv
[2] = { "nbdkit", NULL
};
65 /* Full Perl interpreter initialization is deferred until we read
66 * the first config parameter (which MUST be "script").
68 PERL_SYS_INIT3 (&argc
, (char ***) &argv
, &environ
);
69 my_perl
= perl_alloc ();
71 nbdkit_error ("out of memory allocating Perl interpreter");
74 perl_construct (my_perl
);
80 if (my_perl
!= NULL
) {
81 perl_destruct (my_perl
);
87 /* We use this function to test if the named callback is defined
88 * in the loaded Perl code.
91 callback_defined (const char *perl_func_name
)
94 CLEANUP_FREE
char *cmd
= NULL
;
96 if (asprintf (&cmd
, "defined &%s", perl_func_name
) == -1) {
101 ret
= eval_pv (cmd
, FALSE
);
106 /* Check for a Perl exception, and convert it to an nbdkit error. */
108 check_perl_failure (void)
110 SV
*errsv
= get_sv ("@", TRUE
);
112 if (SvTRUE (errsv
)) {
115 CLEANUP_FREE
char *err_copy
= NULL
;
117 err
= SvPV (errsv
, n
);
119 /* Need to chop off the final \n if there is one. The only way to
120 * do this is to copy the string.
122 err_copy
= strndup (err
, n
);
123 if (err_copy
== NULL
) {
124 nbdkit_error ("malloc failure: original error: %s", err
);
127 if (n
> 0 && err_copy
[n
-1] == '\n')
128 err_copy
[n
-1] = '\0';
130 nbdkit_error ("%s", err_copy
);
138 static int last_error
;
143 /* Is it worth adding error checking for bad arguments? */
145 last_error
= SvIV (ST (0));
146 nbdkit_set_error (last_error
);
151 EXTERN_C
void boot_DynaLoader (pTHX_ CV
*cv
);
156 char *file
= __FILE__
;
157 newXS ("DynaLoader::boot_DynaLoader", boot_DynaLoader
, file
);
158 newXS ("Nbdkit::set_error", set_error
, file
);
162 perl_dump_plugin (void)
166 #ifdef PERL_VERSION_STRING
167 printf ("perl_version=%s\n", PERL_VERSION_STRING
);
170 if (script
&& callback_defined ("dump_plugin")) {
175 call_pv ("dump_plugin", G_EVAL
|G_VOID
|G_DISCARD
);
184 perl_config (const char *key
, const char *value
)
188 char *argv
[3] = { "nbdkit", NULL
, NULL
};
190 /* The first parameter MUST be "script". */
191 if (strcmp (key
, "script") != 0) {
192 nbdkit_error ("the first parameter must be "
193 "script=/path/to/perl/script.pl");
200 /* Load the Perl script. */
201 argv
[1] = (char *) script
;
202 if (perl_parse (my_perl
, xs_init
, argc
, argv
, NULL
) == -1) {
203 nbdkit_error ("%s: error parsing this script", script
);
207 /* Run the Perl script. Note that top-level definitions such as
208 * global variables don't work at all unless you do this.
210 if (perl_run (my_perl
) == -1) {
211 nbdkit_error ("%s: error running this script", script
);
215 /* Minimal set of callbacks which are required (by nbdkit itself). */
216 if (!callback_defined ("open") ||
217 !callback_defined ("get_size") ||
218 !callback_defined ("pread")) {
219 nbdkit_error ("%s: one of the required callbacks "
220 "'open', 'get_size' or 'pread' "
221 "is not defined by this Perl script. "
222 "nbdkit requires these callbacks.", script
);
226 else if (callback_defined ("config")) {
229 /* Other parameters are passed to the Perl .config callback. */
233 XPUSHs (sv_2mortal (newSVpv (key
, strlen (key
))));
234 XPUSHs (sv_2mortal (newSVpv (value
, strlen (value
))));
236 call_pv ("config", G_EVAL
|G_VOID
|G_DISCARD
);
242 if (check_perl_failure () == -1)
246 /* Emulate what core nbdkit does if a config callback is NULL. */
247 nbdkit_error ("%s: this plugin does not need command line configuration",
256 perl_config_complete (void)
260 if (callback_defined ("config_complete")) {
265 call_pv ("config_complete", G_EVAL
|G_VOID
|G_DISCARD
);
270 if (check_perl_failure () == -1)
278 perl_open (int readonly
)
283 /* We check in perl_config that this callback is defined. */
287 XPUSHs (readonly
? &PL_sv_yes
: &PL_sv_no
);
289 call_pv ("open", G_EVAL
|G_SCALAR
);
296 if (check_perl_failure () == -1)
299 nbdkit_debug ("open returns handle (SV *) = %p (type %d)",
306 perl_close (void *handle
)
310 nbdkit_debug ("close called with handle (SV *) = %p (type %d)",
311 handle
, SvTYPE ((SV
*) handle
));
313 if (callback_defined ("close")) {
319 call_pv ("close", G_EVAL
|G_VOID
|G_DISCARD
);
325 check_perl_failure (); /* ignore return value */
328 /* Since nbdkit has closed (and forgotten) the handle, we can now
331 SvREFCNT_dec ((SV
*) handle
);
335 perl_get_size (void *handle
)
341 /* We check in perl_config that this callback is defined. */
347 call_pv ("get_size", G_EVAL
|G_SCALAR
);
349 /* For some reason, this only works if split into two separate statements: */
356 if (check_perl_failure () == -1)
359 nbdkit_debug ("get_size returned %" PRIi64
, size
);
365 perl_boolean (void *handle
, const char *callback_name
, const char *fn_name
)
371 if (callback_defined (callback_name
)) {
372 /* If there's a Perl callback, call it. */
378 call_pv (callback_name
, G_EVAL
|G_SCALAR
);
386 if (check_perl_failure () == -1)
391 /* No Perl callback. If the function is defined, return 1. */
392 else if (fn_name
&& callback_defined (fn_name
))
399 perl_can_write (void *handle
)
401 return perl_boolean (handle
, "can_write", "write");
405 perl_can_flush (void *handle
)
407 return perl_boolean (handle
, "can_flush", "flush");
411 perl_can_trim (void *handle
)
413 return perl_boolean (handle
, "can_trim", "trim");
417 perl_is_rotational (void *handle
)
419 return perl_boolean (handle
, "is_rotational", NULL
);
423 perl_pread (void *handle
, void *buf
,
424 uint32_t count
, uint64_t offset
)
432 /* We check in perl_config that this callback is defined. */
437 XPUSHs (sv_2mortal (newSViv (count
)));
438 XPUSHs (sv_2mortal (newSViv (offset
)));
440 call_pv ("pread", G_EVAL
|G_SCALAR
);
443 pbuf
= SvPV (sv
, len
);
445 nbdkit_error ("buffer returned from pread is too small");
449 memcpy (buf
, pbuf
, count
);
454 if (check_perl_failure () == -1)
461 perl_pwrite (void *handle
, const void *buf
,
462 uint32_t count
, uint64_t offset
)
466 if (callback_defined ("pwrite")) {
471 XPUSHs (sv_2mortal (newSVpv (buf
, count
)));
472 XPUSHs (sv_2mortal (newSViv (offset
)));
474 call_pv ("pwrite", G_EVAL
|G_VOID
|G_DISCARD
);
480 if (check_perl_failure () == -1)
486 nbdkit_error ("write not implemented");
491 perl_zero (void *handle
, uint32_t count
, uint64_t offset
, int may_trim
)
495 if (callback_defined ("zero")) {
501 XPUSHs (sv_2mortal (newSViv (count
)));
502 XPUSHs (sv_2mortal (newSViv (offset
)));
503 XPUSHs (sv_2mortal (newSViv (may_trim
)));
505 call_pv ("zero", G_EVAL
|G_SCALAR
);
511 if (last_error
== EOPNOTSUPP
|| last_error
== ENOTSUP
) {
512 /* When user requests this particular error, we want to
513 gracefully fall back, and to accomodate both a normal return
515 nbdkit_debug ("zero requested falling back to pwrite");
518 if (check_perl_failure () == -1)
524 nbdkit_debug ("zero falling back to pwrite");
525 nbdkit_set_error (EOPNOTSUPP
);
530 perl_flush (void *handle
)
534 if (callback_defined ("flush")) {
540 call_pv ("flush", G_EVAL
|G_VOID
|G_DISCARD
);
546 if (check_perl_failure () == -1)
552 /* Ignore lack of flush callback in Perl, although probably nbdkit
553 * will never call this since .can_flush returns false.
559 perl_trim (void *handle
, uint32_t count
, uint64_t offset
)
563 if (callback_defined ("trim")) {
568 XPUSHs (sv_2mortal (newSViv (count
)));
569 XPUSHs (sv_2mortal (newSViv (offset
)));
571 call_pv ("trim", G_EVAL
|G_VOID
|G_DISCARD
);
577 if (check_perl_failure () == -1)
583 /* Ignore lack of trim callback in Perl, although probably nbdkit
584 * will never call this since .can_trim returns false.
589 #define perl_config_help \
590 "script=<FILENAME> (required) The Perl plugin to run.\n" \
591 "[other arguments may be used by the plugin that you load]"
593 #define THREAD_MODEL NBDKIT_THREAD_MODEL_SERIALIZE_ALL_REQUESTS
595 static struct nbdkit_plugin plugin
= {
597 .version
= PACKAGE_VERSION
,
600 .unload
= perl_unload
,
601 .dump_plugin
= perl_dump_plugin
,
603 .config
= perl_config
,
604 .config_complete
= perl_config_complete
,
605 .config_help
= perl_config_help
,
610 .get_size
= perl_get_size
,
611 .can_write
= perl_can_write
,
612 .can_flush
= perl_can_flush
,
613 .is_rotational
= perl_is_rotational
,
614 .can_trim
= perl_can_trim
,
617 .pwrite
= perl_pwrite
,
623 NBDKIT_REGISTER_PLUGIN(plugin
)