blob: 6e932ad2b6b0e3ead1da04bdb00e91cec530bf63 [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 Kerr19527352018-08-03 15:04:38 +080059 int nbd_timeout;
Jeremy Kerr4ac92462018-08-10 11:16:29 +080060 dev_t nbd_devno;
Jeremy Kerrf403c422018-07-26 12:14:56 +080061 uint8_t *buf;
62 size_t bufsize;
Jeremy Kerr19527352018-08-03 15:04:38 +080063 struct config *configs;
64 int n_configs;
Jeremy Kerr0f1a49c2018-08-03 15:41:38 +080065 struct config *default_config;
Jeremy Kerrc6134c12018-08-09 13:03:33 +080066 struct config *config;
Jeremy Kerr4ac92462018-08-10 11:16:29 +080067 struct udev *udev;
68 struct udev_monitor *monitor;
Jeremy Kerrf403c422018-07-26 12:14:56 +080069};
70
Jeremy Kerr19527352018-08-03 15:04:38 +080071static const char *conf_path = SYSCONFDIR "/nbd-proxy/config.json";
Jeremy Kerrc6134c12018-08-09 13:03:33 +080072static const char *state_hook_path = SYSCONFDIR "/nbd-proxy/state.d";
Jeremy Kerrf403c422018-07-26 12:14:56 +080073static const char *sockpath_tmpl = RUNSTATEDIR "/nbd.%d.sock";
Jeremy Kerrc6134c12018-08-09 13:03:33 +080074
Jeremy Kerrf403c422018-07-26 12:14:56 +080075static const size_t bufsize = 0x20000;
Jeremy Kerr19527352018-08-03 15:04:38 +080076static const int nbd_timeout_default = 30;
Jeremy Kerrf403c422018-07-26 12:14:56 +080077
Jeremy Kerrc6134c12018-08-09 13:03:33 +080078#define BUILD_ASSERT_OR_ZERO(c) (sizeof(struct {int:-!(c);}))
79
Jeremy Kerrf403c422018-07-26 12:14:56 +080080static int open_nbd_socket(struct ctx *ctx)
81{
82 struct sockaddr_un addr;
83 char *path;
84 int sd, rc;
85
86 rc = asprintf(&path, sockpath_tmpl, getpid());
87 if (rc < 0)
88 return -1;
89
Jeremy Kerr4eeb9ed2018-08-09 13:32:19 +080090 sd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
Jeremy Kerrf403c422018-07-26 12:14:56 +080091 if (sd < 0) {
92 warn("can't create socket");
93 goto err_free;
94 }
95
96 rc = fchmod(sd, S_IRUSR | S_IWUSR);
97 if (rc) {
98 warn("can't set permissions on socket");
99 goto err_close;
100 }
101
102 addr.sun_family = AF_UNIX;
103 strncpy(addr.sun_path, path, sizeof(addr.sun_path));
104
105 rc = bind(sd, (struct sockaddr *)&addr, sizeof(addr));
106 if (rc) {
107 warn("can't bind to path %s", path);
108 goto err_close;
109 }
110
111 rc = listen(sd, 1);
112 if (rc) {
113 warn("can't listen on socket %s", path);
114 goto err_unlink;
115 }
116
117 ctx->sock = sd;
118 ctx->sock_path = path;
119 return 0;
120
121err_unlink:
122 unlink(path);
123err_close:
124 close(sd);
125err_free:
126 free(path);
127 return -1;
128}
129
130static int start_nbd_client(struct ctx *ctx)
131{
132 pid_t pid;
133
134 pid = fork();
135 if (pid < 0) {
136 warn("can't create client process");
137 return -1;
138 }
139
140 /* child process: run nbd-client in non-fork mode */
141 if (pid == 0) {
Jeremy Kerr09295f22018-07-27 16:29:38 +0800142 char timeout_str[10];
Jeremy Kerrf403c422018-07-26 12:14:56 +0800143 int fd;
144
Jeremy Kerr19527352018-08-03 15:04:38 +0800145 snprintf(timeout_str, sizeof(timeout_str),
146 "%d", ctx->nbd_timeout);
Jeremy Kerr09295f22018-07-27 16:29:38 +0800147
Jeremy Kerr4eeb9ed2018-08-09 13:32:19 +0800148 fd = open("/dev/null", O_RDWR | O_CLOEXEC);
Jeremy Kerrf403c422018-07-26 12:14:56 +0800149 if (fd < 0)
150 err(EXIT_FAILURE, "can't open /dev/null");
151
152 dup2(fd, STDIN_FILENO);
153 dup2(fd, STDOUT_FILENO);
154 dup2(fd, STDERR_FILENO);
155 close(fd);
Jeremy Kerrf403c422018-07-26 12:14:56 +0800156
157 execlp("nbd-client", "nbd-client",
158 "-u", ctx->sock_path,
159 "-n",
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
283static int process_signal_pipe(struct ctx *ctx, bool *exit)
284{
285 int buf, rc, status;
286
287 rc = read(ctx->signal_pipe[0], &buf, sizeof(buf));
288 if (rc != sizeof(buf))
289 return -1;
290
291 *exit = false;
292
293 switch (buf) {
294 case SIGCHLD:
295 rc = waitpid(ctx->nbd_client_pid, &status, WNOHANG);
296 if (rc > 0) {
297 warnx("nbd client stopped (%s: %d); exiting",
298 WIFEXITED(status) ? "rc" : "sig",
299 WIFEXITED(status) ?
300 WEXITSTATUS(status) :
301 WTERMSIG(status));
302 ctx->nbd_client_pid = 0;
303 }
304 break;
305 case SIGINT:
306 case SIGTERM:
307 *exit = true;
308 break;
309 }
310
311 return 0;
312}
313
314static int wait_for_nbd_client(struct ctx *ctx)
315{
316 struct pollfd pollfds[2];
317 int rc;
318
319 pollfds[0].fd = ctx->sock;
320 pollfds[0].events = POLLIN;
321 pollfds[1].fd = ctx->signal_pipe[0];
322 pollfds[1].events = POLLIN;
323
324 for (;;) {
325 errno = 0;
326 rc = poll(pollfds, 2, -1);
327 if (rc < 0) {
328 if (errno == EINTR)
329 continue;
330 warn("poll failed");
331 return -1;
332 }
333
334 if (pollfds[0].revents) {
Jeremy Kerr4eeb9ed2018-08-09 13:32:19 +0800335 rc = accept4(ctx->sock, NULL, NULL, SOCK_CLOEXEC);
Jeremy Kerrf403c422018-07-26 12:14:56 +0800336 if (rc < 0) {
337 warn("can't create connection");
338 return -1;
339 }
340 ctx->sock_client = rc;
341 break;
342 }
343
344 if (pollfds[1].revents) {
345 bool exit;
346 rc = process_signal_pipe(ctx, &exit);
347 if (rc || exit)
348 return -1;
349 }
350 }
351
352 return 0;
353}
354
Jeremy Kerrc6134c12018-08-09 13:03:33 +0800355#define join_paths(p1, p2, r) \
356 (BUILD_ASSERT_OR_ZERO(sizeof(r) > PATH_MAX) + __join_paths(p1, p2, r))
357static int __join_paths(const char *p1, const char *p2, char res[])
358{
359 size_t len;
360 char *pos;
361
362 len = strlen(p1) + 1 + strlen(p2);
363 if (len > PATH_MAX)
364 return -1;
365
366 pos = res;
367 strcpy(pos, p1);
368 pos += strlen(p1);
369 *pos = '/';
370 pos++;
371 strcpy(pos, p2);
372
373 return 0;
374}
375
376static int run_state_hook(struct ctx *ctx,
377 const char *path, const char *name, const char *action)
378{
379 int status, rc, fd;
380 pid_t pid;
381
382 pid = fork();
383 if (pid < 0) {
384 warn("can't fork to execute hook %s", name);
385 return -1;
386 }
387
388 if (!pid) {
Jeremy Kerr4eeb9ed2018-08-09 13:32:19 +0800389 fd = open("/dev/null", O_RDWR | O_CLOEXEC);
Jeremy Kerrc6134c12018-08-09 13:03:33 +0800390 if (fd < 0)
391 exit(EXIT_FAILURE);
392
393 dup2(fd, STDIN_FILENO);
394 dup2(fd, STDOUT_FILENO);
395 dup2(fd, STDERR_FILENO);
396 execl(path, name, action, ctx->config->name, NULL);
397 exit(EXIT_FAILURE);
398 }
399
400 rc = waitpid(pid, &status, 0);
401 if (rc < 0) {
402 warn("wait");
403 return -1;
404 }
405
406 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
407 warnx("hook %s failed", name);
408 return -1;
409 }
410
411 return 0;
412}
413
414static int run_state_hooks(struct ctx *ctx, const char *action)
415{
416 struct dirent *dirent;
Jeremy Kerrc6134c12018-08-09 13:03:33 +0800417 DIR *dir;
Jeremy Kerr9b00deb2018-08-10 11:14:54 +0800418 int rc;
Jeremy Kerrc6134c12018-08-09 13:03:33 +0800419
420 dir = opendir(state_hook_path);
421 if (!dir)
422 return 0;
423
424 rc = 0;
425
426 for (dirent = readdir(dir); dirent; dirent = readdir(dir)) {
427 char full_path[PATH_MAX+1];
428 struct stat statbuf;
429
430 if (dirent->d_name[0] == '.')
431 continue;
432
433 rc = fstatat(dirfd(dir), dirent->d_name, &statbuf, 0);
Jeremy Kerr9b00deb2018-08-10 11:14:54 +0800434 if (rc) {
435 rc = 0;
Jeremy Kerrc6134c12018-08-09 13:03:33 +0800436 continue;
Jeremy Kerr9b00deb2018-08-10 11:14:54 +0800437 }
Jeremy Kerrc6134c12018-08-09 13:03:33 +0800438
439 if (!(S_ISREG(statbuf.st_mode) || S_ISLNK(statbuf.st_mode)))
440 continue;
441
442 if (faccessat(dirfd(dir), dirent->d_name, X_OK, 0))
443 continue;
444
445 rc = join_paths(state_hook_path, dirent->d_name, full_path);
Jeremy Kerr9b00deb2018-08-10 11:14:54 +0800446 if (rc) {
447 rc = 0;
Jeremy Kerrc6134c12018-08-09 13:03:33 +0800448 continue;
Jeremy Kerr9b00deb2018-08-10 11:14:54 +0800449 }
Jeremy Kerrc6134c12018-08-09 13:03:33 +0800450
451 rc = run_state_hook(ctx, full_path, dirent->d_name, action);
452 if (rc)
453 break;
454 }
455
456 closedir(dir);
457
458 return rc;
459}
Jeremy Kerrf403c422018-07-26 12:14:56 +0800460
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800461static int udev_init(struct ctx *ctx)
462{
463 int rc;
464
465 ctx->udev = udev_new();
466 if (!ctx) {
467 warn("can't create udev object");
468 return -1;
469 }
470
471 ctx->monitor = udev_monitor_new_from_netlink(ctx->udev, "kernel");
472 if (!ctx->monitor) {
473 warn("can't create udev monitor");
474 goto out_unref_udev;
475 }
476
477 rc = udev_monitor_filter_add_match_subsystem_devtype(
478 ctx->monitor, "block", "disk");
479 if (rc) {
480 warn("can't create udev monitor filter");
481 goto out_unref_monitor;
482 }
483
484 rc = udev_monitor_enable_receiving(ctx->monitor);
485 if (rc) {
486 warn("can't start udev monitor");
487 goto out_unref_monitor;
488 }
489
490 return 0;
491
492out_unref_monitor:
493 udev_monitor_unref(ctx->monitor);
494out_unref_udev:
495 udev_unref(ctx->udev);
496 return -1;
497}
498
499static void udev_free(struct ctx *ctx)
500{
501 udev_monitor_unref(ctx->monitor);
502 udev_unref(ctx->udev);
503}
504
505/* Check for the change event on our nbd device, signifying that the kernel
506 * has finished initialising the block device. Once we see the event, we run
507 * the "start" state hook, and close the udev monitor.
508 *
509 * Returns:
510 * 0 if no processing was performed
511 * -1 on state hook error (and the nbd session should be closed)
512 */
513static int udev_process(struct ctx *ctx)
514{
515 struct udev_device *dev;
516 bool action_is_change;
517 dev_t devno;
518 int rc;
519
520 dev = udev_monitor_receive_device(ctx->monitor);
521 if (!dev)
522 return 0;
523
524 devno = udev_device_get_devnum(dev);
525 action_is_change = !strcmp(udev_device_get_action(dev), "change");
526 udev_device_unref(dev);
527
528 if (devno != ctx->nbd_devno)
529 return 0;
530
531 if (!action_is_change)
532 return 0;
533
534 udev_monitor_unref(ctx->monitor);
535 udev_unref(ctx->udev);
536 ctx->monitor = NULL;
537 ctx->udev = NULL;
538
539 rc = run_state_hooks(ctx, "start");
540
541 return rc;
542}
543
Jeremy Kerrf403c422018-07-26 12:14:56 +0800544static int run_proxy(struct ctx *ctx)
545{
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800546 struct pollfd pollfds[4];
Jeremy Kerrf403c422018-07-26 12:14:56 +0800547 bool exit = false;
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800548 int rc, n_fd;
Jeremy Kerrf403c422018-07-26 12:14:56 +0800549
550 /* main proxy: forward data between stdio & socket */
551 pollfds[0].fd = ctx->sock_client;
552 pollfds[0].events = POLLIN;
553 pollfds[1].fd = STDIN_FILENO;
554 pollfds[1].events = POLLIN;
555 pollfds[2].fd = ctx->signal_pipe[0];
556 pollfds[2].events = POLLIN;
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800557 pollfds[3].fd = udev_monitor_get_fd(ctx->monitor);
558 pollfds[3].events = POLLIN;
559
560 n_fd = 4;
Jeremy Kerrf403c422018-07-26 12:14:56 +0800561
562 for (;;) {
563 errno = 0;
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800564 rc = poll(pollfds, n_fd, -1);
Jeremy Kerrf403c422018-07-26 12:14:56 +0800565 if (rc < 0) {
566 if (errno == EINTR)
567 continue;
568 warn("poll failed");
569 break;
570 }
571
572 if (pollfds[0].revents) {
573 rc = copy_fd(ctx, ctx->sock_client, STDOUT_FILENO);
574 if (rc <= 0)
575 break;
576 }
577
578 if (pollfds[1].revents) {
579 rc = copy_fd(ctx, STDIN_FILENO, ctx->sock_client);
580 if (rc <= 0)
581 break;
582 }
583
584 if (pollfds[2].revents) {
585 rc = process_signal_pipe(ctx, &exit);
586 if (rc || exit)
587 break;
588 }
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800589
590 if (pollfds[3].revents) {
591 rc = udev_process(ctx);
592 if (rc)
593 break;
594
595 /* udev_process may have closed the udev connection,
596 * in which case we can stop polling on its fd */
597 if (!ctx->udev) {
598 pollfds[3].fd = 0;
599 pollfds[3].revents = 0;
600 n_fd = 3;
601 }
602 }
Jeremy Kerrf403c422018-07-26 12:14:56 +0800603 }
604
605 return rc ? -1 : 0;
606}
607
Jeremy Kerr13bb28f2018-08-09 10:41:10 +0800608static void print_metadata(struct ctx *ctx)
609{
610 struct json_object *md;
611 int i;
612
613 md = json_object_new_object();
614
615 for (i = 0; i < ctx->n_configs; i++) {
616 struct config *config = &ctx->configs[i];
617 json_object_object_add(md, config->name,
618 config->metadata);
619 }
620
621 puts(json_object_get_string(md));
622
623 json_object_put(md);
624}
625
Jeremy Kerr19527352018-08-03 15:04:38 +0800626static void config_free_one(struct config *config)
Jeremy Kerrf403c422018-07-26 12:14:56 +0800627{
Jeremy Kerr19527352018-08-03 15:04:38 +0800628 if (config->metadata)
629 json_object_put(config->metadata);
630 free(config->nbd_device);
631 free(config->name);
632}
633
634static int config_parse_one(struct config *config, const char *name,
635 json_object *obj)
636{
637 struct json_object *tmp, *meta;
638 json_bool jrc;
639
640 jrc = json_object_object_get_ex(obj, "nbd-device", &tmp);
641 if (!jrc) {
642 warnx("config %s doesn't specify a nbd-device", name);
643 return -1;
644 }
645
646 if (!json_object_is_type(tmp, json_type_string)) {
647 warnx("config %s has invalid nbd-device", name);
648 return -1;
649 }
650
651 config->nbd_device = strdup(json_object_get_string(tmp));
652 config->name = strdup(name);
653
Jeremy Kerr0f1a49c2018-08-03 15:41:38 +0800654 jrc = json_object_object_get_ex(obj, "default", &tmp);
655 config->is_default = jrc && json_object_get_boolean(tmp);
656
Jeremy Kerr19527352018-08-03 15:04:38 +0800657 jrc = json_object_object_get_ex(obj, "metadata", &meta);
658 if (jrc && json_object_is_type(meta, json_type_object))
659 config->metadata = json_object_get(meta);
660 else
661 config->metadata = NULL;
662
663 return 0;
664}
665
666static void config_free(struct ctx *ctx)
667{
668 int i;
669
670 for (i = 0; i < ctx->n_configs; i++)
671 config_free_one(&ctx->configs[i]);
672
673 free(ctx->configs);
674 ctx->n_configs = 0;
675}
676
677static int config_init(struct ctx *ctx)
678{
679 struct json_object *obj, *tmp;
680 json_bool jrc;
681 int i, rc;
682
683 /* apply defaults */
684 ctx->nbd_timeout = nbd_timeout_default;
685
686 obj = json_object_from_file(conf_path);
687 if (!obj) {
688 warnx("can't read configuration from %s\n", conf_path);
689 return -1;
690 }
691
692 /* global configuration */
693 jrc = json_object_object_get_ex(obj, "timeout", &tmp);
694 if (jrc) {
695 errno = 0;
696 ctx->nbd_timeout = json_object_get_int(tmp);
697 if (ctx->nbd_timeout == 0 && errno) {
698 warnx("can't parse timeout value");
699 goto err_free;
700 }
701 }
702
703 /* per-config configuration */
704 jrc = json_object_object_get_ex(obj, "configurations", &tmp);
705 if (!jrc) {
706 warnx("no configurations specified");
707 goto err_free;
708 }
709
710 if (!json_object_is_type(tmp, json_type_object)) {
711 warnx("invalid configurations format");
712 goto err_free;
713 }
714
715 ctx->n_configs = json_object_object_length(tmp);
716 ctx->configs = calloc(ctx->n_configs, sizeof(*ctx->configs));
717
718 i = 0;
Jeremy Kerr0f1a49c2018-08-03 15:41:38 +0800719 json_object_object_foreach(tmp, name, config_json) {
720 struct config *config = &ctx->configs[i];
721
722 rc = config_parse_one(config, name, config_json);
Jeremy Kerr19527352018-08-03 15:04:38 +0800723 if (rc)
724 goto err_free;
Jeremy Kerr0f1a49c2018-08-03 15:41:38 +0800725
726 if (config->is_default) {
727 if (ctx->default_config) {
728 warn("multiple configs flagged as default");
729 goto err_free;
730 }
731 ctx->default_config = config;
732 }
Jeremy Kerr19527352018-08-03 15:04:38 +0800733 i++;
734 }
735
736 json_object_put(obj);
737
Jeremy Kerr0f1a49c2018-08-03 15:41:38 +0800738 if (ctx->n_configs == 1)
739 ctx->default_config = &ctx->configs[0];
740
Jeremy Kerr19527352018-08-03 15:04:38 +0800741 return 0;
742
743err_free:
744 warnx("failed to load config from %s", conf_path);
745 json_object_put(obj);
746 return -1;
747}
748
749static int config_select(struct ctx *ctx, const char *name)
750{
751 struct config *config;
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800752 struct stat statbuf;
753 int i, rc;
Jeremy Kerr19527352018-08-03 15:04:38 +0800754
755 config = NULL;
756
Jeremy Kerr0f1a49c2018-08-03 15:41:38 +0800757 if (!name) {
758 /* no config specified: use the default */
759 if (!ctx->default_config) {
760 warnx("no config specified, and no default");
761 return -1;
Jeremy Kerr19527352018-08-03 15:04:38 +0800762 }
Jeremy Kerr0f1a49c2018-08-03 15:41:38 +0800763 config = ctx->default_config;
Jeremy Kerr19527352018-08-03 15:04:38 +0800764
Jeremy Kerr0f1a49c2018-08-03 15:41:38 +0800765 } else {
766 /* find a matching config... */
767 for (i = 0; i < ctx->n_configs; i++) {
768 if (!strcmp(ctx->configs[i].name, name)) {
769 config = &ctx->configs[i];
770 break;
771 }
772 }
773
774 if (!config) {
775 warnx("no such configuration '%s'", name);
776 return -1;
777 }
Jeremy Kerr19527352018-08-03 15:04:38 +0800778 }
779
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800780 /* check that the device exists */
781 rc = stat(config->nbd_device, &statbuf);
782 if (rc) {
783 warn("can't stat nbd device %s", config->nbd_device);
784 return -1;
785 }
786
787 if (!S_ISBLK(statbuf.st_mode)) {
788 warn("specified nbd path %s isn't a block device",
789 config->nbd_device);
790 return -1;
791 }
792
Jeremy Kerr19527352018-08-03 15:04:38 +0800793 /* ... and apply it */
Jeremy Kerrc6134c12018-08-09 13:03:33 +0800794 ctx->config = config;
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800795 ctx->nbd_devno = statbuf.st_rdev;
796
Jeremy Kerr19527352018-08-03 15:04:38 +0800797 return 0;
798}
799
Jeremy Kerr6f9c4332018-08-03 17:11:15 +0800800static const struct option options[] = {
Jeremy Kerr13bb28f2018-08-09 10:41:10 +0800801 { .name = "help", .val = 'h' },
802 { .name = "metadata", .val = 'm' },
Jeremy Kerr6f9c4332018-08-03 17:11:15 +0800803 { 0 },
804};
805
Jeremy Kerr13bb28f2018-08-09 10:41:10 +0800806enum action {
807 ACTION_PROXY,
808 ACTION_METADATA,
809};
810
Jeremy Kerr6f9c4332018-08-03 17:11:15 +0800811static void print_usage(const char *progname)
812{
Jeremy Kerr13bb28f2018-08-09 10:41:10 +0800813 fprintf(stderr, "usage:\n");
814 fprintf(stderr, "\t%s [configuration]\n", progname);
815 fprintf(stderr, "\t%s --metadata\n", progname);
Jeremy Kerr6f9c4332018-08-03 17:11:15 +0800816}
817
Jeremy Kerr19527352018-08-03 15:04:38 +0800818int main(int argc, char **argv)
819{
Jeremy Kerr13bb28f2018-08-09 10:41:10 +0800820 enum action action = ACTION_PROXY;
Jeremy Kerr19527352018-08-03 15:04:38 +0800821 const char *config_name;
Jeremy Kerrf403c422018-07-26 12:14:56 +0800822 struct ctx _ctx, *ctx;
823 int rc;
824
Jeremy Kerr0f1a49c2018-08-03 15:41:38 +0800825 config_name = NULL;
Jeremy Kerr19527352018-08-03 15:04:38 +0800826
Jeremy Kerr6f9c4332018-08-03 17:11:15 +0800827 for (;;) {
828 int c = getopt_long(argc, argv, "h", options, NULL);
829 if (c == -1)
830 break;
831
832 switch (c) {
Jeremy Kerr13bb28f2018-08-09 10:41:10 +0800833 case 'm':
834 action = ACTION_METADATA;
835 break;
Jeremy Kerr6f9c4332018-08-03 17:11:15 +0800836 case 'h':
837 case '?':
838 print_usage(argv[0]);
839 return c == 'h' ? EXIT_SUCCESS : EXIT_FAILURE;
840 }
841 }
842
843 if (optind < argc)
844 config_name = argv[optind];
Jeremy Kerr19527352018-08-03 15:04:38 +0800845
Jeremy Kerrf403c422018-07-26 12:14:56 +0800846 ctx = &_ctx;
Jeremy Kerr19527352018-08-03 15:04:38 +0800847 memset(ctx, 0, sizeof(*ctx));
Jeremy Kerrf403c422018-07-26 12:14:56 +0800848 ctx->bufsize = bufsize;
849 ctx->buf = malloc(ctx->bufsize);
Jeremy Kerr19527352018-08-03 15:04:38 +0800850
851 rc = config_init(ctx);
852 if (rc)
853 goto out_free;
854
Jeremy Kerr13bb28f2018-08-09 10:41:10 +0800855 if (action == ACTION_METADATA) {
856 print_metadata(ctx);
857 goto out_free;
858 }
859
Jeremy Kerr19527352018-08-03 15:04:38 +0800860 rc = config_select(ctx, config_name);
861 if (rc)
862 goto out_free;
Jeremy Kerrf403c422018-07-26 12:14:56 +0800863
864 rc = open_nbd_socket(ctx);
865 if (rc)
866 goto out_free;
867
868 rc = setup_signals(ctx);
869 if (rc)
870 goto out_close;
871
872 rc = start_nbd_client(ctx);
873 if (rc)
874 goto out_stop_client;
875
876 rc = wait_for_nbd_client(ctx);
877 if (rc)
878 goto out_stop_client;
879
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800880 rc = udev_init(ctx);
Jeremy Kerrc6134c12018-08-09 13:03:33 +0800881 if (rc)
882 goto out_stop_client;
883
Jeremy Kerrf403c422018-07-26 12:14:56 +0800884 rc = run_proxy(ctx);
885
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800886 if (ctx->udev)
887 udev_free(ctx);
888
Jeremy Kerrc6134c12018-08-09 13:03:33 +0800889 run_state_hooks(ctx, "stop");
890
Jeremy Kerrf403c422018-07-26 12:14:56 +0800891out_stop_client:
892 /* we cleanup signals before stopping the client, because we
893 * no longer care about SIGCHLD from the stopping nbd-client
894 * process. stop_nbd_client will be a no-op if the client hasn't
895 * been started. */
896 cleanup_signals(ctx);
897
898 stop_nbd_client(ctx);
899 close(ctx->sock_client);
900
901out_close:
902 if (ctx->sock_path) {
903 unlink(ctx->sock_path);
904 free(ctx->sock_path);
905 }
906 close(ctx->sock);
907out_free:
Jeremy Kerr19527352018-08-03 15:04:38 +0800908 config_free(ctx);
Jeremy Kerrf403c422018-07-26 12:14:56 +0800909 free(ctx->buf);
910 return rc ? EXIT_FAILURE : EXIT_SUCCESS;
911}