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 #define NBDKIT_API_VERSION 2
46 #include <nbdkit-plugin.h>
56 static const char *known_methods
[] = {
93 /* List of method scripts that we have saved. This is stored in
94 * sorted order of method name.
96 struct method_script
{
100 DEFINE_VECTOR_TYPE (method_script_list
, struct method_script
);
101 static method_script_list method_scripts
;
104 compare_script (const void *methodvp
, const struct method_script
*entry
)
106 const char *method
= methodvp
;
108 return strcmp (method
, entry
->method
);
112 insert_method_script (const char *method
, char *script
)
116 struct method_script new_entry
= { .method
= method
, .script
= script
};
118 for (i
= 0; i
< method_scripts
.len
; ++i
) {
119 r
= compare_script (method
, &method_scripts
.ptr
[i
]);
120 /* This shouldn't happen. insert_method_script() must not be
121 * called if the method has already been added. Call get_script()
126 /* Insert before this element. */
127 if (method_script_list_insert (&method_scripts
, new_entry
, i
) == -1) {
128 nbdkit_error ("realloc: %m");
135 /* Insert at end of list. */
136 if (method_script_list_append (&method_scripts
, new_entry
) == -1) {
137 nbdkit_error ("realloc: %m");
143 /* This is called back by methods.c to get the current script name. */
145 get_script (const char *method
)
147 struct method_script
*p
;
149 p
= method_script_list_search (&method_scripts
, method
, compare_script
);
156 /* Save a script into tmpdir. Return its full path (must be freed by
160 create_script (const char *method
, const char *value
)
166 if (asprintf (&script
, "%s/%s", tmpdir
, method
) == -1) {
167 nbdkit_error ("asprintf: %m");
171 /* Special case for user override of missing */
172 if (missing
&& strcmp (script
, missing
) == 0 && unlink (script
) == -1) {
173 nbdkit_error ("unlink: %m");
178 fp
= fopen (script
, "w");
180 nbdkit_error ("fopen: %s: %m", script
);
184 len
= strlen (value
);
186 if (fwrite (value
, len
, 1, fp
) != 1 ||
187 (value
[len
- 1] != '\n' && fputc ('\n', fp
) == EOF
)) {
188 nbdkit_error ("fwrite: %s: %m", script
);
195 if (fchmod (fileno (fp
), 0500) == -1) {
196 nbdkit_error ("fchmod: %s: %m", script
);
202 if (fclose (fp
) == EOF
) {
203 nbdkit_error ("fclose: %s: %m", script
);
216 /* To make things easier, create a "missing" script which always
217 * exits with code 2. If a method is missing we call this script
218 * instead. It can even be overridden by the user.
220 missing
= create_script ("missing", "exit 2");
226 free_method_script (struct method_script entry
)
234 const char *method
= "unload";
235 const char *script
= get_script (method
);
237 /* Run the unload method. Ignore all errors. */
239 const char *args
[] = { script
, method
, NULL
};
245 method_script_list_iter (&method_scripts
, free_method_script
);
246 free (method_scripts
.ptr
);
251 add_method (const char *key
, const char *value
)
254 char *tmp
= missing
; /* Needed to allow user override of missing */
257 if (get_script (key
) != NULL
) {
259 nbdkit_error ("method %s defined more than once on the command line", key
);
264 /* Do a bit of checking to make sure the key isn't malicious. This
265 * duplicates work already done by nbdkit, but better safe than
268 if (strchr (key
, '.') || strchr (key
, '/')) {
269 nbdkit_error ("method name %s is invalid", key
);
273 /* Copy the value into a script in tmpdir. */
274 script
= create_script (key
, value
);
278 /* After this, the script variable will be stored in the global
279 * array and freed on unload.
281 return insert_method_script (key
, script
);
285 eval_config (const char *key
, const char *value
)
289 /* Try to determine if this is a method or a user parameter. */
290 for (i
= 0; known_methods
[i
] != NULL
; ++i
) {
291 if (strcmp (key
, known_methods
[i
]) == 0)
292 return add_method (key
, value
);
295 /* User parameter, so call config. */
296 const char *method
= "config";
297 const char *script
= get_script (method
);
298 const char *args
[] = { script
, method
, key
, value
, NULL
};
300 switch (call (args
)) {
305 /* Emulate what core nbdkit does if a config callback is NULL. */
306 nbdkit_error ("%s: callback '%s' is unknown, and there is no 'config' "
307 "callback to handle it", script
, key
);
314 nbdkit_error ("%s: %s method returned unexpected code (3/false)",
324 create_can_wrapper (const char *test_method
, const char *can_method
,
329 if (get_script (test_method
) != missing
&&
330 get_script (can_method
) == missing
) {
331 can_script
= create_script (can_method
, content
);
334 return insert_method_script (can_method
, can_script
);
341 eval_config_complete (void)
343 const char *method
= "config_complete";
344 const char *script
= get_script (method
);
345 const char *args
[] = { script
, method
, NULL
};
347 /* Synthesize can_* scripts as the core nbdkit server would for C
350 if (create_can_wrapper ("pwrite", "can_write", "exit 0") == -1 ||
351 create_can_wrapper ("flush", "can_flush", "exit 0") == -1 ||
352 create_can_wrapper ("trim", "can_trim", "exit 0") == -1 ||
353 create_can_wrapper ("zero", "can_zero", "exit 0") == -1 ||
354 create_can_wrapper ("extents", "can_extents", "exit 0") == -1 ||
355 create_can_wrapper ("cache", "can_cache", "echo native") == -1)
358 /* Call config_complete. */
359 switch (call (args
)) {
368 nbdkit_error ("%s: %s method returned unexpected code (3/false)",
379 #define eval_config_help \
380 "get_size=' SCRIPT '\n" \
381 "pread=' SCRIPT '\n" \
384 /* See also the comments in call.c:call3() */
385 #define THREAD_MODEL NBDKIT_THREAD_MODEL_PARALLEL
387 static struct nbdkit_plugin plugin
= {
389 .version
= PACKAGE_VERSION
,
391 .unload
= eval_unload
,
393 .dump_plugin
= sh_dump_plugin
,
395 .config
= eval_config
,
396 .config_complete
= eval_config_complete
,
397 .config_help
= eval_config_help
,
398 .thread_model
= sh_thread_model
,
399 .get_ready
= sh_get_ready
,
400 .after_fork
= sh_after_fork
,
402 .preconnect
= sh_preconnect
,
403 .list_exports
= sh_list_exports
,
404 .default_export
= sh_default_export
,
408 .export_description
= sh_export_description
,
409 .get_size
= sh_get_size
,
410 .block_size
= sh_block_size
,
411 .can_write
= sh_can_write
,
412 .can_flush
= sh_can_flush
,
413 .is_rotational
= sh_is_rotational
,
414 .can_trim
= sh_can_trim
,
415 .can_zero
= sh_can_zero
,
416 .can_extents
= sh_can_extents
,
417 .can_fua
= sh_can_fua
,
418 .can_multi_conn
= sh_can_multi_conn
,
419 .can_cache
= sh_can_cache
,
420 .can_fast_zero
= sh_can_fast_zero
,
427 .extents
= sh_extents
,
430 .errno_is_preserved
= 1,
433 NBDKIT_REGISTER_PLUGIN (plugin
)