blob: 3928aac0ed5c7600cedb8a7a40c3cec27ae5f4da [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);
Jeremy Kerrf403c422018-07-26 12:14:56 +0800153 close(fd);
Jeremy Kerrf403c422018-07-26 12:14:56 +0800154
155 execlp("nbd-client", "nbd-client",
156 "-u", ctx->sock_path,
157 "-n",
Tang Yiweicfa074b2022-11-17 10:28:53 +0800158 "-L",
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);
Jeremy Kerra87af842018-08-13 11:48:23 +0800408 execl(state_hook_path, argv0, action, ctx->config->name, NULL);
Jeremy Kerrc6134c12018-08-09 13:03:33 +0800409 exit(EXIT_FAILURE);
410 }
411
Jeremy Kerrfa1d3752018-08-13 13:13:16 +0800412 if (!wait) {
413 ctx->state_hook_pid = pid;
414 return 0;
415 }
416
Jeremy Kerrc6134c12018-08-09 13:03:33 +0800417 rc = waitpid(pid, &status, 0);
418 if (rc < 0) {
419 warn("wait");
420 return -1;
421 }
422
423 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
Jeremy Kerra87af842018-08-13 11:48:23 +0800424 warnx("hook %s failed", state_hook_path);
Jeremy Kerrc6134c12018-08-09 13:03:33 +0800425 return -1;
426 }
427
428 return 0;
429}
430
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800431static int udev_init(struct ctx *ctx)
432{
433 int rc;
434
435 ctx->udev = udev_new();
436 if (!ctx) {
437 warn("can't create udev object");
438 return -1;
439 }
440
441 ctx->monitor = udev_monitor_new_from_netlink(ctx->udev, "kernel");
442 if (!ctx->monitor) {
443 warn("can't create udev monitor");
444 goto out_unref_udev;
445 }
446
447 rc = udev_monitor_filter_add_match_subsystem_devtype(
448 ctx->monitor, "block", "disk");
449 if (rc) {
450 warn("can't create udev monitor filter");
451 goto out_unref_monitor;
452 }
453
454 rc = udev_monitor_enable_receiving(ctx->monitor);
455 if (rc) {
456 warn("can't start udev monitor");
457 goto out_unref_monitor;
458 }
459
460 return 0;
461
462out_unref_monitor:
463 udev_monitor_unref(ctx->monitor);
464out_unref_udev:
465 udev_unref(ctx->udev);
466 return -1;
467}
468
469static void udev_free(struct ctx *ctx)
470{
471 udev_monitor_unref(ctx->monitor);
472 udev_unref(ctx->udev);
473}
474
475/* Check for the change event on our nbd device, signifying that the kernel
476 * has finished initialising the block device. Once we see the event, we run
477 * the "start" state hook, and close the udev monitor.
478 *
479 * Returns:
480 * 0 if no processing was performed
481 * -1 on state hook error (and the nbd session should be closed)
482 */
483static int udev_process(struct ctx *ctx)
484{
485 struct udev_device *dev;
486 bool action_is_change;
487 dev_t devno;
488 int rc;
489
490 dev = udev_monitor_receive_device(ctx->monitor);
491 if (!dev)
492 return 0;
493
494 devno = udev_device_get_devnum(dev);
495 action_is_change = !strcmp(udev_device_get_action(dev), "change");
496 udev_device_unref(dev);
497
498 if (devno != ctx->nbd_devno)
499 return 0;
500
501 if (!action_is_change)
502 return 0;
503
504 udev_monitor_unref(ctx->monitor);
505 udev_unref(ctx->udev);
506 ctx->monitor = NULL;
507 ctx->udev = NULL;
508
Jeremy Kerrfa1d3752018-08-13 13:13:16 +0800509 rc = run_state_hook(ctx, "start", false);
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800510
511 return rc;
512}
513
Jeremy Kerrf403c422018-07-26 12:14:56 +0800514static int run_proxy(struct ctx *ctx)
515{
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800516 struct pollfd pollfds[4];
Jeremy Kerrf403c422018-07-26 12:14:56 +0800517 bool exit = false;
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800518 int rc, n_fd;
Jeremy Kerrf403c422018-07-26 12:14:56 +0800519
520 /* main proxy: forward data between stdio & socket */
521 pollfds[0].fd = ctx->sock_client;
522 pollfds[0].events = POLLIN;
523 pollfds[1].fd = STDIN_FILENO;
524 pollfds[1].events = POLLIN;
525 pollfds[2].fd = ctx->signal_pipe[0];
526 pollfds[2].events = POLLIN;
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800527 pollfds[3].fd = udev_monitor_get_fd(ctx->monitor);
528 pollfds[3].events = POLLIN;
529
530 n_fd = 4;
Jeremy Kerrf403c422018-07-26 12:14:56 +0800531
532 for (;;) {
533 errno = 0;
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800534 rc = poll(pollfds, n_fd, -1);
Jeremy Kerrf403c422018-07-26 12:14:56 +0800535 if (rc < 0) {
536 if (errno == EINTR)
537 continue;
538 warn("poll failed");
539 break;
540 }
541
542 if (pollfds[0].revents) {
543 rc = copy_fd(ctx, ctx->sock_client, STDOUT_FILENO);
544 if (rc <= 0)
545 break;
546 }
547
548 if (pollfds[1].revents) {
549 rc = copy_fd(ctx, STDIN_FILENO, ctx->sock_client);
550 if (rc <= 0)
551 break;
552 }
553
554 if (pollfds[2].revents) {
555 rc = process_signal_pipe(ctx, &exit);
556 if (rc || exit)
557 break;
558 }
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800559
560 if (pollfds[3].revents) {
561 rc = udev_process(ctx);
562 if (rc)
563 break;
564
565 /* udev_process may have closed the udev connection,
566 * in which case we can stop polling on its fd */
567 if (!ctx->udev) {
568 pollfds[3].fd = 0;
569 pollfds[3].revents = 0;
570 n_fd = 3;
571 }
572 }
Jeremy Kerrf403c422018-07-26 12:14:56 +0800573 }
574
575 return rc ? -1 : 0;
576}
577
Jeremy Kerr13bb28f2018-08-09 10:41:10 +0800578static void print_metadata(struct ctx *ctx)
579{
580 struct json_object *md;
581 int i;
582
583 md = json_object_new_object();
584
585 for (i = 0; i < ctx->n_configs; i++) {
586 struct config *config = &ctx->configs[i];
587 json_object_object_add(md, config->name,
Adriana Kobylak74c8a722019-01-10 10:35:44 -0600588 json_object_get(config->metadata));
Jeremy Kerr13bb28f2018-08-09 10:41:10 +0800589 }
590
591 puts(json_object_get_string(md));
592
593 json_object_put(md);
594}
595
Jeremy Kerr19527352018-08-03 15:04:38 +0800596static void config_free_one(struct config *config)
Jeremy Kerrf403c422018-07-26 12:14:56 +0800597{
Jeremy Kerr19527352018-08-03 15:04:38 +0800598 if (config->metadata)
599 json_object_put(config->metadata);
600 free(config->nbd_device);
601 free(config->name);
602}
603
604static int config_parse_one(struct config *config, const char *name,
605 json_object *obj)
606{
607 struct json_object *tmp, *meta;
608 json_bool jrc;
609
610 jrc = json_object_object_get_ex(obj, "nbd-device", &tmp);
611 if (!jrc) {
612 warnx("config %s doesn't specify a nbd-device", name);
613 return -1;
614 }
615
616 if (!json_object_is_type(tmp, json_type_string)) {
617 warnx("config %s has invalid nbd-device", name);
618 return -1;
619 }
620
621 config->nbd_device = strdup(json_object_get_string(tmp));
622 config->name = strdup(name);
623
Jeremy Kerr0f1a49c2018-08-03 15:41:38 +0800624 jrc = json_object_object_get_ex(obj, "default", &tmp);
625 config->is_default = jrc && json_object_get_boolean(tmp);
626
Jeremy Kerr19527352018-08-03 15:04:38 +0800627 jrc = json_object_object_get_ex(obj, "metadata", &meta);
628 if (jrc && json_object_is_type(meta, json_type_object))
629 config->metadata = json_object_get(meta);
630 else
631 config->metadata = NULL;
632
633 return 0;
634}
635
636static void config_free(struct ctx *ctx)
637{
638 int i;
639
640 for (i = 0; i < ctx->n_configs; i++)
641 config_free_one(&ctx->configs[i]);
642
643 free(ctx->configs);
644 ctx->n_configs = 0;
645}
646
647static int config_init(struct ctx *ctx)
648{
649 struct json_object *obj, *tmp;
650 json_bool jrc;
651 int i, rc;
652
653 /* apply defaults */
654 ctx->nbd_timeout = nbd_timeout_default;
655
656 obj = json_object_from_file(conf_path);
657 if (!obj) {
658 warnx("can't read configuration from %s\n", conf_path);
659 return -1;
660 }
661
662 /* global configuration */
663 jrc = json_object_object_get_ex(obj, "timeout", &tmp);
664 if (jrc) {
665 errno = 0;
666 ctx->nbd_timeout = json_object_get_int(tmp);
667 if (ctx->nbd_timeout == 0 && errno) {
668 warnx("can't parse timeout value");
669 goto err_free;
670 }
671 }
672
673 /* per-config configuration */
674 jrc = json_object_object_get_ex(obj, "configurations", &tmp);
675 if (!jrc) {
676 warnx("no configurations specified");
677 goto err_free;
678 }
679
680 if (!json_object_is_type(tmp, json_type_object)) {
681 warnx("invalid configurations format");
682 goto err_free;
683 }
684
685 ctx->n_configs = json_object_object_length(tmp);
686 ctx->configs = calloc(ctx->n_configs, sizeof(*ctx->configs));
687
688 i = 0;
Jeremy Kerr0f1a49c2018-08-03 15:41:38 +0800689 json_object_object_foreach(tmp, name, config_json) {
690 struct config *config = &ctx->configs[i];
691
692 rc = config_parse_one(config, name, config_json);
Jeremy Kerr19527352018-08-03 15:04:38 +0800693 if (rc)
694 goto err_free;
Jeremy Kerr0f1a49c2018-08-03 15:41:38 +0800695
696 if (config->is_default) {
697 if (ctx->default_config) {
698 warn("multiple configs flagged as default");
699 goto err_free;
700 }
701 ctx->default_config = config;
702 }
Jeremy Kerr19527352018-08-03 15:04:38 +0800703 i++;
704 }
705
706 json_object_put(obj);
707
Jeremy Kerr0f1a49c2018-08-03 15:41:38 +0800708 if (ctx->n_configs == 1)
709 ctx->default_config = &ctx->configs[0];
710
Jeremy Kerr19527352018-08-03 15:04:38 +0800711 return 0;
712
713err_free:
714 warnx("failed to load config from %s", conf_path);
715 json_object_put(obj);
716 return -1;
717}
718
719static int config_select(struct ctx *ctx, const char *name)
720{
721 struct config *config;
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800722 struct stat statbuf;
723 int i, rc;
Jeremy Kerr19527352018-08-03 15:04:38 +0800724
725 config = NULL;
726
Jeremy Kerr0f1a49c2018-08-03 15:41:38 +0800727 if (!name) {
728 /* no config specified: use the default */
729 if (!ctx->default_config) {
730 warnx("no config specified, and no default");
731 return -1;
Jeremy Kerr19527352018-08-03 15:04:38 +0800732 }
Jeremy Kerr0f1a49c2018-08-03 15:41:38 +0800733 config = ctx->default_config;
Jeremy Kerr19527352018-08-03 15:04:38 +0800734
Jeremy Kerr0f1a49c2018-08-03 15:41:38 +0800735 } else {
736 /* find a matching config... */
737 for (i = 0; i < ctx->n_configs; i++) {
738 if (!strcmp(ctx->configs[i].name, name)) {
739 config = &ctx->configs[i];
740 break;
741 }
742 }
743
744 if (!config) {
745 warnx("no such configuration '%s'", name);
746 return -1;
747 }
Jeremy Kerr19527352018-08-03 15:04:38 +0800748 }
749
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800750 /* check that the device exists */
751 rc = stat(config->nbd_device, &statbuf);
752 if (rc) {
753 warn("can't stat nbd device %s", config->nbd_device);
754 return -1;
755 }
756
757 if (!S_ISBLK(statbuf.st_mode)) {
758 warn("specified nbd path %s isn't a block device",
759 config->nbd_device);
760 return -1;
761 }
762
Jeremy Kerr19527352018-08-03 15:04:38 +0800763 /* ... and apply it */
Jeremy Kerrc6134c12018-08-09 13:03:33 +0800764 ctx->config = config;
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800765 ctx->nbd_devno = statbuf.st_rdev;
766
Jeremy Kerr19527352018-08-03 15:04:38 +0800767 return 0;
768}
769
Jeremy Kerr6f9c4332018-08-03 17:11:15 +0800770static const struct option options[] = {
Jeremy Kerr13bb28f2018-08-09 10:41:10 +0800771 { .name = "help", .val = 'h' },
772 { .name = "metadata", .val = 'm' },
Jeremy Kerr6f9c4332018-08-03 17:11:15 +0800773 { 0 },
774};
775
Jeremy Kerr13bb28f2018-08-09 10:41:10 +0800776enum action {
777 ACTION_PROXY,
778 ACTION_METADATA,
779};
780
Jeremy Kerr6f9c4332018-08-03 17:11:15 +0800781static void print_usage(const char *progname)
782{
Jeremy Kerr13bb28f2018-08-09 10:41:10 +0800783 fprintf(stderr, "usage:\n");
784 fprintf(stderr, "\t%s [configuration]\n", progname);
785 fprintf(stderr, "\t%s --metadata\n", progname);
Jeremy Kerr6f9c4332018-08-03 17:11:15 +0800786}
787
Jeremy Kerr19527352018-08-03 15:04:38 +0800788int main(int argc, char **argv)
789{
Jeremy Kerr13bb28f2018-08-09 10:41:10 +0800790 enum action action = ACTION_PROXY;
Jeremy Kerr19527352018-08-03 15:04:38 +0800791 const char *config_name;
Jeremy Kerrf403c422018-07-26 12:14:56 +0800792 struct ctx _ctx, *ctx;
793 int rc;
794
Jeremy Kerr0f1a49c2018-08-03 15:41:38 +0800795 config_name = NULL;
Jeremy Kerr19527352018-08-03 15:04:38 +0800796
Jeremy Kerr6f9c4332018-08-03 17:11:15 +0800797 for (;;) {
798 int c = getopt_long(argc, argv, "h", options, NULL);
799 if (c == -1)
800 break;
801
802 switch (c) {
Jeremy Kerr13bb28f2018-08-09 10:41:10 +0800803 case 'm':
804 action = ACTION_METADATA;
805 break;
Jeremy Kerr6f9c4332018-08-03 17:11:15 +0800806 case 'h':
807 case '?':
808 print_usage(argv[0]);
809 return c == 'h' ? EXIT_SUCCESS : EXIT_FAILURE;
810 }
811 }
812
813 if (optind < argc)
814 config_name = argv[optind];
Jeremy Kerr19527352018-08-03 15:04:38 +0800815
Jeremy Kerrf403c422018-07-26 12:14:56 +0800816 ctx = &_ctx;
Jeremy Kerr19527352018-08-03 15:04:38 +0800817 memset(ctx, 0, sizeof(*ctx));
Jeremy Kerrf403c422018-07-26 12:14:56 +0800818 ctx->bufsize = bufsize;
819 ctx->buf = malloc(ctx->bufsize);
Jeremy Kerr19527352018-08-03 15:04:38 +0800820
821 rc = config_init(ctx);
822 if (rc)
823 goto out_free;
824
Jeremy Kerr13bb28f2018-08-09 10:41:10 +0800825 if (action == ACTION_METADATA) {
826 print_metadata(ctx);
827 goto out_free;
828 }
829
Jeremy Kerr19527352018-08-03 15:04:38 +0800830 rc = config_select(ctx, config_name);
831 if (rc)
832 goto out_free;
Jeremy Kerrf403c422018-07-26 12:14:56 +0800833
834 rc = open_nbd_socket(ctx);
835 if (rc)
836 goto out_free;
837
838 rc = setup_signals(ctx);
839 if (rc)
840 goto out_close;
841
842 rc = start_nbd_client(ctx);
843 if (rc)
844 goto out_stop_client;
845
846 rc = wait_for_nbd_client(ctx);
847 if (rc)
848 goto out_stop_client;
849
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800850 rc = udev_init(ctx);
Jeremy Kerrc6134c12018-08-09 13:03:33 +0800851 if (rc)
852 goto out_stop_client;
853
Jeremy Kerrf403c422018-07-26 12:14:56 +0800854 rc = run_proxy(ctx);
855
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800856 if (ctx->udev)
857 udev_free(ctx);
858
Jeremy Kerrfa1d3752018-08-13 13:13:16 +0800859 run_state_hook(ctx, "stop", true);
Jeremy Kerrc6134c12018-08-09 13:03:33 +0800860
Jeremy Kerrf403c422018-07-26 12:14:56 +0800861out_stop_client:
862 /* we cleanup signals before stopping the client, because we
863 * no longer care about SIGCHLD from the stopping nbd-client
864 * process. stop_nbd_client will be a no-op if the client hasn't
865 * been started. */
866 cleanup_signals(ctx);
867
868 stop_nbd_client(ctx);
869 close(ctx->sock_client);
870
871out_close:
872 if (ctx->sock_path) {
873 unlink(ctx->sock_path);
874 free(ctx->sock_path);
875 }
876 close(ctx->sock);
877out_free:
Jeremy Kerr19527352018-08-03 15:04:38 +0800878 config_free(ctx);
Jeremy Kerrf403c422018-07-26 12:14:56 +0800879 free(ctx->buf);
880 return rc ? EXIT_FAILURE : EXIT_SUCCESS;
881}