blob: 2db70df20b50d793b58d396026b2c1baf6b93d1a [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 Kerra87af842018-08-13 11:48:23 +080072static const char *state_hook_path = SYSCONFDIR "/nbd-proxy/state";
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
78static int open_nbd_socket(struct ctx *ctx)
79{
80 struct sockaddr_un addr;
81 char *path;
82 int sd, rc;
83
84 rc = asprintf(&path, sockpath_tmpl, getpid());
85 if (rc < 0)
86 return -1;
87
Jeremy Kerr4eeb9ed2018-08-09 13:32:19 +080088 sd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
Jeremy Kerrf403c422018-07-26 12:14:56 +080089 if (sd < 0) {
90 warn("can't create socket");
91 goto err_free;
92 }
93
94 rc = fchmod(sd, S_IRUSR | S_IWUSR);
95 if (rc) {
96 warn("can't set permissions on socket");
97 goto err_close;
98 }
99
100 addr.sun_family = AF_UNIX;
101 strncpy(addr.sun_path, path, sizeof(addr.sun_path));
102
103 rc = bind(sd, (struct sockaddr *)&addr, sizeof(addr));
104 if (rc) {
105 warn("can't bind to path %s", path);
106 goto err_close;
107 }
108
109 rc = listen(sd, 1);
110 if (rc) {
111 warn("can't listen on socket %s", path);
112 goto err_unlink;
113 }
114
115 ctx->sock = sd;
116 ctx->sock_path = path;
117 return 0;
118
119err_unlink:
120 unlink(path);
121err_close:
122 close(sd);
123err_free:
124 free(path);
125 return -1;
126}
127
128static int start_nbd_client(struct ctx *ctx)
129{
130 pid_t pid;
131
132 pid = fork();
133 if (pid < 0) {
134 warn("can't create client process");
135 return -1;
136 }
137
138 /* child process: run nbd-client in non-fork mode */
139 if (pid == 0) {
Jeremy Kerr09295f22018-07-27 16:29:38 +0800140 char timeout_str[10];
Jeremy Kerrf403c422018-07-26 12:14:56 +0800141 int fd;
142
Jeremy Kerr19527352018-08-03 15:04:38 +0800143 snprintf(timeout_str, sizeof(timeout_str),
144 "%d", ctx->nbd_timeout);
Jeremy Kerr09295f22018-07-27 16:29:38 +0800145
Jeremy Kerr4eeb9ed2018-08-09 13:32:19 +0800146 fd = open("/dev/null", O_RDWR | O_CLOEXEC);
Jeremy Kerrf403c422018-07-26 12:14:56 +0800147 if (fd < 0)
148 err(EXIT_FAILURE, "can't open /dev/null");
149
150 dup2(fd, STDIN_FILENO);
151 dup2(fd, STDOUT_FILENO);
152 dup2(fd, STDERR_FILENO);
153 close(fd);
Jeremy Kerrf403c422018-07-26 12:14:56 +0800154
155 execlp("nbd-client", "nbd-client",
156 "-u", ctx->sock_path,
157 "-n",
Jeremy Kerr09295f22018-07-27 16:29:38 +0800158 "-t", timeout_str,
Jeremy Kerrc6134c12018-08-09 13:03:33 +0800159 ctx->config->nbd_device,
Jeremy Kerrf403c422018-07-26 12:14:56 +0800160 NULL);
161 err(EXIT_FAILURE, "can't start ndb client");
162 }
163
164 ctx->nbd_client_pid = pid;
165 return 0;
166}
167
168static void stop_nbd_client(struct ctx *ctx)
169{
170 int rc;
171
172 if (!ctx->nbd_client_pid)
173 return;
174
175 rc = kill(ctx->nbd_client_pid, SIGTERM);
176 if (rc)
177 return;
178
179 waitpid(ctx->nbd_client_pid, NULL, 0);
180 ctx->nbd_client_pid = 0;
181}
182
183static int copy_fd(struct ctx *ctx, int fd_in, int fd_out)
184{
185#ifdef HAVE_SPLICE
186 int rc;
187
188 rc = splice(fd_in, NULL, fd_out, NULL, ctx->bufsize, 0);
189 if (rc < 0)
190 warn("splice");
191
192 return rc;
193#else
194 size_t len, pos;
195 ssize_t rc;
196
197 for (;;) {
198 errno = 0;
199 rc = read(fd_in, ctx->buf, ctx->bufsize);
200 if (rc < 0) {
201 if (errno == EINTR)
202 continue;
203 warn("read failure");
204 return -1;
205 }
206 if (rc == 0)
207 return 0;
208 break;
209 }
210
211 len = rc;
212
213 for (pos = 0; pos < len;) {
214 errno = 0;
215 rc = write(fd_out, ctx->buf + pos, len - pos);
216 if (rc < 0) {
217 if (errno == EINTR)
218 continue;
219 warn("write failure");
220 return -1;
221 }
222 if (rc == 0)
223 break;
224 pos += rc;
225 }
226
227 return pos;
228#endif
229}
230
231static int signal_pipe_fd = -1;
232
233static void signal_handler(int signal)
234{
235 int rc;
236
237 rc = write(signal_pipe_fd, &signal, sizeof(signal));
238
239 /* not a lot we can do here but exit... */
240 if (rc != sizeof(signal))
241 exit(EXIT_FAILURE);
242}
243
244static int setup_signals(struct ctx *ctx)
245{
246 struct sigaction sa;
247 int rc;
248
Jeremy Kerr4eeb9ed2018-08-09 13:32:19 +0800249 rc = pipe2(ctx->signal_pipe, O_CLOEXEC);
Jeremy Kerrf403c422018-07-26 12:14:56 +0800250 if (rc) {
251 warn("cant setup signal pipe");
252 return -1;
253 }
254
255 signal_pipe_fd = ctx->signal_pipe[1];
256
257 memset(&sa, 0, sizeof(sa));
258 sa.sa_handler = signal_handler;
259
260 sigaction(SIGINT, &sa, NULL);
261 sigaction(SIGTERM, &sa, NULL);
262 sigaction(SIGCHLD, &sa, NULL);
263
264 return 0;
265}
266
267static void cleanup_signals(struct ctx *ctx)
268{
269 struct sigaction sa;
270 memset(&sa, 0, sizeof(sa));
271 sa.sa_handler = SIG_DFL;
272
273 sigaction(SIGINT, &sa, NULL);
274 sigaction(SIGTERM, &sa, NULL);
275 sigaction(SIGCHLD, &sa, NULL);
276
277 close(ctx->signal_pipe[0]);
278 close(ctx->signal_pipe[1]);
279}
280
281static int process_signal_pipe(struct ctx *ctx, bool *exit)
282{
283 int buf, rc, status;
284
285 rc = read(ctx->signal_pipe[0], &buf, sizeof(buf));
286 if (rc != sizeof(buf))
287 return -1;
288
289 *exit = false;
290
291 switch (buf) {
292 case SIGCHLD:
293 rc = waitpid(ctx->nbd_client_pid, &status, WNOHANG);
294 if (rc > 0) {
295 warnx("nbd client stopped (%s: %d); exiting",
296 WIFEXITED(status) ? "rc" : "sig",
297 WIFEXITED(status) ?
298 WEXITSTATUS(status) :
299 WTERMSIG(status));
300 ctx->nbd_client_pid = 0;
301 }
302 break;
303 case SIGINT:
304 case SIGTERM:
305 *exit = true;
306 break;
307 }
308
309 return 0;
310}
311
312static int wait_for_nbd_client(struct ctx *ctx)
313{
314 struct pollfd pollfds[2];
315 int rc;
316
317 pollfds[0].fd = ctx->sock;
318 pollfds[0].events = POLLIN;
319 pollfds[1].fd = ctx->signal_pipe[0];
320 pollfds[1].events = POLLIN;
321
322 for (;;) {
323 errno = 0;
324 rc = poll(pollfds, 2, -1);
325 if (rc < 0) {
326 if (errno == EINTR)
327 continue;
328 warn("poll failed");
329 return -1;
330 }
331
332 if (pollfds[0].revents) {
Jeremy Kerr4eeb9ed2018-08-09 13:32:19 +0800333 rc = accept4(ctx->sock, NULL, NULL, SOCK_CLOEXEC);
Jeremy Kerrf403c422018-07-26 12:14:56 +0800334 if (rc < 0) {
335 warn("can't create connection");
336 return -1;
337 }
338 ctx->sock_client = rc;
339 break;
340 }
341
342 if (pollfds[1].revents) {
343 bool exit;
344 rc = process_signal_pipe(ctx, &exit);
345 if (rc || exit)
346 return -1;
347 }
348 }
349
350 return 0;
351}
352
Jeremy Kerra87af842018-08-13 11:48:23 +0800353static int run_state_hook(struct ctx *ctx, const char *action)
Jeremy Kerrc6134c12018-08-09 13:03:33 +0800354{
355 int status, rc, fd;
356 pid_t pid;
357
Jeremy Kerra87af842018-08-13 11:48:23 +0800358 /* if the hook isn't present or executable, that's not necessarily
359 * an error condition */
360 if (!access(state_hook_path, X_OK))
361 return 0;
362
Jeremy Kerrc6134c12018-08-09 13:03:33 +0800363 pid = fork();
364 if (pid < 0) {
Jeremy Kerra87af842018-08-13 11:48:23 +0800365 warn("can't fork to execute hook %s", state_hook_path);
Jeremy Kerrc6134c12018-08-09 13:03:33 +0800366 return -1;
367 }
368
369 if (!pid) {
Jeremy Kerra87af842018-08-13 11:48:23 +0800370 const char *argv0;
371
372 argv0 = strchr(state_hook_path, '/');
373 if (!argv0)
374 argv0 = state_hook_path;
375
Jeremy Kerr4eeb9ed2018-08-09 13:32:19 +0800376 fd = open("/dev/null", O_RDWR | O_CLOEXEC);
Jeremy Kerrc6134c12018-08-09 13:03:33 +0800377 if (fd < 0)
378 exit(EXIT_FAILURE);
379
380 dup2(fd, STDIN_FILENO);
381 dup2(fd, STDOUT_FILENO);
382 dup2(fd, STDERR_FILENO);
Jeremy Kerra87af842018-08-13 11:48:23 +0800383 execl(state_hook_path, argv0, action, ctx->config->name, NULL);
Jeremy Kerrc6134c12018-08-09 13:03:33 +0800384 exit(EXIT_FAILURE);
385 }
386
387 rc = waitpid(pid, &status, 0);
388 if (rc < 0) {
389 warn("wait");
390 return -1;
391 }
392
393 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
Jeremy Kerra87af842018-08-13 11:48:23 +0800394 warnx("hook %s failed", state_hook_path);
Jeremy Kerrc6134c12018-08-09 13:03:33 +0800395 return -1;
396 }
397
398 return 0;
399}
400
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800401static int udev_init(struct ctx *ctx)
402{
403 int rc;
404
405 ctx->udev = udev_new();
406 if (!ctx) {
407 warn("can't create udev object");
408 return -1;
409 }
410
411 ctx->monitor = udev_monitor_new_from_netlink(ctx->udev, "kernel");
412 if (!ctx->monitor) {
413 warn("can't create udev monitor");
414 goto out_unref_udev;
415 }
416
417 rc = udev_monitor_filter_add_match_subsystem_devtype(
418 ctx->monitor, "block", "disk");
419 if (rc) {
420 warn("can't create udev monitor filter");
421 goto out_unref_monitor;
422 }
423
424 rc = udev_monitor_enable_receiving(ctx->monitor);
425 if (rc) {
426 warn("can't start udev monitor");
427 goto out_unref_monitor;
428 }
429
430 return 0;
431
432out_unref_monitor:
433 udev_monitor_unref(ctx->monitor);
434out_unref_udev:
435 udev_unref(ctx->udev);
436 return -1;
437}
438
439static void udev_free(struct ctx *ctx)
440{
441 udev_monitor_unref(ctx->monitor);
442 udev_unref(ctx->udev);
443}
444
445/* Check for the change event on our nbd device, signifying that the kernel
446 * has finished initialising the block device. Once we see the event, we run
447 * the "start" state hook, and close the udev monitor.
448 *
449 * Returns:
450 * 0 if no processing was performed
451 * -1 on state hook error (and the nbd session should be closed)
452 */
453static int udev_process(struct ctx *ctx)
454{
455 struct udev_device *dev;
456 bool action_is_change;
457 dev_t devno;
458 int rc;
459
460 dev = udev_monitor_receive_device(ctx->monitor);
461 if (!dev)
462 return 0;
463
464 devno = udev_device_get_devnum(dev);
465 action_is_change = !strcmp(udev_device_get_action(dev), "change");
466 udev_device_unref(dev);
467
468 if (devno != ctx->nbd_devno)
469 return 0;
470
471 if (!action_is_change)
472 return 0;
473
474 udev_monitor_unref(ctx->monitor);
475 udev_unref(ctx->udev);
476 ctx->monitor = NULL;
477 ctx->udev = NULL;
478
Jeremy Kerra87af842018-08-13 11:48:23 +0800479 rc = run_state_hook(ctx, "start");
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800480
481 return rc;
482}
483
Jeremy Kerrf403c422018-07-26 12:14:56 +0800484static int run_proxy(struct ctx *ctx)
485{
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800486 struct pollfd pollfds[4];
Jeremy Kerrf403c422018-07-26 12:14:56 +0800487 bool exit = false;
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800488 int rc, n_fd;
Jeremy Kerrf403c422018-07-26 12:14:56 +0800489
490 /* main proxy: forward data between stdio & socket */
491 pollfds[0].fd = ctx->sock_client;
492 pollfds[0].events = POLLIN;
493 pollfds[1].fd = STDIN_FILENO;
494 pollfds[1].events = POLLIN;
495 pollfds[2].fd = ctx->signal_pipe[0];
496 pollfds[2].events = POLLIN;
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800497 pollfds[3].fd = udev_monitor_get_fd(ctx->monitor);
498 pollfds[3].events = POLLIN;
499
500 n_fd = 4;
Jeremy Kerrf403c422018-07-26 12:14:56 +0800501
502 for (;;) {
503 errno = 0;
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800504 rc = poll(pollfds, n_fd, -1);
Jeremy Kerrf403c422018-07-26 12:14:56 +0800505 if (rc < 0) {
506 if (errno == EINTR)
507 continue;
508 warn("poll failed");
509 break;
510 }
511
512 if (pollfds[0].revents) {
513 rc = copy_fd(ctx, ctx->sock_client, STDOUT_FILENO);
514 if (rc <= 0)
515 break;
516 }
517
518 if (pollfds[1].revents) {
519 rc = copy_fd(ctx, STDIN_FILENO, ctx->sock_client);
520 if (rc <= 0)
521 break;
522 }
523
524 if (pollfds[2].revents) {
525 rc = process_signal_pipe(ctx, &exit);
526 if (rc || exit)
527 break;
528 }
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800529
530 if (pollfds[3].revents) {
531 rc = udev_process(ctx);
532 if (rc)
533 break;
534
535 /* udev_process may have closed the udev connection,
536 * in which case we can stop polling on its fd */
537 if (!ctx->udev) {
538 pollfds[3].fd = 0;
539 pollfds[3].revents = 0;
540 n_fd = 3;
541 }
542 }
Jeremy Kerrf403c422018-07-26 12:14:56 +0800543 }
544
545 return rc ? -1 : 0;
546}
547
Jeremy Kerr13bb28f2018-08-09 10:41:10 +0800548static void print_metadata(struct ctx *ctx)
549{
550 struct json_object *md;
551 int i;
552
553 md = json_object_new_object();
554
555 for (i = 0; i < ctx->n_configs; i++) {
556 struct config *config = &ctx->configs[i];
557 json_object_object_add(md, config->name,
558 config->metadata);
559 }
560
561 puts(json_object_get_string(md));
562
563 json_object_put(md);
564}
565
Jeremy Kerr19527352018-08-03 15:04:38 +0800566static void config_free_one(struct config *config)
Jeremy Kerrf403c422018-07-26 12:14:56 +0800567{
Jeremy Kerr19527352018-08-03 15:04:38 +0800568 if (config->metadata)
569 json_object_put(config->metadata);
570 free(config->nbd_device);
571 free(config->name);
572}
573
574static int config_parse_one(struct config *config, const char *name,
575 json_object *obj)
576{
577 struct json_object *tmp, *meta;
578 json_bool jrc;
579
580 jrc = json_object_object_get_ex(obj, "nbd-device", &tmp);
581 if (!jrc) {
582 warnx("config %s doesn't specify a nbd-device", name);
583 return -1;
584 }
585
586 if (!json_object_is_type(tmp, json_type_string)) {
587 warnx("config %s has invalid nbd-device", name);
588 return -1;
589 }
590
591 config->nbd_device = strdup(json_object_get_string(tmp));
592 config->name = strdup(name);
593
Jeremy Kerr0f1a49c2018-08-03 15:41:38 +0800594 jrc = json_object_object_get_ex(obj, "default", &tmp);
595 config->is_default = jrc && json_object_get_boolean(tmp);
596
Jeremy Kerr19527352018-08-03 15:04:38 +0800597 jrc = json_object_object_get_ex(obj, "metadata", &meta);
598 if (jrc && json_object_is_type(meta, json_type_object))
599 config->metadata = json_object_get(meta);
600 else
601 config->metadata = NULL;
602
603 return 0;
604}
605
606static void config_free(struct ctx *ctx)
607{
608 int i;
609
610 for (i = 0; i < ctx->n_configs; i++)
611 config_free_one(&ctx->configs[i]);
612
613 free(ctx->configs);
614 ctx->n_configs = 0;
615}
616
617static int config_init(struct ctx *ctx)
618{
619 struct json_object *obj, *tmp;
620 json_bool jrc;
621 int i, rc;
622
623 /* apply defaults */
624 ctx->nbd_timeout = nbd_timeout_default;
625
626 obj = json_object_from_file(conf_path);
627 if (!obj) {
628 warnx("can't read configuration from %s\n", conf_path);
629 return -1;
630 }
631
632 /* global configuration */
633 jrc = json_object_object_get_ex(obj, "timeout", &tmp);
634 if (jrc) {
635 errno = 0;
636 ctx->nbd_timeout = json_object_get_int(tmp);
637 if (ctx->nbd_timeout == 0 && errno) {
638 warnx("can't parse timeout value");
639 goto err_free;
640 }
641 }
642
643 /* per-config configuration */
644 jrc = json_object_object_get_ex(obj, "configurations", &tmp);
645 if (!jrc) {
646 warnx("no configurations specified");
647 goto err_free;
648 }
649
650 if (!json_object_is_type(tmp, json_type_object)) {
651 warnx("invalid configurations format");
652 goto err_free;
653 }
654
655 ctx->n_configs = json_object_object_length(tmp);
656 ctx->configs = calloc(ctx->n_configs, sizeof(*ctx->configs));
657
658 i = 0;
Jeremy Kerr0f1a49c2018-08-03 15:41:38 +0800659 json_object_object_foreach(tmp, name, config_json) {
660 struct config *config = &ctx->configs[i];
661
662 rc = config_parse_one(config, name, config_json);
Jeremy Kerr19527352018-08-03 15:04:38 +0800663 if (rc)
664 goto err_free;
Jeremy Kerr0f1a49c2018-08-03 15:41:38 +0800665
666 if (config->is_default) {
667 if (ctx->default_config) {
668 warn("multiple configs flagged as default");
669 goto err_free;
670 }
671 ctx->default_config = config;
672 }
Jeremy Kerr19527352018-08-03 15:04:38 +0800673 i++;
674 }
675
676 json_object_put(obj);
677
Jeremy Kerr0f1a49c2018-08-03 15:41:38 +0800678 if (ctx->n_configs == 1)
679 ctx->default_config = &ctx->configs[0];
680
Jeremy Kerr19527352018-08-03 15:04:38 +0800681 return 0;
682
683err_free:
684 warnx("failed to load config from %s", conf_path);
685 json_object_put(obj);
686 return -1;
687}
688
689static int config_select(struct ctx *ctx, const char *name)
690{
691 struct config *config;
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800692 struct stat statbuf;
693 int i, rc;
Jeremy Kerr19527352018-08-03 15:04:38 +0800694
695 config = NULL;
696
Jeremy Kerr0f1a49c2018-08-03 15:41:38 +0800697 if (!name) {
698 /* no config specified: use the default */
699 if (!ctx->default_config) {
700 warnx("no config specified, and no default");
701 return -1;
Jeremy Kerr19527352018-08-03 15:04:38 +0800702 }
Jeremy Kerr0f1a49c2018-08-03 15:41:38 +0800703 config = ctx->default_config;
Jeremy Kerr19527352018-08-03 15:04:38 +0800704
Jeremy Kerr0f1a49c2018-08-03 15:41:38 +0800705 } else {
706 /* find a matching config... */
707 for (i = 0; i < ctx->n_configs; i++) {
708 if (!strcmp(ctx->configs[i].name, name)) {
709 config = &ctx->configs[i];
710 break;
711 }
712 }
713
714 if (!config) {
715 warnx("no such configuration '%s'", name);
716 return -1;
717 }
Jeremy Kerr19527352018-08-03 15:04:38 +0800718 }
719
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800720 /* check that the device exists */
721 rc = stat(config->nbd_device, &statbuf);
722 if (rc) {
723 warn("can't stat nbd device %s", config->nbd_device);
724 return -1;
725 }
726
727 if (!S_ISBLK(statbuf.st_mode)) {
728 warn("specified nbd path %s isn't a block device",
729 config->nbd_device);
730 return -1;
731 }
732
Jeremy Kerr19527352018-08-03 15:04:38 +0800733 /* ... and apply it */
Jeremy Kerrc6134c12018-08-09 13:03:33 +0800734 ctx->config = config;
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800735 ctx->nbd_devno = statbuf.st_rdev;
736
Jeremy Kerr19527352018-08-03 15:04:38 +0800737 return 0;
738}
739
Jeremy Kerr6f9c4332018-08-03 17:11:15 +0800740static const struct option options[] = {
Jeremy Kerr13bb28f2018-08-09 10:41:10 +0800741 { .name = "help", .val = 'h' },
742 { .name = "metadata", .val = 'm' },
Jeremy Kerr6f9c4332018-08-03 17:11:15 +0800743 { 0 },
744};
745
Jeremy Kerr13bb28f2018-08-09 10:41:10 +0800746enum action {
747 ACTION_PROXY,
748 ACTION_METADATA,
749};
750
Jeremy Kerr6f9c4332018-08-03 17:11:15 +0800751static void print_usage(const char *progname)
752{
Jeremy Kerr13bb28f2018-08-09 10:41:10 +0800753 fprintf(stderr, "usage:\n");
754 fprintf(stderr, "\t%s [configuration]\n", progname);
755 fprintf(stderr, "\t%s --metadata\n", progname);
Jeremy Kerr6f9c4332018-08-03 17:11:15 +0800756}
757
Jeremy Kerr19527352018-08-03 15:04:38 +0800758int main(int argc, char **argv)
759{
Jeremy Kerr13bb28f2018-08-09 10:41:10 +0800760 enum action action = ACTION_PROXY;
Jeremy Kerr19527352018-08-03 15:04:38 +0800761 const char *config_name;
Jeremy Kerrf403c422018-07-26 12:14:56 +0800762 struct ctx _ctx, *ctx;
763 int rc;
764
Jeremy Kerr0f1a49c2018-08-03 15:41:38 +0800765 config_name = NULL;
Jeremy Kerr19527352018-08-03 15:04:38 +0800766
Jeremy Kerr6f9c4332018-08-03 17:11:15 +0800767 for (;;) {
768 int c = getopt_long(argc, argv, "h", options, NULL);
769 if (c == -1)
770 break;
771
772 switch (c) {
Jeremy Kerr13bb28f2018-08-09 10:41:10 +0800773 case 'm':
774 action = ACTION_METADATA;
775 break;
Jeremy Kerr6f9c4332018-08-03 17:11:15 +0800776 case 'h':
777 case '?':
778 print_usage(argv[0]);
779 return c == 'h' ? EXIT_SUCCESS : EXIT_FAILURE;
780 }
781 }
782
783 if (optind < argc)
784 config_name = argv[optind];
Jeremy Kerr19527352018-08-03 15:04:38 +0800785
Jeremy Kerrf403c422018-07-26 12:14:56 +0800786 ctx = &_ctx;
Jeremy Kerr19527352018-08-03 15:04:38 +0800787 memset(ctx, 0, sizeof(*ctx));
Jeremy Kerrf403c422018-07-26 12:14:56 +0800788 ctx->bufsize = bufsize;
789 ctx->buf = malloc(ctx->bufsize);
Jeremy Kerr19527352018-08-03 15:04:38 +0800790
791 rc = config_init(ctx);
792 if (rc)
793 goto out_free;
794
Jeremy Kerr13bb28f2018-08-09 10:41:10 +0800795 if (action == ACTION_METADATA) {
796 print_metadata(ctx);
797 goto out_free;
798 }
799
Jeremy Kerr19527352018-08-03 15:04:38 +0800800 rc = config_select(ctx, config_name);
801 if (rc)
802 goto out_free;
Jeremy Kerrf403c422018-07-26 12:14:56 +0800803
804 rc = open_nbd_socket(ctx);
805 if (rc)
806 goto out_free;
807
808 rc = setup_signals(ctx);
809 if (rc)
810 goto out_close;
811
812 rc = start_nbd_client(ctx);
813 if (rc)
814 goto out_stop_client;
815
816 rc = wait_for_nbd_client(ctx);
817 if (rc)
818 goto out_stop_client;
819
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800820 rc = udev_init(ctx);
Jeremy Kerrc6134c12018-08-09 13:03:33 +0800821 if (rc)
822 goto out_stop_client;
823
Jeremy Kerrf403c422018-07-26 12:14:56 +0800824 rc = run_proxy(ctx);
825
Jeremy Kerr4ac92462018-08-10 11:16:29 +0800826 if (ctx->udev)
827 udev_free(ctx);
828
Jeremy Kerra87af842018-08-13 11:48:23 +0800829 run_state_hook(ctx, "stop");
Jeremy Kerrc6134c12018-08-09 13:03:33 +0800830
Jeremy Kerrf403c422018-07-26 12:14:56 +0800831out_stop_client:
832 /* we cleanup signals before stopping the client, because we
833 * no longer care about SIGCHLD from the stopping nbd-client
834 * process. stop_nbd_client will be a no-op if the client hasn't
835 * been started. */
836 cleanup_signals(ctx);
837
838 stop_nbd_client(ctx);
839 close(ctx->sock_client);
840
841out_close:
842 if (ctx->sock_path) {
843 unlink(ctx->sock_path);
844 free(ctx->sock_path);
845 }
846 close(ctx->sock);
847out_free:
Jeremy Kerr19527352018-08-03 15:04:38 +0800848 config_free(ctx);
Jeremy Kerrf403c422018-07-26 12:14:56 +0800849 free(ctx->buf);
850 return rc ? EXIT_FAILURE : EXIT_SUCCESS;
851}