blob: f98feecd31f3487ba4f900f5b2ea5187818c8689 [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;
102 strncpy(addr.sun_path, path, sizeof(addr.sun_path));
103
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",
Jeremy Kerr09295f22018-07-27 16:29:38 +0800159 "-t", timeout_str,
Jeremy Kerrc6134c12018-08-09 13:03:33 +0800160 ctx->config->nbd_device,
Jeremy Kerrf403c422018-07-26 12:14:56 +0800161 NULL);
162 err(EXIT_FAILURE, "can't start ndb client");
163 }
164
165 ctx->nbd_client_pid = pid;
166 return 0;
167}
168
169static void stop_nbd_client(struct ctx *ctx)
170{
171 int rc;
172
173 if (!ctx->nbd_client_pid)
174 return;
175
176 rc = kill(ctx->nbd_client_pid, SIGTERM);
177 if (rc)
178 return;
179
180 waitpid(ctx->nbd_client_pid, NULL, 0);
181 ctx->nbd_client_pid = 0;
182}
183
184static int copy_fd(struct ctx *ctx, int fd_in, int fd_out)
185{
186#ifdef HAVE_SPLICE
187 int rc;
188
189 rc = splice(fd_in, NULL, fd_out, NULL, ctx->bufsize, 0);
190 if (rc < 0)
191 warn("splice");
192
193 return rc;
194#else
195 size_t len, pos;
196 ssize_t rc;
197
198 for (;;) {
199 errno = 0;
200 rc = read(fd_in, ctx->buf, ctx->bufsize);
201 if (rc < 0) {
202 if (errno == EINTR)
203 continue;
204 warn("read failure");
205 return -1;
206 }
207 if (rc == 0)
208 return 0;
209 break;
210 }
211
212 len = rc;
213
214 for (pos = 0; pos < len;) {
215 errno = 0;
216 rc = write(fd_out, ctx->buf + pos, len - pos);
217 if (rc < 0) {
218 if (errno == EINTR)
219 continue;
220 warn("write failure");
221 return -1;
222 }
223 if (rc == 0)
224 break;
225 pos += rc;
226 }
227
228 return pos;
229#endif
230}
231
232static int signal_pipe_fd = -1;
233
234static void signal_handler(int signal)
235{
236 int rc;
237
238 rc = write(signal_pipe_fd, &signal, sizeof(signal));
239
240 /* not a lot we can do here but exit... */
241 if (rc != sizeof(signal))
242 exit(EXIT_FAILURE);
243}
244
245static int setup_signals(struct ctx *ctx)
246{
247 struct sigaction sa;
248 int rc;
249
Jeremy Kerr4eeb9ed2018-08-09 13:32:19 +0800250 rc = pipe2(ctx->signal_pipe, O_CLOEXEC);
Jeremy Kerrf403c422018-07-26 12:14:56 +0800251 if (rc) {
252 warn("cant setup signal pipe");
253 return -1;
254 }
255
256 signal_pipe_fd = ctx->signal_pipe[1];
257
258 memset(&sa, 0, sizeof(sa));
259 sa.sa_handler = signal_handler;
260
261 sigaction(SIGINT, &sa, NULL);
262 sigaction(SIGTERM, &sa, NULL);
263 sigaction(SIGCHLD, &sa, NULL);
264
265 return 0;
266}
267
268static void cleanup_signals(struct ctx *ctx)
269{
270 struct sigaction sa;
271 memset(&sa, 0, sizeof(sa));
272 sa.sa_handler = SIG_DFL;
273
274 sigaction(SIGINT, &sa, NULL);
275 sigaction(SIGTERM, &sa, NULL);
276 sigaction(SIGCHLD, &sa, NULL);
277
278 close(ctx->signal_pipe[0]);
279 close(ctx->signal_pipe[1]);
280}
281
Jeremy Kerrfa1d3752018-08-13 13:13:16 +0800282static void process_sigchld(struct ctx *ctx, bool *exit)
283{
284 int status;
285 pid_t pid;
286
287 for (;;) {
288 pid = waitpid(-1, &status, WNOHANG);
289 if (pid == 0)
290 break;
291
292 if (pid == ctx->nbd_client_pid) {
293 warnx("nbd client stopped (%s: %d); exiting",
294 WIFEXITED(status) ? "rc" : "sig",
295 WIFEXITED(status) ?
296 WEXITSTATUS(status) :
297 WTERMSIG(status));
298 ctx->nbd_client_pid = 0;
299 *exit = true;
300
301 } else if (pid == ctx->state_hook_pid) {
302 if (!WIFEXITED(status) || WEXITSTATUS(status)) {
303 warnx("state hook failed (%s: %d); exiting",
304 WIFEXITED(status) ? "rc" : "sig",
305 WIFEXITED(status) ?
306 WEXITSTATUS(status) :
307 WTERMSIG(status));
308 *exit = true;
309 }
310 ctx->state_hook_pid = 0;
311 }
312 }
313}
314
Jeremy Kerrf403c422018-07-26 12:14:56 +0800315static int process_signal_pipe(struct ctx *ctx, bool *exit)
316{
Jeremy Kerrfa1d3752018-08-13 13:13:16 +0800317 int buf, rc;
Jeremy Kerrf403c422018-07-26 12:14:56 +0800318
319 rc = read(ctx->signal_pipe[0], &buf, sizeof(buf));
320 if (rc != sizeof(buf))
321 return -1;
322
323 *exit = false;
324
325 switch (buf) {
326 case SIGCHLD:
Jeremy Kerrfa1d3752018-08-13 13:13:16 +0800327 process_sigchld(ctx, exit);
Jeremy Kerrf403c422018-07-26 12:14:56 +0800328 break;
329 case SIGINT:
330 case SIGTERM:
331 *exit = true;
332 break;
333 }
334
335 return 0;
336}
337
338static int wait_for_nbd_client(struct ctx *ctx)
339{
340 struct pollfd pollfds[2];
341 int rc;
342
343 pollfds[0].fd = ctx->sock;
344 pollfds[0].events = POLLIN;
345 pollfds[1].fd = ctx->signal_pipe[0];
346 pollfds[1].events = POLLIN;
347
348 for (;;) {
349 errno = 0;
350 rc = poll(pollfds, 2, -1);
351 if (rc < 0) {
352 if (errno == EINTR)
353 continue;
354 warn("poll failed");
355 return -1;
356 }
357
358 if (pollfds[0].revents) {
Jeremy Kerr4eeb9ed2018-08-09 13:32:19 +0800359 rc = accept4(ctx->sock, NULL, NULL, SOCK_CLOEXEC);
Jeremy Kerrf403c422018-07-26 12:14:56 +0800360 if (rc < 0) {
361 warn("can't create connection");
362 return -1;
363 }
364 ctx->sock_client = rc;
365 break;
366 }
367
368 if (pollfds[1].revents) {
369 bool exit;
370 rc = process_signal_pipe(ctx, &exit);
371 if (rc || exit)
372 return -1;
373 }
374 }
375
376 return 0;
377}
378
Jeremy Kerrfa1d3752018-08-13 13:13:16 +0800379static int run_state_hook(struct ctx *ctx, const char *action, bool wait)
Jeremy Kerrc6134c12018-08-09 13:03:33 +0800380{
381 int status, rc, fd;
382 pid_t pid;
383
Jeremy Kerra87af842018-08-13 11:48:23 +0800384 /* if the hook isn't present or executable, that's not necessarily
385 * an error condition */
Jeremy Kerrfa1d3752018-08-13 13:13:16 +0800386 if (access(state_hook_path, X_OK))
Jeremy Kerra87af842018-08-13 11:48:23 +0800387 return 0;
388
Jeremy Kerrc6134c12018-08-09 13:03:33 +0800389 pid = fork();
390 if (pid < 0) {
Jeremy Kerra87af842018-08-13 11:48:23 +0800391 warn("can't fork to execute hook %s", state_hook_path);
Jeremy Kerrc6134c12018-08-09 13:03:33 +0800392 return -1;
393 }
394
395 if (!pid) {
Jeremy Kerra87af842018-08-13 11:48:23 +0800396 const char *argv0;
397
398 argv0 = strchr(state_hook_path, '/');
399 if (!argv0)
400 argv0 = state_hook_path;
401
Jeremy Kerr4eeb9ed2018-08-09 13:32:19 +0800402 fd = open("/dev/null", O_RDWR | O_CLOEXEC);
Jeremy Kerrc6134c12018-08-09 13:03:33 +0800403 if (fd < 0)
404 exit(EXIT_FAILURE);
405
406 dup2(fd, STDIN_FILENO);
407 dup2(fd, STDOUT_FILENO);
408 dup2(fd, STDERR_FILENO);
Jeremy Kerra87af842018-08-13 11:48:23 +0800409 execl(state_hook_path, argv0, action, ctx->config->name, NULL);
Jeremy Kerrc6134c12018-08-09 13:03:33 +0800410 exit(EXIT_FAILURE);
411 }
412
Jeremy Kerrfa1d3752018-08-13 13:13:16 +0800413 if (!wait) {
414 ctx->state_hook_pid = pid;
415 return 0;
416 }
417
Jeremy Kerrc6134c12018-08-09 13:03:33 +0800418 rc = waitpid(pid, &status, 0);
419 if (rc < 0) {
420 warn("wait");
421 return -1;
422 }
423
424 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
Jeremy Kerra87af842018-08-13 11:48:23 +0800425 warnx("hook %s failed", state_hook_path);
Jeremy Kerrc6134c12018-08-09 13:03:33 +0800426 return -1;
427 }
428
429 return 0;
430}
431
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800432static int udev_init(struct ctx *ctx)
433{
434 int rc;
435
436 ctx->udev = udev_new();
437 if (!ctx) {
438 warn("can't create udev object");
439 return -1;
440 }
441
442 ctx->monitor = udev_monitor_new_from_netlink(ctx->udev, "kernel");
443 if (!ctx->monitor) {
444 warn("can't create udev monitor");
445 goto out_unref_udev;
446 }
447
448 rc = udev_monitor_filter_add_match_subsystem_devtype(
449 ctx->monitor, "block", "disk");
450 if (rc) {
451 warn("can't create udev monitor filter");
452 goto out_unref_monitor;
453 }
454
455 rc = udev_monitor_enable_receiving(ctx->monitor);
456 if (rc) {
457 warn("can't start udev monitor");
458 goto out_unref_monitor;
459 }
460
461 return 0;
462
463out_unref_monitor:
464 udev_monitor_unref(ctx->monitor);
465out_unref_udev:
466 udev_unref(ctx->udev);
467 return -1;
468}
469
470static void udev_free(struct ctx *ctx)
471{
472 udev_monitor_unref(ctx->monitor);
473 udev_unref(ctx->udev);
474}
475
476/* Check for the change event on our nbd device, signifying that the kernel
477 * has finished initialising the block device. Once we see the event, we run
478 * the "start" state hook, and close the udev monitor.
479 *
480 * Returns:
481 * 0 if no processing was performed
482 * -1 on state hook error (and the nbd session should be closed)
483 */
484static int udev_process(struct ctx *ctx)
485{
486 struct udev_device *dev;
487 bool action_is_change;
488 dev_t devno;
489 int rc;
490
491 dev = udev_monitor_receive_device(ctx->monitor);
492 if (!dev)
493 return 0;
494
495 devno = udev_device_get_devnum(dev);
496 action_is_change = !strcmp(udev_device_get_action(dev), "change");
497 udev_device_unref(dev);
498
499 if (devno != ctx->nbd_devno)
500 return 0;
501
502 if (!action_is_change)
503 return 0;
504
505 udev_monitor_unref(ctx->monitor);
506 udev_unref(ctx->udev);
507 ctx->monitor = NULL;
508 ctx->udev = NULL;
509
Jeremy Kerrfa1d3752018-08-13 13:13:16 +0800510 rc = run_state_hook(ctx, "start", false);
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800511
512 return rc;
513}
514
Jeremy Kerrf403c422018-07-26 12:14:56 +0800515static int run_proxy(struct ctx *ctx)
516{
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800517 struct pollfd pollfds[4];
Jeremy Kerrf403c422018-07-26 12:14:56 +0800518 bool exit = false;
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800519 int rc, n_fd;
Jeremy Kerrf403c422018-07-26 12:14:56 +0800520
521 /* main proxy: forward data between stdio & socket */
522 pollfds[0].fd = ctx->sock_client;
523 pollfds[0].events = POLLIN;
524 pollfds[1].fd = STDIN_FILENO;
525 pollfds[1].events = POLLIN;
526 pollfds[2].fd = ctx->signal_pipe[0];
527 pollfds[2].events = POLLIN;
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800528 pollfds[3].fd = udev_monitor_get_fd(ctx->monitor);
529 pollfds[3].events = POLLIN;
530
531 n_fd = 4;
Jeremy Kerrf403c422018-07-26 12:14:56 +0800532
533 for (;;) {
534 errno = 0;
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800535 rc = poll(pollfds, n_fd, -1);
Jeremy Kerrf403c422018-07-26 12:14:56 +0800536 if (rc < 0) {
537 if (errno == EINTR)
538 continue;
539 warn("poll failed");
540 break;
541 }
542
543 if (pollfds[0].revents) {
544 rc = copy_fd(ctx, ctx->sock_client, STDOUT_FILENO);
545 if (rc <= 0)
546 break;
547 }
548
549 if (pollfds[1].revents) {
550 rc = copy_fd(ctx, STDIN_FILENO, ctx->sock_client);
551 if (rc <= 0)
552 break;
553 }
554
555 if (pollfds[2].revents) {
556 rc = process_signal_pipe(ctx, &exit);
557 if (rc || exit)
558 break;
559 }
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800560
561 if (pollfds[3].revents) {
562 rc = udev_process(ctx);
563 if (rc)
564 break;
565
566 /* udev_process may have closed the udev connection,
567 * in which case we can stop polling on its fd */
568 if (!ctx->udev) {
569 pollfds[3].fd = 0;
570 pollfds[3].revents = 0;
571 n_fd = 3;
572 }
573 }
Jeremy Kerrf403c422018-07-26 12:14:56 +0800574 }
575
576 return rc ? -1 : 0;
577}
578
Jeremy Kerr13bb28f2018-08-09 10:41:10 +0800579static void print_metadata(struct ctx *ctx)
580{
581 struct json_object *md;
582 int i;
583
584 md = json_object_new_object();
585
586 for (i = 0; i < ctx->n_configs; i++) {
587 struct config *config = &ctx->configs[i];
588 json_object_object_add(md, config->name,
589 config->metadata);
590 }
591
592 puts(json_object_get_string(md));
593
594 json_object_put(md);
595}
596
Jeremy Kerr19527352018-08-03 15:04:38 +0800597static void config_free_one(struct config *config)
Jeremy Kerrf403c422018-07-26 12:14:56 +0800598{
Jeremy Kerr19527352018-08-03 15:04:38 +0800599 if (config->metadata)
600 json_object_put(config->metadata);
601 free(config->nbd_device);
602 free(config->name);
603}
604
605static int config_parse_one(struct config *config, const char *name,
606 json_object *obj)
607{
608 struct json_object *tmp, *meta;
609 json_bool jrc;
610
611 jrc = json_object_object_get_ex(obj, "nbd-device", &tmp);
612 if (!jrc) {
613 warnx("config %s doesn't specify a nbd-device", name);
614 return -1;
615 }
616
617 if (!json_object_is_type(tmp, json_type_string)) {
618 warnx("config %s has invalid nbd-device", name);
619 return -1;
620 }
621
622 config->nbd_device = strdup(json_object_get_string(tmp));
623 config->name = strdup(name);
624
Jeremy Kerr0f1a49c2018-08-03 15:41:38 +0800625 jrc = json_object_object_get_ex(obj, "default", &tmp);
626 config->is_default = jrc && json_object_get_boolean(tmp);
627
Jeremy Kerr19527352018-08-03 15:04:38 +0800628 jrc = json_object_object_get_ex(obj, "metadata", &meta);
629 if (jrc && json_object_is_type(meta, json_type_object))
630 config->metadata = json_object_get(meta);
631 else
632 config->metadata = NULL;
633
634 return 0;
635}
636
637static void config_free(struct ctx *ctx)
638{
639 int i;
640
641 for (i = 0; i < ctx->n_configs; i++)
642 config_free_one(&ctx->configs[i]);
643
644 free(ctx->configs);
645 ctx->n_configs = 0;
646}
647
648static int config_init(struct ctx *ctx)
649{
650 struct json_object *obj, *tmp;
651 json_bool jrc;
652 int i, rc;
653
654 /* apply defaults */
655 ctx->nbd_timeout = nbd_timeout_default;
656
657 obj = json_object_from_file(conf_path);
658 if (!obj) {
659 warnx("can't read configuration from %s\n", conf_path);
660 return -1;
661 }
662
663 /* global configuration */
664 jrc = json_object_object_get_ex(obj, "timeout", &tmp);
665 if (jrc) {
666 errno = 0;
667 ctx->nbd_timeout = json_object_get_int(tmp);
668 if (ctx->nbd_timeout == 0 && errno) {
669 warnx("can't parse timeout value");
670 goto err_free;
671 }
672 }
673
674 /* per-config configuration */
675 jrc = json_object_object_get_ex(obj, "configurations", &tmp);
676 if (!jrc) {
677 warnx("no configurations specified");
678 goto err_free;
679 }
680
681 if (!json_object_is_type(tmp, json_type_object)) {
682 warnx("invalid configurations format");
683 goto err_free;
684 }
685
686 ctx->n_configs = json_object_object_length(tmp);
687 ctx->configs = calloc(ctx->n_configs, sizeof(*ctx->configs));
688
689 i = 0;
Jeremy Kerr0f1a49c2018-08-03 15:41:38 +0800690 json_object_object_foreach(tmp, name, config_json) {
691 struct config *config = &ctx->configs[i];
692
693 rc = config_parse_one(config, name, config_json);
Jeremy Kerr19527352018-08-03 15:04:38 +0800694 if (rc)
695 goto err_free;
Jeremy Kerr0f1a49c2018-08-03 15:41:38 +0800696
697 if (config->is_default) {
698 if (ctx->default_config) {
699 warn("multiple configs flagged as default");
700 goto err_free;
701 }
702 ctx->default_config = config;
703 }
Jeremy Kerr19527352018-08-03 15:04:38 +0800704 i++;
705 }
706
707 json_object_put(obj);
708
Jeremy Kerr0f1a49c2018-08-03 15:41:38 +0800709 if (ctx->n_configs == 1)
710 ctx->default_config = &ctx->configs[0];
711
Jeremy Kerr19527352018-08-03 15:04:38 +0800712 return 0;
713
714err_free:
715 warnx("failed to load config from %s", conf_path);
716 json_object_put(obj);
717 return -1;
718}
719
720static int config_select(struct ctx *ctx, const char *name)
721{
722 struct config *config;
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800723 struct stat statbuf;
724 int i, rc;
Jeremy Kerr19527352018-08-03 15:04:38 +0800725
726 config = NULL;
727
Jeremy Kerr0f1a49c2018-08-03 15:41:38 +0800728 if (!name) {
729 /* no config specified: use the default */
730 if (!ctx->default_config) {
731 warnx("no config specified, and no default");
732 return -1;
Jeremy Kerr19527352018-08-03 15:04:38 +0800733 }
Jeremy Kerr0f1a49c2018-08-03 15:41:38 +0800734 config = ctx->default_config;
Jeremy Kerr19527352018-08-03 15:04:38 +0800735
Jeremy Kerr0f1a49c2018-08-03 15:41:38 +0800736 } else {
737 /* find a matching config... */
738 for (i = 0; i < ctx->n_configs; i++) {
739 if (!strcmp(ctx->configs[i].name, name)) {
740 config = &ctx->configs[i];
741 break;
742 }
743 }
744
745 if (!config) {
746 warnx("no such configuration '%s'", name);
747 return -1;
748 }
Jeremy Kerr19527352018-08-03 15:04:38 +0800749 }
750
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800751 /* check that the device exists */
752 rc = stat(config->nbd_device, &statbuf);
753 if (rc) {
754 warn("can't stat nbd device %s", config->nbd_device);
755 return -1;
756 }
757
758 if (!S_ISBLK(statbuf.st_mode)) {
759 warn("specified nbd path %s isn't a block device",
760 config->nbd_device);
761 return -1;
762 }
763
Jeremy Kerr19527352018-08-03 15:04:38 +0800764 /* ... and apply it */
Jeremy Kerrc6134c12018-08-09 13:03:33 +0800765 ctx->config = config;
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800766 ctx->nbd_devno = statbuf.st_rdev;
767
Jeremy Kerr19527352018-08-03 15:04:38 +0800768 return 0;
769}
770
Jeremy Kerr6f9c4332018-08-03 17:11:15 +0800771static const struct option options[] = {
Jeremy Kerr13bb28f2018-08-09 10:41:10 +0800772 { .name = "help", .val = 'h' },
773 { .name = "metadata", .val = 'm' },
Jeremy Kerr6f9c4332018-08-03 17:11:15 +0800774 { 0 },
775};
776
Jeremy Kerr13bb28f2018-08-09 10:41:10 +0800777enum action {
778 ACTION_PROXY,
779 ACTION_METADATA,
780};
781
Jeremy Kerr6f9c4332018-08-03 17:11:15 +0800782static void print_usage(const char *progname)
783{
Jeremy Kerr13bb28f2018-08-09 10:41:10 +0800784 fprintf(stderr, "usage:\n");
785 fprintf(stderr, "\t%s [configuration]\n", progname);
786 fprintf(stderr, "\t%s --metadata\n", progname);
Jeremy Kerr6f9c4332018-08-03 17:11:15 +0800787}
788
Jeremy Kerr19527352018-08-03 15:04:38 +0800789int main(int argc, char **argv)
790{
Jeremy Kerr13bb28f2018-08-09 10:41:10 +0800791 enum action action = ACTION_PROXY;
Jeremy Kerr19527352018-08-03 15:04:38 +0800792 const char *config_name;
Jeremy Kerrf403c422018-07-26 12:14:56 +0800793 struct ctx _ctx, *ctx;
794 int rc;
795
Jeremy Kerr0f1a49c2018-08-03 15:41:38 +0800796 config_name = NULL;
Jeremy Kerr19527352018-08-03 15:04:38 +0800797
Jeremy Kerr6f9c4332018-08-03 17:11:15 +0800798 for (;;) {
799 int c = getopt_long(argc, argv, "h", options, NULL);
800 if (c == -1)
801 break;
802
803 switch (c) {
Jeremy Kerr13bb28f2018-08-09 10:41:10 +0800804 case 'm':
805 action = ACTION_METADATA;
806 break;
Jeremy Kerr6f9c4332018-08-03 17:11:15 +0800807 case 'h':
808 case '?':
809 print_usage(argv[0]);
810 return c == 'h' ? EXIT_SUCCESS : EXIT_FAILURE;
811 }
812 }
813
814 if (optind < argc)
815 config_name = argv[optind];
Jeremy Kerr19527352018-08-03 15:04:38 +0800816
Jeremy Kerrf403c422018-07-26 12:14:56 +0800817 ctx = &_ctx;
Jeremy Kerr19527352018-08-03 15:04:38 +0800818 memset(ctx, 0, sizeof(*ctx));
Jeremy Kerrf403c422018-07-26 12:14:56 +0800819 ctx->bufsize = bufsize;
820 ctx->buf = malloc(ctx->bufsize);
Jeremy Kerr19527352018-08-03 15:04:38 +0800821
822 rc = config_init(ctx);
823 if (rc)
824 goto out_free;
825
Jeremy Kerr13bb28f2018-08-09 10:41:10 +0800826 if (action == ACTION_METADATA) {
827 print_metadata(ctx);
828 goto out_free;
829 }
830
Jeremy Kerr19527352018-08-03 15:04:38 +0800831 rc = config_select(ctx, config_name);
832 if (rc)
833 goto out_free;
Jeremy Kerrf403c422018-07-26 12:14:56 +0800834
835 rc = open_nbd_socket(ctx);
836 if (rc)
837 goto out_free;
838
839 rc = setup_signals(ctx);
840 if (rc)
841 goto out_close;
842
843 rc = start_nbd_client(ctx);
844 if (rc)
845 goto out_stop_client;
846
847 rc = wait_for_nbd_client(ctx);
848 if (rc)
849 goto out_stop_client;
850
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800851 rc = udev_init(ctx);
Jeremy Kerrc6134c12018-08-09 13:03:33 +0800852 if (rc)
853 goto out_stop_client;
854
Jeremy Kerrf403c422018-07-26 12:14:56 +0800855 rc = run_proxy(ctx);
856
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800857 if (ctx->udev)
858 udev_free(ctx);
859
Jeremy Kerrfa1d3752018-08-13 13:13:16 +0800860 run_state_hook(ctx, "stop", true);
Jeremy Kerrc6134c12018-08-09 13:03:33 +0800861
Jeremy Kerrf403c422018-07-26 12:14:56 +0800862out_stop_client:
863 /* we cleanup signals before stopping the client, because we
864 * no longer care about SIGCHLD from the stopping nbd-client
865 * process. stop_nbd_client will be a no-op if the client hasn't
866 * been started. */
867 cleanup_signals(ctx);
868
869 stop_nbd_client(ctx);
870 close(ctx->sock_client);
871
872out_close:
873 if (ctx->sock_path) {
874 unlink(ctx->sock_path);
875 free(ctx->sock_path);
876 }
877 close(ctx->sock);
878out_free:
Jeremy Kerr19527352018-08-03 15:04:38 +0800879 config_free(ctx);
Jeremy Kerrf403c422018-07-26 12:14:56 +0800880 free(ctx->buf);
881 return rc ? EXIT_FAILURE : EXIT_SUCCESS;
882}