Update Red Hat Copyright Notices
[nbdkit.git] / plugins / eval / eval.c
blobfbc158383956e10bf1c749aff5d459226aa576c2
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 <stdbool.h>
38 #include <inttypes.h>
39 #include <string.h>
40 #include <unistd.h>
41 #include <errno.h>
42 #include <sys/stat.h>
44 #define NBDKIT_API_VERSION 2
46 #include <nbdkit-plugin.h>
48 #include "cleanup.h"
49 #include "vector.h"
51 #include "call.h"
52 #include "methods.h"
54 static char *missing;
56 static const char *known_methods[] = {
57 "after_fork",
58 "block_size",
59 "cache",
60 "can_cache",
61 "can_extents",
62 "can_fast_zero",
63 "can_flush",
64 "can_fua",
65 "can_multi_conn",
66 "can_trim",
67 "can_write",
68 "can_zero",
69 "close",
70 "config",
71 "config_complete",
72 "default_export",
73 "dump_plugin",
74 "export_description",
75 "extents",
76 "flush",
77 "get_ready",
78 "get_size",
79 "is_rotational",
80 "list_exports",
81 "missing",
82 "open",
83 "pread",
84 "preconnect",
85 "pwrite",
86 "thread_model",
87 "trim",
88 "unload",
89 "zero",
90 NULL
93 /* List of method scripts that we have saved. This is stored in
94 * sorted order of method name.
96 struct method_script {
97 const char *method;
98 char *script;
100 DEFINE_VECTOR_TYPE (method_script_list, struct method_script);
101 static method_script_list method_scripts;
103 static int
104 compare_script (const void *methodvp, const struct method_script *entry)
106 const char *method = methodvp;
108 return strcmp (method, entry->method);
111 static int
112 insert_method_script (const char *method, char *script)
114 int r;
115 size_t i;
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()
122 * first to check.
124 assert (r != 0);
125 if (r < 0) {
126 /* Insert before this element. */
127 if (method_script_list_insert (&method_scripts, new_entry, i) == -1) {
128 nbdkit_error ("realloc: %m");
129 return -1;
131 return 0;
135 /* Insert at end of list. */
136 if (method_script_list_append (&method_scripts, new_entry) == -1) {
137 nbdkit_error ("realloc: %m");
138 return -1;
140 return 0;
143 /* This is called back by methods.c to get the current script name. */
144 const char *
145 get_script (const char *method)
147 struct method_script *p;
149 p = method_script_list_search (&method_scripts, method, compare_script);
150 if (p)
151 return p->script;
152 else
153 return missing;
156 /* Save a script into tmpdir. Return its full path (must be freed by
157 * the caller).
159 static char *
160 create_script (const char *method, const char *value)
162 FILE *fp;
163 char *script;
164 size_t len;
166 if (asprintf (&script, "%s/%s", tmpdir, method) == -1) {
167 nbdkit_error ("asprintf: %m");
168 return NULL;
171 /* Special case for user override of missing */
172 if (missing && strcmp (script, missing) == 0 && unlink (script) == -1) {
173 nbdkit_error ("unlink: %m");
174 free (script);
175 return NULL;
178 fp = fopen (script, "w");
179 if (fp == NULL) {
180 nbdkit_error ("fopen: %s: %m", script);
181 free (script);
182 return NULL;
184 len = strlen (value);
185 if (len > 0) {
186 if (fwrite (value, len, 1, fp) != 1 ||
187 (value[len - 1] != '\n' && fputc ('\n', fp) == EOF)) {
188 nbdkit_error ("fwrite: %s: %m", script);
189 fclose (fp);
190 free (script);
191 return NULL;
195 if (fchmod (fileno (fp), 0500) == -1) {
196 nbdkit_error ("fchmod: %s: %m", script);
197 fclose (fp);
198 free (script);
199 return NULL;
202 if (fclose (fp) == EOF) {
203 nbdkit_error ("fclose: %s: %m", script);
204 free (script);
205 return NULL;
208 return script;
211 static void
212 eval_load (void)
214 call_load ();
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");
221 if (!missing)
222 exit (EXIT_FAILURE);
225 static void
226 free_method_script (struct method_script entry)
228 free (entry.script);
231 static void
232 eval_unload (void)
234 const char *method = "unload";
235 const char *script = get_script (method);
237 /* Run the unload method. Ignore all errors. */
238 if (script) {
239 const char *args[] = { script, method, NULL };
241 call (args);
244 call_unload ();
245 method_script_list_iter (&method_scripts, free_method_script);
246 free (method_scripts.ptr);
247 free (missing);
250 static int
251 add_method (const char *key, const char *value)
253 char *script;
254 char *tmp = missing; /* Needed to allow user override of missing */
256 missing = NULL;
257 if (get_script (key) != NULL) {
258 missing = tmp;
259 nbdkit_error ("method %s defined more than once on the command line", key);
260 return -1;
262 missing = tmp;
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
266 * sorry.
268 if (strchr (key, '.') || strchr (key, '/')) {
269 nbdkit_error ("method name %s is invalid", key);
270 return -1;
273 /* Copy the value into a script in tmpdir. */
274 script = create_script (key, value);
275 if (!script)
276 return -1;
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);
284 static int
285 eval_config (const char *key, const char *value)
287 size_t i;
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)) {
301 case OK:
302 return 0;
304 case MISSING:
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);
308 return -1;
310 case ERROR:
311 return -1;
313 case RET_FALSE:
314 nbdkit_error ("%s: %s method returned unexpected code (3/false)",
315 script, "config");
316 errno = EIO;
317 return -1;
319 default: abort ();
323 static int
324 create_can_wrapper (const char *test_method, const char *can_method,
325 const char *content)
327 char *can_script;
329 if (get_script (test_method) != missing &&
330 get_script (can_method) == missing) {
331 can_script = create_script (can_method, content);
332 if (!can_script)
333 return -1;
334 return insert_method_script (can_method, can_script);
337 return 0;
340 static int
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
348 * plugins.
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)
356 return -1;
358 /* Call config_complete. */
359 switch (call (args)) {
360 case OK:
361 case MISSING:
362 return 0;
364 case ERROR:
365 return -1;
367 case RET_FALSE:
368 nbdkit_error ("%s: %s method returned unexpected code (3/false)",
369 script, method);
370 errno = EIO;
371 return -1;
373 default: abort ();
376 return 0;
379 #define eval_config_help \
380 "get_size=' SCRIPT '\n" \
381 "pread=' SCRIPT '\n" \
382 "[etc]"
384 /* See also the comments in call.c:call3() */
385 #define THREAD_MODEL NBDKIT_THREAD_MODEL_PARALLEL
387 static struct nbdkit_plugin plugin = {
388 .name = "eval",
389 .version = PACKAGE_VERSION,
390 .load = eval_load,
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,
405 .open = sh_open,
406 .close = sh_close,
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,
422 .pread = sh_pread,
423 .pwrite = sh_pwrite,
424 .flush = sh_flush,
425 .trim = sh_trim,
426 .zero = sh_zero,
427 .extents = sh_extents,
428 .cache = sh_cache,
430 .errno_is_preserved = 1,
433 NBDKIT_REGISTER_PLUGIN (plugin)