plugins: Wire up rust plugin support for NBD_INFO_INIT_STATE
[nbdkit/ericb.git] / server / captive.c
blob102a77ea8c787622df0fa08ff4ada2a80cbfb6d1
1 /* nbdkit
2 * Copyright (C) 2019 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
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 <stdarg.h>
38 #include <string.h>
39 #include <unistd.h>
40 #include <sys/types.h>
41 #include <sys/wait.h>
42 #include <signal.h>
43 #include <assert.h>
45 #include "utils.h"
47 #include "internal.h"
49 /* Handle the --run option. If run is NULL, does nothing. If run is
50 * not NULL then run nbdkit as a captive subprocess of the command.
52 void
53 run_command (void)
55 FILE *fp;
56 char *cmd = NULL;
57 size_t len = 0;
58 int r, status;
59 pid_t pid;
61 if (!run)
62 return;
64 assert (exportname != NULL);
66 fp = open_memstream (&cmd, &len);
67 if (fp == NULL) {
68 perror ("open_memstream");
69 exit (EXIT_FAILURE);
72 /* Construct $uri. */
73 fprintf (fp, "uri=");
74 if (port) {
75 fprintf (fp, "nbd://localhost:");
76 shell_quote (port, fp);
77 if (strcmp (exportname, "") != 0) {
78 putc ('/', fp);
79 uri_quote (exportname, fp);
82 else if (unixsocket) {
83 fprintf (fp, "nbd+unix://");
84 if (strcmp (exportname, "") != 0) {
85 putc ('/', fp);
86 uri_quote (exportname, fp);
88 fprintf (fp, "\\?socket=");
89 uri_quote (unixsocket, fp);
91 putc ('\n', fp);
93 /* Expose $exportname. */
94 fprintf (fp, "exportname=");
95 shell_quote (exportname, fp);
96 putc ('\n', fp);
98 /* Construct older $nbd "URL". Unfortunately guestfish and qemu take
99 * different syntax, so try to guess which one we need.
101 fprintf (fp, "nbd=");
102 if (strstr (run, "guestfish")) {
103 if (port) {
104 fprintf (fp, "nbd://localhost:");
105 shell_quote (port, fp);
107 else if (unixsocket) {
108 fprintf (fp, "nbd://\\?socket=");
109 shell_quote (unixsocket, fp);
111 else
112 abort ();
114 else /* qemu */ {
115 if (port) {
116 fprintf (fp, "nbd:localhost:");
117 shell_quote (port, fp);
119 else if (unixsocket) {
120 fprintf (fp, "nbd:unix:");
121 shell_quote (unixsocket, fp);
123 else
124 abort ();
126 putc ('\n', fp);
128 /* Construct $port and $unixsocket. */
129 fprintf (fp, "port=");
130 if (port)
131 shell_quote (port, fp);
132 putc ('\n', fp);
133 fprintf (fp, "unixsocket=");
134 if (unixsocket)
135 shell_quote (unixsocket, fp);
136 fprintf (fp, "\n");
138 /* Add the --run command. Note we don't have to quote this. */
139 fprintf (fp, "%s", run);
141 if (fclose (fp) == EOF) {
142 perror ("memstream failed");
143 exit (EXIT_FAILURE);
146 /* Fork. Captive nbdkit runs as the child process. */
147 pid = fork ();
148 if (pid == -1) {
149 perror ("fork");
150 exit (EXIT_FAILURE);
153 if (pid > 0) { /* Parent process is the run command. */
154 r = system (cmd);
155 if (r == -1) {
156 nbdkit_error ("failure to execute external command: %m");
157 r = EXIT_FAILURE;
159 else if (WIFEXITED (r))
160 r = WEXITSTATUS (r);
161 else {
162 assert (WIFSIGNALED (r));
163 fprintf (stderr, "%s: external command was killed by signal %d\n",
164 program_name, WTERMSIG (r));
165 r = WTERMSIG (r) + 128;
168 switch (waitpid (pid, &status, WNOHANG)) {
169 case -1:
170 nbdkit_error ("waitpid: %m");
171 r = EXIT_FAILURE;
172 break;
173 case 0:
174 /* Captive nbdkit still running; kill it, but no need to wait
175 * for it, as the --run program's exit status is good enough (if
176 * the captive nbdkit fails to exit after SIGTERM, we have a
177 * bigger bug to fix).
179 kill (pid, SIGTERM);
180 break;
181 default:
182 /* Captive nbdkit exited unexpectedly; update the exit status. */
183 if (WIFEXITED (status)) {
184 if (r == 0)
185 r = WEXITSTATUS (status);
187 else {
188 assert (WIFSIGNALED (status));
189 fprintf (stderr, "%s: nbdkit command was killed by signal %d\n",
190 program_name, WTERMSIG (status));
191 r = WTERMSIG (status) + 128;
195 exit (r);
198 free (cmd);
200 debug ("forked into background (new pid = %d)", getpid ());