blob: 54c8cf7aeec47470e98159674c37e59e9befb0e2 [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
Jeremy Kerrc6134c12018-08-09 13:03:33 +080020#include <dirent.h>
Jeremy Kerrf403c422018-07-26 12:14:56 +080021#include <err.h>
22#include <errno.h>
23#include <fcntl.h>
Jeremy Kerr6f9c4332018-08-03 17:11:15 +080024#include <getopt.h>
Jeremy Kerrc6134c12018-08-09 13:03:33 +080025#include <limits.h>
Jeremy Kerrf403c422018-07-26 12:14:56 +080026#include <signal.h>
27#include <stdbool.h>
28#include <stdint.h>
29#include <stdio.h>
30#include <stdlib.h>
31#include <string.h>
32#include <unistd.h>
33
34#include <sys/poll.h>
35#include <sys/socket.h>
36#include <sys/stat.h>
Jeremy Kerrc6134c12018-08-09 13:03:33 +080037#include <sys/types.h>
Jeremy Kerrf403c422018-07-26 12:14:56 +080038#include <sys/un.h>
39#include <sys/wait.h>
40
Jeremy Kerr19527352018-08-03 15:04:38 +080041#include <json.h>
Jeremy Kerr4ac92462018-08-10 11:16:29 +080042#include <libudev.h>
Jeremy Kerr19527352018-08-03 15:04:38 +080043
Jeremy Kerrf403c422018-07-26 12:14:56 +080044#include "config.h"
45
Jeremy Kerr19527352018-08-03 15:04:38 +080046struct config {
47 char *name;
Jeremy Kerr0f1a49c2018-08-03 15:41:38 +080048 bool is_default;
Jeremy Kerr19527352018-08-03 15:04:38 +080049 char *nbd_device;
50 struct json_object *metadata;
51};
52
Jeremy Kerrf403c422018-07-26 12:14:56 +080053struct ctx {
54 int sock;
55 int sock_client;
56 int signal_pipe[2];
57 char *sock_path;
58 pid_t nbd_client_pid;
Jeremy Kerrfa1d3752018-08-13 13:13:16 +080059 pid_t state_hook_pid;
Jeremy Kerr19527352018-08-03 15:04:38 +080060 int nbd_timeout;
Jeremy Kerr4ac92462018-08-10 11:16:29 +080061 dev_t nbd_devno;
Jeremy Kerrf403c422018-07-26 12:14:56 +080062 uint8_t *buf;
63 size_t bufsize;
Jeremy Kerr19527352018-08-03 15:04:38 +080064 struct config *configs;
65 int n_configs;
Jeremy Kerr0f1a49c2018-08-03 15:41:38 +080066 struct config *default_config;
Jeremy Kerrc6134c12018-08-09 13:03:33 +080067 struct config *config;
Jeremy Kerr4ac92462018-08-10 11:16:29 +080068 struct udev *udev;
69 struct udev_monitor *monitor;
Jeremy Kerrf403c422018-07-26 12:14:56 +080070};
71
Jeremy Kerr19527352018-08-03 15:04:38 +080072static const char *conf_path = SYSCONFDIR "/nbd-proxy/config.json";
Jeremy Kerra87af842018-08-13 11:48:23 +080073static const char *state_hook_path = SYSCONFDIR "/nbd-proxy/state";
Jeremy Kerrf403c422018-07-26 12:14:56 +080074static 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
79static int open_nbd_socket(struct ctx *ctx)
80{
81 struct sockaddr_un addr;
82 char *path;
83 int sd, rc;
84
85 rc = asprintf(&path, sockpath_tmpl, getpid());
86 if (rc < 0)
87 return -1;
88
Jeremy Kerr4eeb9ed2018-08-09 13:32:19 +080089 sd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
Jeremy Kerrf403c422018-07-26 12:14:56 +080090 if (sd < 0) {
91 warn("can't create socket");
92 goto err_free;
93 }
94
95 rc = fchmod(sd, S_IRUSR | S_IWUSR);
96 if (rc) {
97 warn("can't set permissions on socket");
98 goto err_close;
99 }
100
101 addr.sun_family = AF_UNIX;
Brad Bishopd5b98572018-10-29 08:44:25 -0400102 strncpy(addr.sun_path, path, sizeof(addr.sun_path) -1);
Jeremy Kerrf403c422018-07-26 12:14:56 +0800103
104 rc = bind(sd, (struct sockaddr *)&addr, sizeof(addr));
105 if (rc) {
106 warn("can't bind to path %s", path);
107 goto err_close;
108 }
109
110 rc = listen(sd, 1);
111 if (rc) {
112 warn("can't listen on socket %s", path);
113 goto err_unlink;
114 }
115
116 ctx->sock = sd;
117 ctx->sock_path = path;
118 return 0;
119
120err_unlink:
121 unlink(path);
122err_close:
123 close(sd);
124err_free:
125 free(path);
126 return -1;
127}
128
129static int start_nbd_client(struct ctx *ctx)
130{
131 pid_t pid;
132
133 pid = fork();
134 if (pid < 0) {
135 warn("can't create client process");
136 return -1;
137 }
138
139 /* child process: run nbd-client in non-fork mode */
140 if (pid == 0) {
Jeremy Kerr09295f22018-07-27 16:29:38 +0800141 char timeout_str[10];
Jeremy Kerrf403c422018-07-26 12:14:56 +0800142 int fd;
143
Jeremy Kerr19527352018-08-03 15:04:38 +0800144 snprintf(timeout_str, sizeof(timeout_str),
145 "%d", ctx->nbd_timeout);
Jeremy Kerr09295f22018-07-27 16:29:38 +0800146
Jeremy Kerr4eeb9ed2018-08-09 13:32:19 +0800147 fd = open("/dev/null", O_RDWR | O_CLOEXEC);
Jeremy Kerrf403c422018-07-26 12:14:56 +0800148 if (fd < 0)
149 err(EXIT_FAILURE, "can't open /dev/null");
150
151 dup2(fd, STDIN_FILENO);
152 dup2(fd, STDOUT_FILENO);
153 dup2(fd, STDERR_FILENO);
154 close(fd);
Jeremy Kerrf403c422018-07-26 12:14:56 +0800155
156 execlp("nbd-client", "nbd-client",
157 "-u", ctx->sock_path,
158 "-n",
Tang Yiweicfa074b2022-11-17 10:28:53 +0800159 "-L",
Jeremy Kerr09295f22018-07-27 16:29:38 +0800160 "-t", timeout_str,
Jeremy Kerrc6134c12018-08-09 13:03:33 +0800161 ctx->config->nbd_device,
Jeremy Kerrf403c422018-07-26 12:14:56 +0800162 NULL);
163 err(EXIT_FAILURE, "can't start ndb client");
164 }
165
166 ctx->nbd_client_pid = pid;
167 return 0;
168}
169
170static void stop_nbd_client(struct ctx *ctx)
171{
172 int rc;
173
174 if (!ctx->nbd_client_pid)
175 return;
176
177 rc = kill(ctx->nbd_client_pid, SIGTERM);
178 if (rc)
179 return;
180
181 waitpid(ctx->nbd_client_pid, NULL, 0);
182 ctx->nbd_client_pid = 0;
183}
184
185static int copy_fd(struct ctx *ctx, int fd_in, int fd_out)
186{
187#ifdef HAVE_SPLICE
188 int rc;
189
190 rc = splice(fd_in, NULL, fd_out, NULL, ctx->bufsize, 0);
191 if (rc < 0)
192 warn("splice");
193
194 return rc;
195#else
196 size_t len, pos;
197 ssize_t rc;
198
199 for (;;) {
200 errno = 0;
201 rc = read(fd_in, ctx->buf, ctx->bufsize);
202 if (rc < 0) {
203 if (errno == EINTR)
204 continue;
205 warn("read failure");
206 return -1;
207 }
208 if (rc == 0)
209 return 0;
210 break;
211 }
212
213 len = rc;
214
215 for (pos = 0; pos < len;) {
216 errno = 0;
217 rc = write(fd_out, ctx->buf + pos, len - pos);
218 if (rc < 0) {
219 if (errno == EINTR)
220 continue;
221 warn("write failure");
222 return -1;
223 }
224 if (rc == 0)
225 break;
226 pos += rc;
227 }
228
229 return pos;
230#endif
231}
232
233static int signal_pipe_fd = -1;
234
235static void signal_handler(int signal)
236{
237 int rc;
238
239 rc = write(signal_pipe_fd, &signal, sizeof(signal));
240
241 /* not a lot we can do here but exit... */
242 if (rc != sizeof(signal))
243 exit(EXIT_FAILURE);
244}
245
246static int setup_signals(struct ctx *ctx)
247{
248 struct sigaction sa;
249 int rc;
250
Jeremy Kerr4eeb9ed2018-08-09 13:32:19 +0800251 rc = pipe2(ctx->signal_pipe, O_CLOEXEC);
Jeremy Kerrf403c422018-07-26 12:14:56 +0800252 if (rc) {
253 warn("cant setup signal pipe");
254 return -1;
255 }
256
257 signal_pipe_fd = ctx->signal_pipe[1];
258
259 memset(&sa, 0, sizeof(sa));
260 sa.sa_handler = signal_handler;
261
262 sigaction(SIGINT, &sa, NULL);
263 sigaction(SIGTERM, &sa, NULL);
264 sigaction(SIGCHLD, &sa, NULL);
265
266 return 0;
267}
268
269static void cleanup_signals(struct ctx *ctx)
270{
271 struct sigaction sa;
272 memset(&sa, 0, sizeof(sa));
273 sa.sa_handler = SIG_DFL;
274
275 sigaction(SIGINT, &sa, NULL);
276 sigaction(SIGTERM, &sa, NULL);
277 sigaction(SIGCHLD, &sa, NULL);
278
279 close(ctx->signal_pipe[0]);
280 close(ctx->signal_pipe[1]);
281}
282
Jeremy Kerrfa1d3752018-08-13 13:13:16 +0800283static void process_sigchld(struct ctx *ctx, bool *exit)
284{
285 int status;
286 pid_t pid;
287
288 for (;;) {
289 pid = waitpid(-1, &status, WNOHANG);
290 if (pid == 0)
291 break;
292
293 if (pid == ctx->nbd_client_pid) {
294 warnx("nbd client stopped (%s: %d); exiting",
295 WIFEXITED(status) ? "rc" : "sig",
296 WIFEXITED(status) ?
297 WEXITSTATUS(status) :
298 WTERMSIG(status));
299 ctx->nbd_client_pid = 0;
300 *exit = true;
301
302 } else if (pid == ctx->state_hook_pid) {
303 if (!WIFEXITED(status) || WEXITSTATUS(status)) {
304 warnx("state hook failed (%s: %d); exiting",
305 WIFEXITED(status) ? "rc" : "sig",
306 WIFEXITED(status) ?
307 WEXITSTATUS(status) :
308 WTERMSIG(status));
309 *exit = true;
310 }
311 ctx->state_hook_pid = 0;
312 }
313 }
314}
315
Jeremy Kerrf403c422018-07-26 12:14:56 +0800316static int process_signal_pipe(struct ctx *ctx, bool *exit)
317{
Jeremy Kerrfa1d3752018-08-13 13:13:16 +0800318 int buf, rc;
Jeremy Kerrf403c422018-07-26 12:14:56 +0800319
320 rc = read(ctx->signal_pipe[0], &buf, sizeof(buf));
321 if (rc != sizeof(buf))
322 return -1;
323
324 *exit = false;
325
326 switch (buf) {
327 case SIGCHLD:
Jeremy Kerrfa1d3752018-08-13 13:13:16 +0800328 process_sigchld(ctx, exit);
Jeremy Kerrf403c422018-07-26 12:14:56 +0800329 break;
330 case SIGINT:
331 case SIGTERM:
332 *exit = true;
333 break;
334 }
335
336 return 0;
337}
338
339static int wait_for_nbd_client(struct ctx *ctx)
340{
341 struct pollfd pollfds[2];
342 int rc;
343
344 pollfds[0].fd = ctx->sock;
345 pollfds[0].events = POLLIN;
346 pollfds[1].fd = ctx->signal_pipe[0];
347 pollfds[1].events = POLLIN;
348
349 for (;;) {
350 errno = 0;
351 rc = poll(pollfds, 2, -1);
352 if (rc < 0) {
353 if (errno == EINTR)
354 continue;
355 warn("poll failed");
356 return -1;
357 }
358
359 if (pollfds[0].revents) {
Jeremy Kerr4eeb9ed2018-08-09 13:32:19 +0800360 rc = accept4(ctx->sock, NULL, NULL, SOCK_CLOEXEC);
Jeremy Kerrf403c422018-07-26 12:14:56 +0800361 if (rc < 0) {
362 warn("can't create connection");
363 return -1;
364 }
365 ctx->sock_client = rc;
366 break;
367 }
368
369 if (pollfds[1].revents) {
370 bool exit;
371 rc = process_signal_pipe(ctx, &exit);
372 if (rc || exit)
373 return -1;
374 }
375 }
376
377 return 0;
378}
379
Jeremy Kerrfa1d3752018-08-13 13:13:16 +0800380static int run_state_hook(struct ctx *ctx, const char *action, bool wait)
Jeremy Kerrc6134c12018-08-09 13:03:33 +0800381{
382 int status, rc, fd;
383 pid_t pid;
384
Jeremy Kerra87af842018-08-13 11:48:23 +0800385 /* if the hook isn't present or executable, that's not necessarily
386 * an error condition */
Jeremy Kerrfa1d3752018-08-13 13:13:16 +0800387 if (access(state_hook_path, X_OK))
Jeremy Kerra87af842018-08-13 11:48:23 +0800388 return 0;
389
Jeremy Kerrc6134c12018-08-09 13:03:33 +0800390 pid = fork();
391 if (pid < 0) {
Jeremy Kerra87af842018-08-13 11:48:23 +0800392 warn("can't fork to execute hook %s", state_hook_path);
Jeremy Kerrc6134c12018-08-09 13:03:33 +0800393 return -1;
394 }
395
396 if (!pid) {
Jeremy Kerra87af842018-08-13 11:48:23 +0800397 const char *argv0;
398
399 argv0 = strchr(state_hook_path, '/');
400 if (!argv0)
401 argv0 = state_hook_path;
402
Jeremy Kerr4eeb9ed2018-08-09 13:32:19 +0800403 fd = open("/dev/null", O_RDWR | O_CLOEXEC);
Jeremy Kerrc6134c12018-08-09 13:03:33 +0800404 if (fd < 0)
405 exit(EXIT_FAILURE);
406
407 dup2(fd, STDIN_FILENO);
408 dup2(fd, STDOUT_FILENO);
409 dup2(fd, STDERR_FILENO);
Jeremy Kerra87af842018-08-13 11:48:23 +0800410 execl(state_hook_path, argv0, action, ctx->config->name, NULL);
Jeremy Kerrc6134c12018-08-09 13:03:33 +0800411 exit(EXIT_FAILURE);
412 }
413
Jeremy Kerrfa1d3752018-08-13 13:13:16 +0800414 if (!wait) {
415 ctx->state_hook_pid = pid;
416 return 0;
417 }
418
Jeremy Kerrc6134c12018-08-09 13:03:33 +0800419 rc = waitpid(pid, &status, 0);
420 if (rc < 0) {
421 warn("wait");
422 return -1;
423 }
424
425 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
Jeremy Kerra87af842018-08-13 11:48:23 +0800426 warnx("hook %s failed", state_hook_path);
Jeremy Kerrc6134c12018-08-09 13:03:33 +0800427 return -1;
428 }
429
430 return 0;
431}
432
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800433static int udev_init(struct ctx *ctx)
434{
435 int rc;
436
437 ctx->udev = udev_new();
438 if (!ctx) {
439 warn("can't create udev object");
440 return -1;
441 }
442
443 ctx->monitor = udev_monitor_new_from_netlink(ctx->udev, "kernel");
444 if (!ctx->monitor) {
445 warn("can't create udev monitor");
446 goto out_unref_udev;
447 }
448
449 rc = udev_monitor_filter_add_match_subsystem_devtype(
450 ctx->monitor, "block", "disk");
451 if (rc) {
452 warn("can't create udev monitor filter");
453 goto out_unref_monitor;
454 }
455
456 rc = udev_monitor_enable_receiving(ctx->monitor);
457 if (rc) {
458 warn("can't start udev monitor");
459 goto out_unref_monitor;
460 }
461
462 return 0;
463
464out_unref_monitor:
465 udev_monitor_unref(ctx->monitor);
466out_unref_udev:
467 udev_unref(ctx->udev);
468 return -1;
469}
470
471static void udev_free(struct ctx *ctx)
472{
473 udev_monitor_unref(ctx->monitor);
474 udev_unref(ctx->udev);
475}
476
477/* Check for the change event on our nbd device, signifying that the kernel
478 * has finished initialising the block device. Once we see the event, we run
479 * the "start" state hook, and close the udev monitor.
480 *
481 * Returns:
482 * 0 if no processing was performed
483 * -1 on state hook error (and the nbd session should be closed)
484 */
485static int udev_process(struct ctx *ctx)
486{
487 struct udev_device *dev;
488 bool action_is_change;
489 dev_t devno;
490 int rc;
491
492 dev = udev_monitor_receive_device(ctx->monitor);
493 if (!dev)
494 return 0;
495
496 devno = udev_device_get_devnum(dev);
497 action_is_change = !strcmp(udev_device_get_action(dev), "change");
498 udev_device_unref(dev);
499
500 if (devno != ctx->nbd_devno)
501 return 0;
502
503 if (!action_is_change)
504 return 0;
505
506 udev_monitor_unref(ctx->monitor);
507 udev_unref(ctx->udev);
508 ctx->monitor = NULL;
509 ctx->udev = NULL;
510
Jeremy Kerrfa1d3752018-08-13 13:13:16 +0800511 rc = run_state_hook(ctx, "start", false);
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800512
513 return rc;
514}
515
Jeremy Kerrf403c422018-07-26 12:14:56 +0800516static int run_proxy(struct ctx *ctx)
517{
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800518 struct pollfd pollfds[4];
Jeremy Kerrf403c422018-07-26 12:14:56 +0800519 bool exit = false;
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800520 int rc, n_fd;
Jeremy Kerrf403c422018-07-26 12:14:56 +0800521
522 /* main proxy: forward data between stdio & socket */
523 pollfds[0].fd = ctx->sock_client;
524 pollfds[0].events = POLLIN;
525 pollfds[1].fd = STDIN_FILENO;
526 pollfds[1].events = POLLIN;
527 pollfds[2].fd = ctx->signal_pipe[0];
528 pollfds[2].events = POLLIN;
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800529 pollfds[3].fd = udev_monitor_get_fd(ctx->monitor);
530 pollfds[3].events = POLLIN;
531
532 n_fd = 4;
Jeremy Kerrf403c422018-07-26 12:14:56 +0800533
534 for (;;) {
535 errno = 0;
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800536 rc = poll(pollfds, n_fd, -1);
Jeremy Kerrf403c422018-07-26 12:14:56 +0800537 if (rc < 0) {
538 if (errno == EINTR)
539 continue;
540 warn("poll failed");
541 break;
542 }
543
544 if (pollfds[0].revents) {
545 rc = copy_fd(ctx, ctx->sock_client, STDOUT_FILENO);
546 if (rc <= 0)
547 break;
548 }
549
550 if (pollfds[1].revents) {
551 rc = copy_fd(ctx, STDIN_FILENO, ctx->sock_client);
552 if (rc <= 0)
553 break;
554 }
555
556 if (pollfds[2].revents) {
557 rc = process_signal_pipe(ctx, &exit);
558 if (rc || exit)
559 break;
560 }
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800561
562 if (pollfds[3].revents) {
563 rc = udev_process(ctx);
564 if (rc)
565 break;
566
567 /* udev_process may have closed the udev connection,
568 * in which case we can stop polling on its fd */
569 if (!ctx->udev) {
570 pollfds[3].fd = 0;
571 pollfds[3].revents = 0;
572 n_fd = 3;
573 }
574 }
Jeremy Kerrf403c422018-07-26 12:14:56 +0800575 }
576
577 return rc ? -1 : 0;
578}
579
Jeremy Kerr13bb28f2018-08-09 10:41:10 +0800580static void print_metadata(struct ctx *ctx)
581{
582 struct json_object *md;
583 int i;
584
585 md = json_object_new_object();
586
587 for (i = 0; i < ctx->n_configs; i++) {
588 struct config *config = &ctx->configs[i];
589 json_object_object_add(md, config->name,
Adriana Kobylak74c8a722019-01-10 10:35:44 -0600590 json_object_get(config->metadata));
Jeremy Kerr13bb28f2018-08-09 10:41:10 +0800591 }
592
593 puts(json_object_get_string(md));
594
595 json_object_put(md);
596}
597
Jeremy Kerr19527352018-08-03 15:04:38 +0800598static void config_free_one(struct config *config)
Jeremy Kerrf403c422018-07-26 12:14:56 +0800599{
Jeremy Kerr19527352018-08-03 15:04:38 +0800600 if (config->metadata)
601 json_object_put(config->metadata);
602 free(config->nbd_device);
603 free(config->name);
604}
605
606static int config_parse_one(struct config *config, const char *name,
607 json_object *obj)
608{
609 struct json_object *tmp, *meta;
610 json_bool jrc;
611
612 jrc = json_object_object_get_ex(obj, "nbd-device", &tmp);
613 if (!jrc) {
614 warnx("config %s doesn't specify a nbd-device", name);
615 return -1;
616 }
617
618 if (!json_object_is_type(tmp, json_type_string)) {
619 warnx("config %s has invalid nbd-device", name);
620 return -1;
621 }
622
623 config->nbd_device = strdup(json_object_get_string(tmp));
624 config->name = strdup(name);
625
Jeremy Kerr0f1a49c2018-08-03 15:41:38 +0800626 jrc = json_object_object_get_ex(obj, "default", &tmp);
627 config->is_default = jrc && json_object_get_boolean(tmp);
628
Jeremy Kerr19527352018-08-03 15:04:38 +0800629 jrc = json_object_object_get_ex(obj, "metadata", &meta);
630 if (jrc && json_object_is_type(meta, json_type_object))
631 config->metadata = json_object_get(meta);
632 else
633 config->metadata = NULL;
634
635 return 0;
636}
637
638static void config_free(struct ctx *ctx)
639{
640 int i;
641
642 for (i = 0; i < ctx->n_configs; i++)
643 config_free_one(&ctx->configs[i]);
644
645 free(ctx->configs);
646 ctx->n_configs = 0;
647}
648
649static int config_init(struct ctx *ctx)
650{
651 struct json_object *obj, *tmp;
652 json_bool jrc;
653 int i, rc;
654
655 /* apply defaults */
656 ctx->nbd_timeout = nbd_timeout_default;
657
658 obj = json_object_from_file(conf_path);
659 if (!obj) {
660 warnx("can't read configuration from %s\n", conf_path);
661 return -1;
662 }
663
664 /* global configuration */
665 jrc = json_object_object_get_ex(obj, "timeout", &tmp);
666 if (jrc) {
667 errno = 0;
668 ctx->nbd_timeout = json_object_get_int(tmp);
669 if (ctx->nbd_timeout == 0 && errno) {
670 warnx("can't parse timeout value");
671 goto err_free;
672 }
673 }
674
675 /* per-config configuration */
676 jrc = json_object_object_get_ex(obj, "configurations", &tmp);
677 if (!jrc) {
678 warnx("no configurations specified");
679 goto err_free;
680 }
681
682 if (!json_object_is_type(tmp, json_type_object)) {
683 warnx("invalid configurations format");
684 goto err_free;
685 }
686
687 ctx->n_configs = json_object_object_length(tmp);
688 ctx->configs = calloc(ctx->n_configs, sizeof(*ctx->configs));
689
690 i = 0;
Jeremy Kerr0f1a49c2018-08-03 15:41:38 +0800691 json_object_object_foreach(tmp, name, config_json) {
692 struct config *config = &ctx->configs[i];
693
694 rc = config_parse_one(config, name, config_json);
Jeremy Kerr19527352018-08-03 15:04:38 +0800695 if (rc)
696 goto err_free;
Jeremy Kerr0f1a49c2018-08-03 15:41:38 +0800697
698 if (config->is_default) {
699 if (ctx->default_config) {
700 warn("multiple configs flagged as default");
701 goto err_free;
702 }
703 ctx->default_config = config;
704 }
Jeremy Kerr19527352018-08-03 15:04:38 +0800705 i++;
706 }
707
708 json_object_put(obj);
709
Jeremy Kerr0f1a49c2018-08-03 15:41:38 +0800710 if (ctx->n_configs == 1)
711 ctx->default_config = &ctx->configs[0];
712
Jeremy Kerr19527352018-08-03 15:04:38 +0800713 return 0;
714
715err_free:
716 warnx("failed to load config from %s", conf_path);
717 json_object_put(obj);
718 return -1;
719}
720
721static int config_select(struct ctx *ctx, const char *name)
722{
723 struct config *config;
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800724 struct stat statbuf;
725 int i, rc;
Jeremy Kerr19527352018-08-03 15:04:38 +0800726
727 config = NULL;
728
Jeremy Kerr0f1a49c2018-08-03 15:41:38 +0800729 if (!name) {
730 /* no config specified: use the default */
731 if (!ctx->default_config) {
732 warnx("no config specified, and no default");
733 return -1;
Jeremy Kerr19527352018-08-03 15:04:38 +0800734 }
Jeremy Kerr0f1a49c2018-08-03 15:41:38 +0800735 config = ctx->default_config;
Jeremy Kerr19527352018-08-03 15:04:38 +0800736
Jeremy Kerr0f1a49c2018-08-03 15:41:38 +0800737 } else {
738 /* find a matching config... */
739 for (i = 0; i < ctx->n_configs; i++) {
740 if (!strcmp(ctx->configs[i].name, name)) {
741 config = &ctx->configs[i];
742 break;
743 }
744 }
745
746 if (!config) {
747 warnx("no such configuration '%s'", name);
748 return -1;
749 }
Jeremy Kerr19527352018-08-03 15:04:38 +0800750 }
751
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800752 /* check that the device exists */
753 rc = stat(config->nbd_device, &statbuf);
754 if (rc) {
755 warn("can't stat nbd device %s", config->nbd_device);
756 return -1;
757 }
758
759 if (!S_ISBLK(statbuf.st_mode)) {
760 warn("specified nbd path %s isn't a block device",
761 config->nbd_device);
762 return -1;
763 }
764
Jeremy Kerr19527352018-08-03 15:04:38 +0800765 /* ... and apply it */
Jeremy Kerrc6134c12018-08-09 13:03:33 +0800766 ctx->config = config;
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800767 ctx->nbd_devno = statbuf.st_rdev;
768
Jeremy Kerr19527352018-08-03 15:04:38 +0800769 return 0;
770}
771
Jeremy Kerr6f9c4332018-08-03 17:11:15 +0800772static const struct option options[] = {
Jeremy Kerr13bb28f2018-08-09 10:41:10 +0800773 { .name = "help", .val = 'h' },
774 { .name = "metadata", .val = 'm' },
Jeremy Kerr6f9c4332018-08-03 17:11:15 +0800775 { 0 },
776};
777
Jeremy Kerr13bb28f2018-08-09 10:41:10 +0800778enum action {
779 ACTION_PROXY,
780 ACTION_METADATA,
781};
782
Jeremy Kerr6f9c4332018-08-03 17:11:15 +0800783static void print_usage(const char *progname)
784{
Jeremy Kerr13bb28f2018-08-09 10:41:10 +0800785 fprintf(stderr, "usage:\n");
786 fprintf(stderr, "\t%s [configuration]\n", progname);
787 fprintf(stderr, "\t%s --metadata\n", progname);
Jeremy Kerr6f9c4332018-08-03 17:11:15 +0800788}
789
Jeremy Kerr19527352018-08-03 15:04:38 +0800790int main(int argc, char **argv)
791{
Jeremy Kerr13bb28f2018-08-09 10:41:10 +0800792 enum action action = ACTION_PROXY;
Jeremy Kerr19527352018-08-03 15:04:38 +0800793 const char *config_name;
Jeremy Kerrf403c422018-07-26 12:14:56 +0800794 struct ctx _ctx, *ctx;
795 int rc;
796
Jeremy Kerr0f1a49c2018-08-03 15:41:38 +0800797 config_name = NULL;
Jeremy Kerr19527352018-08-03 15:04:38 +0800798
Jeremy Kerr6f9c4332018-08-03 17:11:15 +0800799 for (;;) {
800 int c = getopt_long(argc, argv, "h", options, NULL);
801 if (c == -1)
802 break;
803
804 switch (c) {
Jeremy Kerr13bb28f2018-08-09 10:41:10 +0800805 case 'm':
806 action = ACTION_METADATA;
807 break;
Jeremy Kerr6f9c4332018-08-03 17:11:15 +0800808 case 'h':
809 case '?':
810 print_usage(argv[0]);
811 return c == 'h' ? EXIT_SUCCESS : EXIT_FAILURE;
812 }
813 }
814
815 if (optind < argc)
816 config_name = argv[optind];
Jeremy Kerr19527352018-08-03 15:04:38 +0800817
Jeremy Kerrf403c422018-07-26 12:14:56 +0800818 ctx = &_ctx;
Jeremy Kerr19527352018-08-03 15:04:38 +0800819 memset(ctx, 0, sizeof(*ctx));
Jeremy Kerrf403c422018-07-26 12:14:56 +0800820 ctx->bufsize = bufsize;
821 ctx->buf = malloc(ctx->bufsize);
Jeremy Kerr19527352018-08-03 15:04:38 +0800822
823 rc = config_init(ctx);
824 if (rc)
825 goto out_free;
826
Jeremy Kerr13bb28f2018-08-09 10:41:10 +0800827 if (action == ACTION_METADATA) {
828 print_metadata(ctx);
829 goto out_free;
830 }
831
Jeremy Kerr19527352018-08-03 15:04:38 +0800832 rc = config_select(ctx, config_name);
833 if (rc)
834 goto out_free;
Jeremy Kerrf403c422018-07-26 12:14:56 +0800835
836 rc = open_nbd_socket(ctx);
837 if (rc)
838 goto out_free;
839
840 rc = setup_signals(ctx);
841 if (rc)
842 goto out_close;
843
844 rc = start_nbd_client(ctx);
845 if (rc)
846 goto out_stop_client;
847
848 rc = wait_for_nbd_client(ctx);
849 if (rc)
850 goto out_stop_client;
851
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800852 rc = udev_init(ctx);
Jeremy Kerrc6134c12018-08-09 13:03:33 +0800853 if (rc)
854 goto out_stop_client;
855
Jeremy Kerrf403c422018-07-26 12:14:56 +0800856 rc = run_proxy(ctx);
857
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800858 if (ctx->udev)
859 udev_free(ctx);
860
Jeremy Kerrfa1d3752018-08-13 13:13:16 +0800861 run_state_hook(ctx, "stop", true);
Jeremy Kerrc6134c12018-08-09 13:03:33 +0800862
Jeremy Kerrf403c422018-07-26 12:14:56 +0800863out_stop_client:
864 /* we cleanup signals before stopping the client, because we
865 * no longer care about SIGCHLD from the stopping nbd-client
866 * process. stop_nbd_client will be a no-op if the client hasn't
867 * been started. */
868 cleanup_signals(ctx);
869
870 stop_nbd_client(ctx);
871 close(ctx->sock_client);
872
873out_close:
874 if (ctx->sock_path) {
875 unlink(ctx->sock_path);
876 free(ctx->sock_path);
877 }
878 close(ctx->sock);
879out_free:
Jeremy Kerr19527352018-08-03 15:04:38 +0800880 config_free(ctx);
Jeremy Kerrf403c422018-07-26 12:14:56 +0800881 free(ctx->buf);
882 return rc ? EXIT_FAILURE : EXIT_SUCCESS;
883}