blob: 92dfb757c7564668b30b3af245289f14d94f919a [file] [log] [blame]
Jeremy Kerrf403c422018-07-26 12:14:56 +08001/* Copyright 2018 IBM Corp.
2 *
3 * Author: Jeremy Kerr <jk@ozlabs.org>
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
6 * use this file except in compliance with the License. You may obtain a copy
7 * of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14 * License for the specific language governing permissions and limitations
15 * under the License.
16 */
17
18#define _GNU_SOURCE
19
Ed Tanous4136ad72023-07-26 11:15:56 -070020#include "config.h"
21
Jeremy Kerrc6134c12018-08-09 13:03:33 +080022#include <dirent.h>
Jeremy Kerrf403c422018-07-26 12:14:56 +080023#include <err.h>
24#include <errno.h>
25#include <fcntl.h>
Jeremy Kerr6f9c4332018-08-03 17:11:15 +080026#include <getopt.h>
Ed Tanous4136ad72023-07-26 11:15:56 -070027#include <json.h>
28#include <libudev.h>
Jeremy Kerrc6134c12018-08-09 13:03:33 +080029#include <limits.h>
Jeremy Kerrf403c422018-07-26 12:14:56 +080030#include <signal.h>
31#include <stdbool.h>
32#include <stdint.h>
33#include <stdio.h>
34#include <stdlib.h>
35#include <string.h>
Jeremy Kerrf403c422018-07-26 12:14:56 +080036#include <sys/poll.h>
37#include <sys/socket.h>
38#include <sys/stat.h>
Jeremy Kerrc6134c12018-08-09 13:03:33 +080039#include <sys/types.h>
Jeremy Kerrf403c422018-07-26 12:14:56 +080040#include <sys/un.h>
41#include <sys/wait.h>
Ed Tanous4136ad72023-07-26 11:15:56 -070042#include <unistd.h>
Jeremy Kerrf403c422018-07-26 12:14:56 +080043
Ed Tanous4136ad72023-07-26 11:15:56 -070044struct config
45{
46 char* name;
47 bool is_default;
48 char* nbd_device;
49 struct json_object* metadata;
Jeremy Kerr19527352018-08-03 15:04:38 +080050};
51
Ed Tanous4136ad72023-07-26 11:15:56 -070052struct ctx
53{
54 int sock;
55 int sock_client;
56 int signal_pipe[2];
57 char* sock_path;
58 pid_t nbd_client_pid;
59 pid_t state_hook_pid;
60 int nbd_timeout;
61 dev_t nbd_devno;
62 uint8_t* buf;
63 size_t bufsize;
64 struct config* configs;
65 int n_configs;
66 struct config* default_config;
67 struct config* config;
68 struct udev* udev;
69 struct udev_monitor* monitor;
Jeremy Kerrf403c422018-07-26 12:14:56 +080070};
71
Ed Tanous4136ad72023-07-26 11:15:56 -070072static const char* conf_path = SYSCONFDIR "/nbd-proxy/config.json";
73static const char* state_hook_path = SYSCONFDIR "/nbd-proxy/state";
74static const char* sockpath_tmpl = RUNSTATEDIR "/nbd.%d.sock";
Jeremy Kerrc6134c12018-08-09 13:03:33 +080075
Jeremy Kerrf403c422018-07-26 12:14:56 +080076static const size_t bufsize = 0x20000;
Jeremy Kerr19527352018-08-03 15:04:38 +080077static const int nbd_timeout_default = 30;
Jeremy Kerrf403c422018-07-26 12:14:56 +080078
Ed Tanous4136ad72023-07-26 11:15:56 -070079static int open_nbd_socket(struct ctx* ctx)
Jeremy Kerrf403c422018-07-26 12:14:56 +080080{
Ed Tanous4136ad72023-07-26 11:15:56 -070081 struct sockaddr_un addr;
82 char* path;
83 int sd, rc;
Jeremy Kerrf403c422018-07-26 12:14:56 +080084
Ed Tanous4136ad72023-07-26 11:15:56 -070085 rc = asprintf(&path, sockpath_tmpl, getpid());
86 if (rc < 0)
87 return -1;
Jeremy Kerrf403c422018-07-26 12:14:56 +080088
Ed Tanous4136ad72023-07-26 11:15:56 -070089 sd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
90 if (sd < 0)
91 {
92 warn("can't create socket");
93 goto err_free;
94 }
Jeremy Kerrf403c422018-07-26 12:14:56 +080095
Ed Tanous4136ad72023-07-26 11:15:56 -070096 rc = fchmod(sd, S_IRUSR | S_IWUSR);
97 if (rc)
98 {
99 warn("can't set permissions on socket");
100 goto err_close;
101 }
Jeremy Kerrf403c422018-07-26 12:14:56 +0800102
Ed Tanous4136ad72023-07-26 11:15:56 -0700103 addr.sun_family = AF_UNIX;
104 strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1);
Jeremy Kerrf403c422018-07-26 12:14:56 +0800105
Ed Tanous4136ad72023-07-26 11:15:56 -0700106 rc = bind(sd, (struct sockaddr*)&addr, sizeof(addr));
107 if (rc)
108 {
109 warn("can't bind to path %s", path);
110 goto err_close;
111 }
Jeremy Kerrf403c422018-07-26 12:14:56 +0800112
Ed Tanous4136ad72023-07-26 11:15:56 -0700113 rc = listen(sd, 1);
114 if (rc)
115 {
116 warn("can't listen on socket %s", path);
117 goto err_unlink;
118 }
Jeremy Kerrf403c422018-07-26 12:14:56 +0800119
Ed Tanous4136ad72023-07-26 11:15:56 -0700120 ctx->sock = sd;
121 ctx->sock_path = path;
122 return 0;
Jeremy Kerrf403c422018-07-26 12:14:56 +0800123
124err_unlink:
Ed Tanous4136ad72023-07-26 11:15:56 -0700125 unlink(path);
Jeremy Kerrf403c422018-07-26 12:14:56 +0800126err_close:
Ed Tanous4136ad72023-07-26 11:15:56 -0700127 close(sd);
Jeremy Kerrf403c422018-07-26 12:14:56 +0800128err_free:
Ed Tanous4136ad72023-07-26 11:15:56 -0700129 free(path);
130 return -1;
Jeremy Kerrf403c422018-07-26 12:14:56 +0800131}
132
Ed Tanous4136ad72023-07-26 11:15:56 -0700133static int start_nbd_client(struct ctx* ctx)
Jeremy Kerrf403c422018-07-26 12:14:56 +0800134{
Ed Tanous4136ad72023-07-26 11:15:56 -0700135 pid_t pid;
Jeremy Kerrf403c422018-07-26 12:14:56 +0800136
Ed Tanous4136ad72023-07-26 11:15:56 -0700137 pid = fork();
138 if (pid < 0)
139 {
140 warn("can't create client process");
141 return -1;
142 }
Jeremy Kerrf403c422018-07-26 12:14:56 +0800143
Ed Tanous4136ad72023-07-26 11:15:56 -0700144 /* child process: run nbd-client in non-fork mode */
145 if (pid == 0)
146 {
147 char timeout_str[10];
148 int fd;
Jeremy Kerrf403c422018-07-26 12:14:56 +0800149
Ed Tanous4136ad72023-07-26 11:15:56 -0700150 snprintf(timeout_str, sizeof(timeout_str), "%d", ctx->nbd_timeout);
Jeremy Kerr09295f22018-07-27 16:29:38 +0800151
Ed Tanous4136ad72023-07-26 11:15:56 -0700152 fd = open("/dev/null", O_RDWR | O_CLOEXEC);
153 if (fd < 0)
154 err(EXIT_FAILURE, "can't open /dev/null");
Jeremy Kerrf403c422018-07-26 12:14:56 +0800155
Ed Tanous4136ad72023-07-26 11:15:56 -0700156 dup2(fd, STDIN_FILENO);
157 dup2(fd, STDOUT_FILENO);
158 close(fd);
Jeremy Kerrf403c422018-07-26 12:14:56 +0800159
Ed Tanous4136ad72023-07-26 11:15:56 -0700160 execlp("nbd-client", "nbd-client", "-u", ctx->sock_path, "-n", "-L",
161 "-t", timeout_str, ctx->config->nbd_device, NULL);
162 err(EXIT_FAILURE, "can't start ndb client");
163 }
Jeremy Kerrf403c422018-07-26 12:14:56 +0800164
Ed Tanous4136ad72023-07-26 11:15:56 -0700165 ctx->nbd_client_pid = pid;
166 return 0;
Jeremy Kerrf403c422018-07-26 12:14:56 +0800167}
168
Ed Tanous4136ad72023-07-26 11:15:56 -0700169static void stop_nbd_client(struct ctx* ctx)
Jeremy Kerrf403c422018-07-26 12:14:56 +0800170{
Ed Tanous4136ad72023-07-26 11:15:56 -0700171 int rc;
Jeremy Kerrf403c422018-07-26 12:14:56 +0800172
Ed Tanous4136ad72023-07-26 11:15:56 -0700173 if (!ctx->nbd_client_pid)
174 return;
Jeremy Kerrf403c422018-07-26 12:14:56 +0800175
Ed Tanous4136ad72023-07-26 11:15:56 -0700176 rc = kill(ctx->nbd_client_pid, SIGTERM);
177 if (rc)
178 return;
Jeremy Kerrf403c422018-07-26 12:14:56 +0800179
Ed Tanous4136ad72023-07-26 11:15:56 -0700180 waitpid(ctx->nbd_client_pid, NULL, 0);
181 ctx->nbd_client_pid = 0;
Jeremy Kerrf403c422018-07-26 12:14:56 +0800182}
183
Ed Tanous4136ad72023-07-26 11:15:56 -0700184static int copy_fd(struct ctx* ctx, int fd_in, int fd_out)
Jeremy Kerrf403c422018-07-26 12:14:56 +0800185{
186#ifdef HAVE_SPLICE
Ed Tanous4136ad72023-07-26 11:15:56 -0700187 int rc;
Jeremy Kerrf403c422018-07-26 12:14:56 +0800188
Ed Tanous4136ad72023-07-26 11:15:56 -0700189 rc = splice(fd_in, NULL, fd_out, NULL, ctx->bufsize, 0);
190 if (rc < 0)
191 warn("splice");
Jeremy Kerrf403c422018-07-26 12:14:56 +0800192
Ed Tanous4136ad72023-07-26 11:15:56 -0700193 return rc;
Jeremy Kerrf403c422018-07-26 12:14:56 +0800194#else
Ed Tanous4136ad72023-07-26 11:15:56 -0700195 size_t len, pos;
196 ssize_t rc;
Jeremy Kerrf403c422018-07-26 12:14:56 +0800197
Ed Tanous4136ad72023-07-26 11:15:56 -0700198 for (;;)
199 {
200 errno = 0;
201 rc = read(fd_in, ctx->buf, ctx->bufsize);
202 if (rc < 0)
203 {
204 if (errno == EINTR)
205 continue;
206 warn("read failure");
207 return -1;
208 }
209 if (rc == 0)
210 return 0;
211 break;
212 }
Jeremy Kerrf403c422018-07-26 12:14:56 +0800213
Ed Tanous4136ad72023-07-26 11:15:56 -0700214 len = rc;
Jeremy Kerrf403c422018-07-26 12:14:56 +0800215
Ed Tanous4136ad72023-07-26 11:15:56 -0700216 for (pos = 0; pos < len;)
217 {
218 errno = 0;
219 rc = write(fd_out, ctx->buf + pos, len - pos);
220 if (rc < 0)
221 {
222 if (errno == EINTR)
223 continue;
224 warn("write failure");
225 return -1;
226 }
227 if (rc == 0)
228 break;
229 pos += rc;
230 }
Jeremy Kerrf403c422018-07-26 12:14:56 +0800231
Ed Tanous4136ad72023-07-26 11:15:56 -0700232 return pos;
Jeremy Kerrf403c422018-07-26 12:14:56 +0800233#endif
234}
235
236static int signal_pipe_fd = -1;
237
238static void signal_handler(int signal)
239{
Ed Tanous4136ad72023-07-26 11:15:56 -0700240 int rc;
Jeremy Kerrf403c422018-07-26 12:14:56 +0800241
Ed Tanous4136ad72023-07-26 11:15:56 -0700242 rc = write(signal_pipe_fd, &signal, sizeof(signal));
Jeremy Kerrf403c422018-07-26 12:14:56 +0800243
Ed Tanous4136ad72023-07-26 11:15:56 -0700244 /* not a lot we can do here but exit... */
245 if (rc != sizeof(signal))
246 exit(EXIT_FAILURE);
Jeremy Kerrf403c422018-07-26 12:14:56 +0800247}
248
Ed Tanous4136ad72023-07-26 11:15:56 -0700249static int setup_signals(struct ctx* ctx)
Jeremy Kerrf403c422018-07-26 12:14:56 +0800250{
Ed Tanous4136ad72023-07-26 11:15:56 -0700251 struct sigaction sa;
252 int rc;
Jeremy Kerrf403c422018-07-26 12:14:56 +0800253
Ed Tanous4136ad72023-07-26 11:15:56 -0700254 rc = pipe2(ctx->signal_pipe, O_CLOEXEC);
255 if (rc)
256 {
257 warn("cant setup signal pipe");
258 return -1;
259 }
Jeremy Kerrf403c422018-07-26 12:14:56 +0800260
Ed Tanous4136ad72023-07-26 11:15:56 -0700261 signal_pipe_fd = ctx->signal_pipe[1];
Jeremy Kerrf403c422018-07-26 12:14:56 +0800262
Ed Tanous4136ad72023-07-26 11:15:56 -0700263 memset(&sa, 0, sizeof(sa));
264 sa.sa_handler = signal_handler;
Jeremy Kerrf403c422018-07-26 12:14:56 +0800265
Ed Tanous4136ad72023-07-26 11:15:56 -0700266 sigaction(SIGINT, &sa, NULL);
267 sigaction(SIGTERM, &sa, NULL);
268 sigaction(SIGCHLD, &sa, NULL);
Jeremy Kerrf403c422018-07-26 12:14:56 +0800269
Ed Tanous4136ad72023-07-26 11:15:56 -0700270 return 0;
Jeremy Kerrf403c422018-07-26 12:14:56 +0800271}
272
Ed Tanous4136ad72023-07-26 11:15:56 -0700273static void cleanup_signals(struct ctx* ctx)
Jeremy Kerrf403c422018-07-26 12:14:56 +0800274{
Ed Tanous4136ad72023-07-26 11:15:56 -0700275 struct sigaction sa;
276 memset(&sa, 0, sizeof(sa));
277 sa.sa_handler = SIG_DFL;
Jeremy Kerrf403c422018-07-26 12:14:56 +0800278
Ed Tanous4136ad72023-07-26 11:15:56 -0700279 sigaction(SIGINT, &sa, NULL);
280 sigaction(SIGTERM, &sa, NULL);
281 sigaction(SIGCHLD, &sa, NULL);
Jeremy Kerrf403c422018-07-26 12:14:56 +0800282
Ed Tanous4136ad72023-07-26 11:15:56 -0700283 close(ctx->signal_pipe[0]);
284 close(ctx->signal_pipe[1]);
Jeremy Kerrf403c422018-07-26 12:14:56 +0800285}
286
Ed Tanous4136ad72023-07-26 11:15:56 -0700287static void process_sigchld(struct ctx* ctx, bool* exit)
Jeremy Kerrfa1d3752018-08-13 13:13:16 +0800288{
Ed Tanous4136ad72023-07-26 11:15:56 -0700289 int status;
290 pid_t pid;
Jeremy Kerrfa1d3752018-08-13 13:13:16 +0800291
Ed Tanous4136ad72023-07-26 11:15:56 -0700292 for (;;)
293 {
294 pid = waitpid(-1, &status, WNOHANG);
295 if (pid == 0)
296 break;
Jeremy Kerrfa1d3752018-08-13 13:13:16 +0800297
Ed Tanous4136ad72023-07-26 11:15:56 -0700298 if (pid == ctx->nbd_client_pid)
299 {
300 warnx("nbd client stopped (%s: %d); exiting",
301 WIFEXITED(status) ? "rc" : "sig",
302 WIFEXITED(status) ? WEXITSTATUS(status) : WTERMSIG(status));
303 ctx->nbd_client_pid = 0;
304 *exit = true;
305 }
306 else if (pid == ctx->state_hook_pid)
307 {
308 if (!WIFEXITED(status) || WEXITSTATUS(status))
309 {
310 warnx("state hook failed (%s: %d); exiting",
311 WIFEXITED(status) ? "rc" : "sig",
312 WIFEXITED(status) ? WEXITSTATUS(status)
313 : WTERMSIG(status));
314 *exit = true;
315 }
316 ctx->state_hook_pid = 0;
317 }
318 }
Jeremy Kerrfa1d3752018-08-13 13:13:16 +0800319}
320
Ed Tanous4136ad72023-07-26 11:15:56 -0700321static int process_signal_pipe(struct ctx* ctx, bool* exit)
Jeremy Kerrf403c422018-07-26 12:14:56 +0800322{
Ed Tanous4136ad72023-07-26 11:15:56 -0700323 int buf, rc;
Jeremy Kerrf403c422018-07-26 12:14:56 +0800324
Ed Tanous4136ad72023-07-26 11:15:56 -0700325 rc = read(ctx->signal_pipe[0], &buf, sizeof(buf));
326 if (rc != sizeof(buf))
327 return -1;
Jeremy Kerrf403c422018-07-26 12:14:56 +0800328
Ed Tanous4136ad72023-07-26 11:15:56 -0700329 *exit = false;
Jeremy Kerrf403c422018-07-26 12:14:56 +0800330
Ed Tanous4136ad72023-07-26 11:15:56 -0700331 switch (buf)
332 {
333 case SIGCHLD:
334 process_sigchld(ctx, exit);
335 break;
336 case SIGINT:
337 case SIGTERM:
338 *exit = true;
339 break;
340 }
Jeremy Kerrf403c422018-07-26 12:14:56 +0800341
Ed Tanous4136ad72023-07-26 11:15:56 -0700342 return 0;
Jeremy Kerrf403c422018-07-26 12:14:56 +0800343}
344
Ed Tanous4136ad72023-07-26 11:15:56 -0700345static int wait_for_nbd_client(struct ctx* ctx)
Jeremy Kerrf403c422018-07-26 12:14:56 +0800346{
Ed Tanous4136ad72023-07-26 11:15:56 -0700347 struct pollfd pollfds[2];
348 int rc;
Jeremy Kerrf403c422018-07-26 12:14:56 +0800349
Ed Tanous4136ad72023-07-26 11:15:56 -0700350 pollfds[0].fd = ctx->sock;
351 pollfds[0].events = POLLIN;
352 pollfds[1].fd = ctx->signal_pipe[0];
353 pollfds[1].events = POLLIN;
Jeremy Kerrf403c422018-07-26 12:14:56 +0800354
Ed Tanous4136ad72023-07-26 11:15:56 -0700355 for (;;)
356 {
357 errno = 0;
358 rc = poll(pollfds, 2, -1);
359 if (rc < 0)
360 {
361 if (errno == EINTR)
362 continue;
363 warn("poll failed");
364 return -1;
365 }
Jeremy Kerrf403c422018-07-26 12:14:56 +0800366
Ed Tanous4136ad72023-07-26 11:15:56 -0700367 if (pollfds[0].revents)
368 {
369 rc = accept4(ctx->sock, NULL, NULL, SOCK_CLOEXEC);
370 if (rc < 0)
371 {
372 warn("can't create connection");
373 return -1;
374 }
375 ctx->sock_client = rc;
376 break;
377 }
Jeremy Kerrf403c422018-07-26 12:14:56 +0800378
Ed Tanous4136ad72023-07-26 11:15:56 -0700379 if (pollfds[1].revents)
380 {
381 bool exit;
382 rc = process_signal_pipe(ctx, &exit);
383 if (rc || exit)
384 return -1;
385 }
386 }
Jeremy Kerrf403c422018-07-26 12:14:56 +0800387
Ed Tanous4136ad72023-07-26 11:15:56 -0700388 return 0;
Jeremy Kerrf403c422018-07-26 12:14:56 +0800389}
390
Ed Tanous4136ad72023-07-26 11:15:56 -0700391static int run_state_hook(struct ctx* ctx, const char* action, bool wait)
Jeremy Kerrc6134c12018-08-09 13:03:33 +0800392{
Ed Tanous4136ad72023-07-26 11:15:56 -0700393 int status, rc, fd;
394 pid_t pid;
Jeremy Kerrc6134c12018-08-09 13:03:33 +0800395
Ed Tanous4136ad72023-07-26 11:15:56 -0700396 /* if the hook isn't present or executable, that's not necessarily
397 * an error condition */
398 if (access(state_hook_path, X_OK))
399 return 0;
Jeremy Kerra87af842018-08-13 11:48:23 +0800400
Ed Tanous4136ad72023-07-26 11:15:56 -0700401 pid = fork();
402 if (pid < 0)
403 {
404 warn("can't fork to execute hook %s", state_hook_path);
405 return -1;
406 }
Jeremy Kerrc6134c12018-08-09 13:03:33 +0800407
Ed Tanous4136ad72023-07-26 11:15:56 -0700408 if (!pid)
409 {
410 const char* argv0;
Jeremy Kerra87af842018-08-13 11:48:23 +0800411
Ed Tanous4136ad72023-07-26 11:15:56 -0700412 argv0 = strchr(state_hook_path, '/');
413 if (!argv0)
414 argv0 = state_hook_path;
Jeremy Kerra87af842018-08-13 11:48:23 +0800415
Ed Tanous4136ad72023-07-26 11:15:56 -0700416 fd = open("/dev/null", O_RDWR | O_CLOEXEC);
417 if (fd < 0)
418 exit(EXIT_FAILURE);
Jeremy Kerrc6134c12018-08-09 13:03:33 +0800419
Ed Tanous4136ad72023-07-26 11:15:56 -0700420 dup2(fd, STDIN_FILENO);
421 dup2(fd, STDOUT_FILENO);
422 execl(state_hook_path, argv0, action, ctx->config->name, NULL);
423 exit(EXIT_FAILURE);
424 }
Jeremy Kerrc6134c12018-08-09 13:03:33 +0800425
Ed Tanous4136ad72023-07-26 11:15:56 -0700426 if (!wait)
427 {
428 ctx->state_hook_pid = pid;
429 return 0;
430 }
Jeremy Kerrfa1d3752018-08-13 13:13:16 +0800431
Ed Tanous4136ad72023-07-26 11:15:56 -0700432 rc = waitpid(pid, &status, 0);
433 if (rc < 0)
434 {
435 warn("wait");
436 return -1;
437 }
Jeremy Kerrc6134c12018-08-09 13:03:33 +0800438
Ed Tanous4136ad72023-07-26 11:15:56 -0700439 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
440 {
441 warnx("hook %s failed", state_hook_path);
442 return -1;
443 }
Jeremy Kerrc6134c12018-08-09 13:03:33 +0800444
Ed Tanous4136ad72023-07-26 11:15:56 -0700445 return 0;
Jeremy Kerrc6134c12018-08-09 13:03:33 +0800446}
447
Ed Tanous4136ad72023-07-26 11:15:56 -0700448static int udev_init(struct ctx* ctx)
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800449{
Ed Tanous4136ad72023-07-26 11:15:56 -0700450 int rc;
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800451
Ed Tanous4136ad72023-07-26 11:15:56 -0700452 ctx->udev = udev_new();
Ed Tanousba2a6362023-07-26 12:16:09 -0700453 if (!ctx->udev)
Ed Tanous4136ad72023-07-26 11:15:56 -0700454 {
455 warn("can't create udev object");
456 return -1;
457 }
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800458
Ed Tanous4136ad72023-07-26 11:15:56 -0700459 ctx->monitor = udev_monitor_new_from_netlink(ctx->udev, "kernel");
460 if (!ctx->monitor)
461 {
462 warn("can't create udev monitor");
463 goto out_unref_udev;
464 }
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800465
Ed Tanous4136ad72023-07-26 11:15:56 -0700466 rc = udev_monitor_filter_add_match_subsystem_devtype(ctx->monitor, "block",
467 "disk");
468 if (rc)
469 {
470 warn("can't create udev monitor filter");
471 goto out_unref_monitor;
472 }
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800473
Ed Tanous4136ad72023-07-26 11:15:56 -0700474 rc = udev_monitor_enable_receiving(ctx->monitor);
475 if (rc)
476 {
477 warn("can't start udev monitor");
478 goto out_unref_monitor;
479 }
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800480
Ed Tanous4136ad72023-07-26 11:15:56 -0700481 return 0;
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800482
483out_unref_monitor:
Ed Tanous4136ad72023-07-26 11:15:56 -0700484 udev_monitor_unref(ctx->monitor);
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800485out_unref_udev:
Ed Tanous4136ad72023-07-26 11:15:56 -0700486 udev_unref(ctx->udev);
487 return -1;
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800488}
489
Ed Tanous4136ad72023-07-26 11:15:56 -0700490static void udev_free(struct ctx* ctx)
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800491{
Ed Tanous4136ad72023-07-26 11:15:56 -0700492 udev_monitor_unref(ctx->monitor);
493 udev_unref(ctx->udev);
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800494}
495
496/* Check for the change event on our nbd device, signifying that the kernel
497 * has finished initialising the block device. Once we see the event, we run
498 * the "start" state hook, and close the udev monitor.
499 *
500 * Returns:
501 * 0 if no processing was performed
502 * -1 on state hook error (and the nbd session should be closed)
503 */
Ed Tanous4136ad72023-07-26 11:15:56 -0700504static int udev_process(struct ctx* ctx)
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800505{
Ed Tanous4136ad72023-07-26 11:15:56 -0700506 struct udev_device* dev;
507 bool action_is_change;
508 dev_t devno;
509 int rc;
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800510
Ed Tanous4136ad72023-07-26 11:15:56 -0700511 dev = udev_monitor_receive_device(ctx->monitor);
512 if (!dev)
513 return 0;
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800514
Ed Tanous4136ad72023-07-26 11:15:56 -0700515 devno = udev_device_get_devnum(dev);
516 action_is_change = !strcmp(udev_device_get_action(dev), "change");
517 udev_device_unref(dev);
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800518
Ed Tanous4136ad72023-07-26 11:15:56 -0700519 if (devno != ctx->nbd_devno)
520 return 0;
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800521
Ed Tanous4136ad72023-07-26 11:15:56 -0700522 if (!action_is_change)
523 return 0;
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800524
Ed Tanous4136ad72023-07-26 11:15:56 -0700525 udev_monitor_unref(ctx->monitor);
526 udev_unref(ctx->udev);
527 ctx->monitor = NULL;
528 ctx->udev = NULL;
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800529
Ed Tanous4136ad72023-07-26 11:15:56 -0700530 rc = run_state_hook(ctx, "start", false);
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800531
Ed Tanous4136ad72023-07-26 11:15:56 -0700532 return rc;
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800533}
534
Ed Tanous4136ad72023-07-26 11:15:56 -0700535static int run_proxy(struct ctx* ctx)
Jeremy Kerrf403c422018-07-26 12:14:56 +0800536{
Ed Tanous4136ad72023-07-26 11:15:56 -0700537 struct pollfd pollfds[4];
538 bool exit = false;
539 int rc, n_fd;
Jeremy Kerrf403c422018-07-26 12:14:56 +0800540
Ed Tanous4136ad72023-07-26 11:15:56 -0700541 /* main proxy: forward data between stdio & socket */
542 pollfds[0].fd = ctx->sock_client;
543 pollfds[0].events = POLLIN;
544 pollfds[1].fd = STDIN_FILENO;
545 pollfds[1].events = POLLIN;
546 pollfds[2].fd = ctx->signal_pipe[0];
547 pollfds[2].events = POLLIN;
548 pollfds[3].fd = udev_monitor_get_fd(ctx->monitor);
549 pollfds[3].events = POLLIN;
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800550
Ed Tanous4136ad72023-07-26 11:15:56 -0700551 n_fd = 4;
Jeremy Kerrf403c422018-07-26 12:14:56 +0800552
Ed Tanous4136ad72023-07-26 11:15:56 -0700553 for (;;)
554 {
555 errno = 0;
556 rc = poll(pollfds, n_fd, -1);
557 if (rc < 0)
558 {
559 if (errno == EINTR)
560 continue;
561 warn("poll failed");
562 break;
563 }
Jeremy Kerrf403c422018-07-26 12:14:56 +0800564
Ed Tanous4136ad72023-07-26 11:15:56 -0700565 if (pollfds[0].revents)
566 {
567 rc = copy_fd(ctx, ctx->sock_client, STDOUT_FILENO);
568 if (rc <= 0)
569 break;
570 }
Jeremy Kerrf403c422018-07-26 12:14:56 +0800571
Ed Tanous4136ad72023-07-26 11:15:56 -0700572 if (pollfds[1].revents)
573 {
574 rc = copy_fd(ctx, STDIN_FILENO, ctx->sock_client);
575 if (rc <= 0)
576 break;
577 }
Jeremy Kerrf403c422018-07-26 12:14:56 +0800578
Ed Tanous4136ad72023-07-26 11:15:56 -0700579 if (pollfds[2].revents)
580 {
581 rc = process_signal_pipe(ctx, &exit);
582 if (rc || exit)
583 break;
584 }
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800585
Ed Tanous4136ad72023-07-26 11:15:56 -0700586 if (pollfds[3].revents)
587 {
588 rc = udev_process(ctx);
589 if (rc)
590 break;
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800591
Ed Tanous4136ad72023-07-26 11:15:56 -0700592 /* udev_process may have closed the udev connection,
593 * in which case we can stop polling on its fd */
594 if (!ctx->udev)
595 {
596 pollfds[3].fd = 0;
597 pollfds[3].revents = 0;
598 n_fd = 3;
599 }
600 }
601 }
Jeremy Kerrf403c422018-07-26 12:14:56 +0800602
Ed Tanous4136ad72023-07-26 11:15:56 -0700603 return rc ? -1 : 0;
Jeremy Kerrf403c422018-07-26 12:14:56 +0800604}
605
Ed Tanous4136ad72023-07-26 11:15:56 -0700606static void print_metadata(struct ctx* ctx)
Jeremy Kerr13bb28f2018-08-09 10:41:10 +0800607{
Ed Tanous4136ad72023-07-26 11:15:56 -0700608 struct json_object* md;
609 int i;
Jeremy Kerr13bb28f2018-08-09 10:41:10 +0800610
Ed Tanous4136ad72023-07-26 11:15:56 -0700611 md = json_object_new_object();
Jeremy Kerr13bb28f2018-08-09 10:41:10 +0800612
Ed Tanous4136ad72023-07-26 11:15:56 -0700613 for (i = 0; i < ctx->n_configs; i++)
614 {
615 struct config* config = &ctx->configs[i];
616 json_object_object_add(md, config->name,
617 json_object_get(config->metadata));
618 }
Jeremy Kerr13bb28f2018-08-09 10:41:10 +0800619
Ed Tanous4136ad72023-07-26 11:15:56 -0700620 puts(json_object_get_string(md));
Jeremy Kerr13bb28f2018-08-09 10:41:10 +0800621
Ed Tanous4136ad72023-07-26 11:15:56 -0700622 json_object_put(md);
Jeremy Kerr13bb28f2018-08-09 10:41:10 +0800623}
624
Ed Tanous4136ad72023-07-26 11:15:56 -0700625static void config_free_one(struct config* config)
Jeremy Kerrf403c422018-07-26 12:14:56 +0800626{
Ed Tanous4136ad72023-07-26 11:15:56 -0700627 if (config->metadata)
628 json_object_put(config->metadata);
629 free(config->nbd_device);
630 free(config->name);
Jeremy Kerr19527352018-08-03 15:04:38 +0800631}
632
Ed Tanous4136ad72023-07-26 11:15:56 -0700633static int config_parse_one(struct config* config, const char* name,
634 json_object* obj)
Jeremy Kerr19527352018-08-03 15:04:38 +0800635{
Ed Tanous4136ad72023-07-26 11:15:56 -0700636 struct json_object *tmp, *meta;
637 json_bool jrc;
Jeremy Kerr19527352018-08-03 15:04:38 +0800638
Ed Tanous4136ad72023-07-26 11:15:56 -0700639 jrc = json_object_object_get_ex(obj, "nbd-device", &tmp);
640 if (!jrc)
641 {
642 warnx("config %s doesn't specify a nbd-device", name);
643 return -1;
644 }
Jeremy Kerr19527352018-08-03 15:04:38 +0800645
Ed Tanous4136ad72023-07-26 11:15:56 -0700646 if (!json_object_is_type(tmp, json_type_string))
647 {
648 warnx("config %s has invalid nbd-device", name);
649 return -1;
650 }
Jeremy Kerr19527352018-08-03 15:04:38 +0800651
Ed Tanous4136ad72023-07-26 11:15:56 -0700652 config->nbd_device = strdup(json_object_get_string(tmp));
653 config->name = strdup(name);
Jeremy Kerr19527352018-08-03 15:04:38 +0800654
Ed Tanous4136ad72023-07-26 11:15:56 -0700655 jrc = json_object_object_get_ex(obj, "default", &tmp);
656 config->is_default = jrc && json_object_get_boolean(tmp);
Jeremy Kerr0f1a49c2018-08-03 15:41:38 +0800657
Ed Tanous4136ad72023-07-26 11:15:56 -0700658 jrc = json_object_object_get_ex(obj, "metadata", &meta);
659 if (jrc && json_object_is_type(meta, json_type_object))
660 config->metadata = json_object_get(meta);
661 else
662 config->metadata = NULL;
Jeremy Kerr19527352018-08-03 15:04:38 +0800663
Ed Tanous4136ad72023-07-26 11:15:56 -0700664 return 0;
Jeremy Kerr19527352018-08-03 15:04:38 +0800665}
666
Ed Tanous4136ad72023-07-26 11:15:56 -0700667static void config_free(struct ctx* ctx)
Jeremy Kerr19527352018-08-03 15:04:38 +0800668{
Ed Tanous4136ad72023-07-26 11:15:56 -0700669 int i;
Jeremy Kerr19527352018-08-03 15:04:38 +0800670
Ed Tanous4136ad72023-07-26 11:15:56 -0700671 for (i = 0; i < ctx->n_configs; i++)
672 config_free_one(&ctx->configs[i]);
Jeremy Kerr19527352018-08-03 15:04:38 +0800673
Ed Tanous4136ad72023-07-26 11:15:56 -0700674 free(ctx->configs);
675 ctx->n_configs = 0;
Jeremy Kerr19527352018-08-03 15:04:38 +0800676}
677
Ed Tanous4136ad72023-07-26 11:15:56 -0700678static int config_init(struct ctx* ctx)
Jeremy Kerr19527352018-08-03 15:04:38 +0800679{
Ed Tanous4136ad72023-07-26 11:15:56 -0700680 struct json_object *obj, *tmp;
681 json_bool jrc;
682 int i, rc;
Jeremy Kerr19527352018-08-03 15:04:38 +0800683
Ed Tanous4136ad72023-07-26 11:15:56 -0700684 /* apply defaults */
685 ctx->nbd_timeout = nbd_timeout_default;
Jeremy Kerr19527352018-08-03 15:04:38 +0800686
Ed Tanous4136ad72023-07-26 11:15:56 -0700687 obj = json_object_from_file(conf_path);
688 if (!obj)
689 {
690 warnx("can't read configuration from %s\n", conf_path);
691 return -1;
692 }
Jeremy Kerr19527352018-08-03 15:04:38 +0800693
Ed Tanous4136ad72023-07-26 11:15:56 -0700694 /* global configuration */
695 jrc = json_object_object_get_ex(obj, "timeout", &tmp);
696 if (jrc)
697 {
698 errno = 0;
699 ctx->nbd_timeout = json_object_get_int(tmp);
700 if (ctx->nbd_timeout == 0 && errno)
701 {
702 warnx("can't parse timeout value");
703 goto err_free;
704 }
705 }
Jeremy Kerr19527352018-08-03 15:04:38 +0800706
Ed Tanous4136ad72023-07-26 11:15:56 -0700707 /* per-config configuration */
708 jrc = json_object_object_get_ex(obj, "configurations", &tmp);
709 if (!jrc)
710 {
711 warnx("no configurations specified");
712 goto err_free;
713 }
Jeremy Kerr19527352018-08-03 15:04:38 +0800714
Ed Tanous4136ad72023-07-26 11:15:56 -0700715 if (!json_object_is_type(tmp, json_type_object))
716 {
717 warnx("invalid configurations format");
718 goto err_free;
719 }
Jeremy Kerr19527352018-08-03 15:04:38 +0800720
Ed Tanous4136ad72023-07-26 11:15:56 -0700721 ctx->n_configs = json_object_object_length(tmp);
722 ctx->configs = calloc(ctx->n_configs, sizeof(*ctx->configs));
Jeremy Kerr19527352018-08-03 15:04:38 +0800723
Ed Tanous4136ad72023-07-26 11:15:56 -0700724 i = 0;
725 json_object_object_foreach(tmp, name, config_json)
726 {
727 struct config* config = &ctx->configs[i];
Jeremy Kerr0f1a49c2018-08-03 15:41:38 +0800728
Ed Tanous4136ad72023-07-26 11:15:56 -0700729 rc = config_parse_one(config, name, config_json);
730 if (rc)
731 goto err_free;
Jeremy Kerr0f1a49c2018-08-03 15:41:38 +0800732
Ed Tanous4136ad72023-07-26 11:15:56 -0700733 if (config->is_default)
734 {
735 if (ctx->default_config)
736 {
737 warn("multiple configs flagged as default");
738 goto err_free;
739 }
740 ctx->default_config = config;
741 }
742 i++;
743 }
Jeremy Kerr19527352018-08-03 15:04:38 +0800744
Ed Tanous4136ad72023-07-26 11:15:56 -0700745 json_object_put(obj);
Jeremy Kerr19527352018-08-03 15:04:38 +0800746
Ed Tanous4136ad72023-07-26 11:15:56 -0700747 if (ctx->n_configs == 1)
748 ctx->default_config = &ctx->configs[0];
Jeremy Kerr0f1a49c2018-08-03 15:41:38 +0800749
Ed Tanous4136ad72023-07-26 11:15:56 -0700750 return 0;
Jeremy Kerr19527352018-08-03 15:04:38 +0800751
752err_free:
Ed Tanous4136ad72023-07-26 11:15:56 -0700753 warnx("failed to load config from %s", conf_path);
754 json_object_put(obj);
755 return -1;
Jeremy Kerr19527352018-08-03 15:04:38 +0800756}
757
Ed Tanous4136ad72023-07-26 11:15:56 -0700758static int config_select(struct ctx* ctx, const char* name)
Jeremy Kerr19527352018-08-03 15:04:38 +0800759{
Ed Tanous4136ad72023-07-26 11:15:56 -0700760 struct config* config;
761 struct stat statbuf;
762 int i, rc;
Jeremy Kerr19527352018-08-03 15:04:38 +0800763
Ed Tanous4136ad72023-07-26 11:15:56 -0700764 config = NULL;
Jeremy Kerr19527352018-08-03 15:04:38 +0800765
Ed Tanous4136ad72023-07-26 11:15:56 -0700766 if (!name)
767 {
768 /* no config specified: use the default */
769 if (!ctx->default_config)
770 {
771 warnx("no config specified, and no default");
772 return -1;
773 }
774 config = ctx->default_config;
775 }
776 else
777 {
778 /* find a matching config... */
779 for (i = 0; i < ctx->n_configs; i++)
780 {
781 if (!strcmp(ctx->configs[i].name, name))
782 {
783 config = &ctx->configs[i];
784 break;
785 }
786 }
Jeremy Kerr19527352018-08-03 15:04:38 +0800787
Ed Tanous4136ad72023-07-26 11:15:56 -0700788 if (!config)
789 {
790 warnx("no such configuration '%s'", name);
791 return -1;
792 }
793 }
Jeremy Kerr0f1a49c2018-08-03 15:41:38 +0800794
Ed Tanous4136ad72023-07-26 11:15:56 -0700795 /* check that the device exists */
796 rc = stat(config->nbd_device, &statbuf);
797 if (rc)
798 {
799 warn("can't stat nbd device %s", config->nbd_device);
800 return -1;
801 }
Jeremy Kerr19527352018-08-03 15:04:38 +0800802
Ed Tanous4136ad72023-07-26 11:15:56 -0700803 if (!S_ISBLK(statbuf.st_mode))
804 {
805 warn("specified nbd path %s isn't a block device", config->nbd_device);
806 return -1;
807 }
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800808
Ed Tanous4136ad72023-07-26 11:15:56 -0700809 /* ... and apply it */
810 ctx->config = config;
811 ctx->nbd_devno = statbuf.st_rdev;
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800812
Ed Tanous4136ad72023-07-26 11:15:56 -0700813 return 0;
Jeremy Kerr19527352018-08-03 15:04:38 +0800814}
815
Jeremy Kerr6f9c4332018-08-03 17:11:15 +0800816static const struct option options[] = {
Ed Tanous4136ad72023-07-26 11:15:56 -0700817 {.name = "help", .val = 'h'},
818 {.name = "metadata", .val = 'm'},
819 {0},
Jeremy Kerr6f9c4332018-08-03 17:11:15 +0800820};
821
Ed Tanous4136ad72023-07-26 11:15:56 -0700822enum action
Jeremy Kerr6f9c4332018-08-03 17:11:15 +0800823{
Ed Tanous4136ad72023-07-26 11:15:56 -0700824 ACTION_PROXY,
825 ACTION_METADATA,
826};
827
828static void print_usage(const char* progname)
829{
830 fprintf(stderr, "usage:\n");
831 fprintf(stderr, "\t%s [configuration]\n", progname);
832 fprintf(stderr, "\t%s --metadata\n", progname);
Jeremy Kerr6f9c4332018-08-03 17:11:15 +0800833}
834
Ed Tanous4136ad72023-07-26 11:15:56 -0700835int main(int argc, char** argv)
Jeremy Kerr19527352018-08-03 15:04:38 +0800836{
Ed Tanous4136ad72023-07-26 11:15:56 -0700837 enum action action = ACTION_PROXY;
838 const char* config_name;
839 struct ctx _ctx, *ctx;
840 int rc;
Jeremy Kerrf403c422018-07-26 12:14:56 +0800841
Ed Tanous4136ad72023-07-26 11:15:56 -0700842 config_name = NULL;
Jeremy Kerr19527352018-08-03 15:04:38 +0800843
Ed Tanous4136ad72023-07-26 11:15:56 -0700844 for (;;)
845 {
846 int c = getopt_long(argc, argv, "h", options, NULL);
847 if (c == -1)
848 break;
Jeremy Kerr6f9c4332018-08-03 17:11:15 +0800849
Ed Tanous4136ad72023-07-26 11:15:56 -0700850 switch (c)
851 {
852 case 'm':
853 action = ACTION_METADATA;
854 break;
855 case 'h':
856 case '?':
857 print_usage(argv[0]);
858 return c == 'h' ? EXIT_SUCCESS : EXIT_FAILURE;
859 }
860 }
Jeremy Kerr6f9c4332018-08-03 17:11:15 +0800861
Ed Tanous4136ad72023-07-26 11:15:56 -0700862 if (optind < argc)
863 config_name = argv[optind];
Jeremy Kerr19527352018-08-03 15:04:38 +0800864
Ed Tanous4136ad72023-07-26 11:15:56 -0700865 ctx = &_ctx;
866 memset(ctx, 0, sizeof(*ctx));
867 ctx->bufsize = bufsize;
868 ctx->buf = malloc(ctx->bufsize);
Jeremy Kerr19527352018-08-03 15:04:38 +0800869
Ed Tanous4136ad72023-07-26 11:15:56 -0700870 rc = config_init(ctx);
871 if (rc)
872 goto out_free;
Jeremy Kerr19527352018-08-03 15:04:38 +0800873
Ed Tanous4136ad72023-07-26 11:15:56 -0700874 if (action == ACTION_METADATA)
875 {
876 print_metadata(ctx);
877 goto out_free;
878 }
Jeremy Kerr13bb28f2018-08-09 10:41:10 +0800879
Ed Tanous4136ad72023-07-26 11:15:56 -0700880 rc = config_select(ctx, config_name);
881 if (rc)
882 goto out_free;
Jeremy Kerrf403c422018-07-26 12:14:56 +0800883
Ed Tanous4136ad72023-07-26 11:15:56 -0700884 rc = open_nbd_socket(ctx);
885 if (rc)
886 goto out_free;
Jeremy Kerrf403c422018-07-26 12:14:56 +0800887
Ed Tanous4136ad72023-07-26 11:15:56 -0700888 rc = setup_signals(ctx);
889 if (rc)
890 goto out_close;
Jeremy Kerrf403c422018-07-26 12:14:56 +0800891
Ed Tanous4136ad72023-07-26 11:15:56 -0700892 rc = start_nbd_client(ctx);
893 if (rc)
894 goto out_stop_client;
Jeremy Kerrf403c422018-07-26 12:14:56 +0800895
Ed Tanous4136ad72023-07-26 11:15:56 -0700896 rc = wait_for_nbd_client(ctx);
897 if (rc)
898 goto out_stop_client;
Jeremy Kerrf403c422018-07-26 12:14:56 +0800899
Ed Tanous4136ad72023-07-26 11:15:56 -0700900 rc = udev_init(ctx);
901 if (rc)
902 goto out_stop_client;
Jeremy Kerrc6134c12018-08-09 13:03:33 +0800903
Ed Tanous4136ad72023-07-26 11:15:56 -0700904 rc = run_proxy(ctx);
Jeremy Kerrf403c422018-07-26 12:14:56 +0800905
Ed Tanous4136ad72023-07-26 11:15:56 -0700906 if (ctx->udev)
907 udev_free(ctx);
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800908
Ed Tanous4136ad72023-07-26 11:15:56 -0700909 run_state_hook(ctx, "stop", true);
Jeremy Kerrc6134c12018-08-09 13:03:33 +0800910
Jeremy Kerrf403c422018-07-26 12:14:56 +0800911out_stop_client:
Ed Tanous4136ad72023-07-26 11:15:56 -0700912 /* we cleanup signals before stopping the client, because we
913 * no longer care about SIGCHLD from the stopping nbd-client
914 * process. stop_nbd_client will be a no-op if the client hasn't
915 * been started. */
916 cleanup_signals(ctx);
Jeremy Kerrf403c422018-07-26 12:14:56 +0800917
Ed Tanous4136ad72023-07-26 11:15:56 -0700918 stop_nbd_client(ctx);
919 close(ctx->sock_client);
Jeremy Kerrf403c422018-07-26 12:14:56 +0800920
921out_close:
Ed Tanous4136ad72023-07-26 11:15:56 -0700922 if (ctx->sock_path)
923 {
924 unlink(ctx->sock_path);
925 free(ctx->sock_path);
926 }
927 close(ctx->sock);
Jeremy Kerrf403c422018-07-26 12:14:56 +0800928out_free:
Ed Tanous4136ad72023-07-26 11:15:56 -0700929 config_free(ctx);
930 free(ctx->buf);
931 return rc ? EXIT_FAILURE : EXIT_SUCCESS;
Jeremy Kerrf403c422018-07-26 12:14:56 +0800932}