Update Red Hat Copyright Notices
[nbdkit.git] / plugins / sh / sh.c
blobac8133bbd98553bf2bf12e7c6f450c37ed82fe1b
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"
50 #include "call.h"
51 #include "methods.h"
53 static char *script;
54 static char *magic_config_key;
56 /* This is called back by methods.c to get the current script name (if
57 * set). The method parameter is ignored by nbdkit-sh-plugin.
59 const char *
60 get_script (const char *method)
62 return script;
65 static void
66 sh_load (void)
68 call_load ();
71 static void
72 sh_unload (void)
74 const char *method = "unload";
76 /* Run the unload method. Ignore all errors. */
77 if (script) {
78 const char *args[] = { script, method, NULL };
80 call (args);
83 call_unload ();
84 free (script);
85 free (magic_config_key);
88 /* This implements the "inline script" feature. Read stdin into a
89 * temporary file and return the name of the file which the caller
90 * must free. For convenience we put the temporary file into tmpdir
91 * but that's an implementation detail.
93 static char *
94 inline_script (void)
96 const char scriptname[] = "inline-script.sh";
97 char *filename = NULL;
98 CLEANUP_FREE char *cmd = NULL;
100 if (!nbdkit_stdio_safe ()) {
101 nbdkit_error ("inline script is incompatible with -s");
102 return NULL;
105 if (asprintf (&filename, "%s/%s", tmpdir, scriptname) == -1) {
106 nbdkit_error ("asprintf: %m");
107 goto err;
110 /* Safe because both the tmpdir and script name are controlled by us
111 * and don't contain characters that need quoting.
113 if (asprintf (&cmd, "cat > %s", filename) == -1) {
114 nbdkit_error ("asprintf: %m");
115 goto err;
118 if (system (cmd) != 0) {
119 nbdkit_error ("sh: failed to copy inline script to temporary file");
120 goto err;
123 if (chmod (filename, 0500) == -1) {
124 nbdkit_error ("chmod: %s: %m", filename);
125 goto err;
128 return filename;
130 err:
131 free (filename);
132 return NULL;
135 static int
136 sh_config (const char *key, const char *value)
138 if (!script) {
139 /* The first parameter MUST be "script". */
140 if (strcmp (key, "script") != 0) {
141 nbdkit_error ("the first parameter must be script=/path/to/script");
142 return -1;
145 /* If the script name is not "-" then it's expected to be a
146 * filename, otherwise it's an inline script which must be read
147 * into a temporary file. Either way we want an absolute path.
149 if (strcmp (value, "-") != 0)
150 script = nbdkit_realpath (value);
151 else
152 script = inline_script ();
153 if (script == NULL)
154 return -1;
156 /* Call the load method. */
157 const char *args[] = { script, "load", NULL };
158 switch (call (args)) {
159 case OK:
160 case MISSING:
161 break;
163 case ERROR:
164 return -1;
166 case RET_FALSE:
167 nbdkit_error ("%s: %s method returned unexpected code (3/false)",
168 script, "load");
169 errno = EIO;
170 return -1;
172 default: abort ();
175 /* Call the magic_config_key method if it exists. */
176 const char *args2[] = { script, "magic_config_key", NULL };
177 CLEANUP_FREE_STRING string s = empty_vector;
178 switch (call_read (&s, args2)) {
179 case OK:
180 if (s.len > 0 && s.ptr[s.len-1] == '\n')
181 s.ptr[s.len-1] = '\0';
182 magic_config_key = strdup (s.ptr);
183 if (magic_config_key == NULL) {
184 nbdkit_error ("strdup: %m");
185 return -1;
187 break;
189 case MISSING:
190 break;
192 case ERROR:
193 return -1;
195 case RET_FALSE:
196 nbdkit_error ("%s: %s method returned unexpected code (3/false)",
197 script, "magic_config_key");
198 errno = EIO;
199 return -1;
201 default: abort ();
204 else {
205 /* If the script sets a magic_config_key then it's possible that
206 * we will be called here with key == "script" (which is the
207 * plugin.magic_config_key). If that happens then swap in the
208 * script magic_config_key as the key. However if the script
209 * didn't define a magic_config_key then it's an error, emulating
210 * the behaviour of the core server.
212 if (strcmp (key, "script") == 0) {
213 if (magic_config_key)
214 key = magic_config_key;
215 else {
216 nbdkit_error ("%s: expecting key=value on the command line but got: "
217 "%s\n",
218 script, value);
219 return -1;
223 const char *args[] = { script, "config", key, value, NULL };
224 switch (call (args)) {
225 case OK:
226 return 0;
228 case MISSING:
229 /* Emulate what core nbdkit does if a config callback is NULL. */
230 nbdkit_error ("%s: this plugin does not need command line configuration",
231 script);
232 return -1;
234 case ERROR:
235 return -1;
237 case RET_FALSE:
238 nbdkit_error ("%s: %s method returned unexpected code (3/false)",
239 script, "config");
240 errno = EIO;
241 return -1;
243 default: abort ();
247 return 0;
250 static int
251 sh_config_complete (void)
253 const char *args[] = { script, "config_complete", NULL };
255 if (!script) {
256 nbdkit_error ("missing script parameter");
257 return -1;
260 switch (call (args)) {
261 case OK:
262 case MISSING:
263 return 0;
265 case ERROR:
266 return -1;
268 case RET_FALSE:
269 nbdkit_error ("%s: %s method returned unexpected code (3/false)",
270 script, "config_complete");
271 errno = EIO;
272 return -1;
274 default: abort ();
278 #define sh_config_help \
279 "script=<FILENAME> (required) The shell script to run.\n" \
280 "[other arguments may be used by the plugin that you load]"
282 /* See also the comments in call.c:call3() */
283 #define THREAD_MODEL NBDKIT_THREAD_MODEL_PARALLEL
285 static struct nbdkit_plugin plugin = {
286 .name = "sh",
287 .version = PACKAGE_VERSION,
288 .load = sh_load,
289 .unload = sh_unload,
291 .dump_plugin = sh_dump_plugin,
293 .config = sh_config,
294 .config_complete = sh_config_complete,
295 .config_help = sh_config_help,
296 .magic_config_key = "script",
297 .thread_model = sh_thread_model,
298 .get_ready = sh_get_ready,
299 .after_fork = sh_after_fork,
301 .preconnect = sh_preconnect,
302 .list_exports = sh_list_exports,
303 .default_export = sh_default_export,
304 .open = sh_open,
305 .close = sh_close,
307 .export_description = sh_export_description,
308 .get_size = sh_get_size,
309 .block_size = sh_block_size,
310 .can_write = sh_can_write,
311 .can_flush = sh_can_flush,
312 .is_rotational = sh_is_rotational,
313 .can_trim = sh_can_trim,
314 .can_zero = sh_can_zero,
315 .can_extents = sh_can_extents,
316 .can_fua = sh_can_fua,
317 .can_multi_conn = sh_can_multi_conn,
318 .can_cache = sh_can_cache,
319 .can_fast_zero = sh_can_fast_zero,
321 .pread = sh_pread,
322 .pwrite = sh_pwrite,
323 .flush = sh_flush,
324 .trim = sh_trim,
325 .zero = sh_zero,
326 .extents = sh_extents,
327 .cache = sh_cache,
329 .errno_is_preserved = 1,
332 NBDKIT_REGISTER_PLUGIN (plugin)