blob: 6ecc12053495162731c0bce963bdcad06cd1db0e [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>
42
Jeremy Kerrf403c422018-07-26 12:14:56 +080043#include "config.h"
44
Jeremy Kerr19527352018-08-03 15:04:38 +080045struct config {
46 char *name;
Jeremy Kerr0f1a49c2018-08-03 15:41:38 +080047 bool is_default;
Jeremy Kerr19527352018-08-03 15:04:38 +080048 char *nbd_device;
49 struct json_object *metadata;
50};
51
Jeremy Kerrf403c422018-07-26 12:14:56 +080052struct ctx {
53 int sock;
54 int sock_client;
55 int signal_pipe[2];
56 char *sock_path;
57 pid_t nbd_client_pid;
Jeremy Kerr19527352018-08-03 15:04:38 +080058 int nbd_timeout;
Jeremy Kerrf403c422018-07-26 12:14:56 +080059 uint8_t *buf;
60 size_t bufsize;
Jeremy Kerr19527352018-08-03 15:04:38 +080061 struct config *configs;
62 int n_configs;
Jeremy Kerr0f1a49c2018-08-03 15:41:38 +080063 struct config *default_config;
Jeremy Kerrc6134c12018-08-09 13:03:33 +080064 struct config *config;
Jeremy Kerrf403c422018-07-26 12:14:56 +080065};
66
Jeremy Kerr19527352018-08-03 15:04:38 +080067static const char *conf_path = SYSCONFDIR "/nbd-proxy/config.json";
Jeremy Kerrc6134c12018-08-09 13:03:33 +080068static const char *state_hook_path = SYSCONFDIR "/nbd-proxy/state.d";
Jeremy Kerrf403c422018-07-26 12:14:56 +080069static const char *sockpath_tmpl = RUNSTATEDIR "/nbd.%d.sock";
Jeremy Kerrc6134c12018-08-09 13:03:33 +080070
Jeremy Kerrf403c422018-07-26 12:14:56 +080071static const size_t bufsize = 0x20000;
Jeremy Kerr19527352018-08-03 15:04:38 +080072static const int nbd_timeout_default = 30;
Jeremy Kerrf403c422018-07-26 12:14:56 +080073
Jeremy Kerrc6134c12018-08-09 13:03:33 +080074#define BUILD_ASSERT_OR_ZERO(c) (sizeof(struct {int:-!(c);}))
75
Jeremy Kerrf403c422018-07-26 12:14:56 +080076static int open_nbd_socket(struct ctx *ctx)
77{
78 struct sockaddr_un addr;
79 char *path;
80 int sd, rc;
81
82 rc = asprintf(&path, sockpath_tmpl, getpid());
83 if (rc < 0)
84 return -1;
85
Jeremy Kerr4eeb9ed2018-08-09 13:32:19 +080086 sd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
Jeremy Kerrf403c422018-07-26 12:14:56 +080087 if (sd < 0) {
88 warn("can't create socket");
89 goto err_free;
90 }
91
92 rc = fchmod(sd, S_IRUSR | S_IWUSR);
93 if (rc) {
94 warn("can't set permissions on socket");
95 goto err_close;
96 }
97
98 addr.sun_family = AF_UNIX;
99 strncpy(addr.sun_path, path, sizeof(addr.sun_path));
100
101 rc = bind(sd, (struct sockaddr *)&addr, sizeof(addr));
102 if (rc) {
103 warn("can't bind to path %s", path);
104 goto err_close;
105 }
106
107 rc = listen(sd, 1);
108 if (rc) {
109 warn("can't listen on socket %s", path);
110 goto err_unlink;
111 }
112
113 ctx->sock = sd;
114 ctx->sock_path = path;
115 return 0;
116
117err_unlink:
118 unlink(path);
119err_close:
120 close(sd);
121err_free:
122 free(path);
123 return -1;
124}
125
126static int start_nbd_client(struct ctx *ctx)
127{
128 pid_t pid;
129
130 pid = fork();
131 if (pid < 0) {
132 warn("can't create client process");
133 return -1;
134 }
135
136 /* child process: run nbd-client in non-fork mode */
137 if (pid == 0) {
Jeremy Kerr09295f22018-07-27 16:29:38 +0800138 char timeout_str[10];
Jeremy Kerrf403c422018-07-26 12:14:56 +0800139 int fd;
140
Jeremy Kerr19527352018-08-03 15:04:38 +0800141 snprintf(timeout_str, sizeof(timeout_str),
142 "%d", ctx->nbd_timeout);
Jeremy Kerr09295f22018-07-27 16:29:38 +0800143
Jeremy Kerr4eeb9ed2018-08-09 13:32:19 +0800144 fd = open("/dev/null", O_RDWR | O_CLOEXEC);
Jeremy Kerrf403c422018-07-26 12:14:56 +0800145 if (fd < 0)
146 err(EXIT_FAILURE, "can't open /dev/null");
147
148 dup2(fd, STDIN_FILENO);
149 dup2(fd, STDOUT_FILENO);
150 dup2(fd, STDERR_FILENO);
151 close(fd);
Jeremy Kerrf403c422018-07-26 12:14:56 +0800152
153 execlp("nbd-client", "nbd-client",
154 "-u", ctx->sock_path,
155 "-n",
Jeremy Kerr09295f22018-07-27 16:29:38 +0800156 "-t", timeout_str,
Jeremy Kerrc6134c12018-08-09 13:03:33 +0800157 ctx->config->nbd_device,
Jeremy Kerrf403c422018-07-26 12:14:56 +0800158 NULL);
159 err(EXIT_FAILURE, "can't start ndb client");
160 }
161
162 ctx->nbd_client_pid = pid;
163 return 0;
164}
165
166static void stop_nbd_client(struct ctx *ctx)
167{
168 int rc;
169
170 if (!ctx->nbd_client_pid)
171 return;
172
173 rc = kill(ctx->nbd_client_pid, SIGTERM);
174 if (rc)
175 return;
176
177 waitpid(ctx->nbd_client_pid, NULL, 0);
178 ctx->nbd_client_pid = 0;
179}
180
181static int copy_fd(struct ctx *ctx, int fd_in, int fd_out)
182{
183#ifdef HAVE_SPLICE
184 int rc;
185
186 rc = splice(fd_in, NULL, fd_out, NULL, ctx->bufsize, 0);
187 if (rc < 0)
188 warn("splice");
189
190 return rc;
191#else
192 size_t len, pos;
193 ssize_t rc;
194
195 for (;;) {
196 errno = 0;
197 rc = read(fd_in, ctx->buf, ctx->bufsize);
198 if (rc < 0) {
199 if (errno == EINTR)
200 continue;
201 warn("read failure");
202 return -1;
203 }
204 if (rc == 0)
205 return 0;
206 break;
207 }
208
209 len = rc;
210
211 for (pos = 0; pos < len;) {
212 errno = 0;
213 rc = write(fd_out, ctx->buf + pos, len - pos);
214 if (rc < 0) {
215 if (errno == EINTR)
216 continue;
217 warn("write failure");
218 return -1;
219 }
220 if (rc == 0)
221 break;
222 pos += rc;
223 }
224
225 return pos;
226#endif
227}
228
229static int signal_pipe_fd = -1;
230
231static void signal_handler(int signal)
232{
233 int rc;
234
235 rc = write(signal_pipe_fd, &signal, sizeof(signal));
236
237 /* not a lot we can do here but exit... */
238 if (rc != sizeof(signal))
239 exit(EXIT_FAILURE);
240}
241
242static int setup_signals(struct ctx *ctx)
243{
244 struct sigaction sa;
245 int rc;
246
Jeremy Kerr4eeb9ed2018-08-09 13:32:19 +0800247 rc = pipe2(ctx->signal_pipe, O_CLOEXEC);
Jeremy Kerrf403c422018-07-26 12:14:56 +0800248 if (rc) {
249 warn("cant setup signal pipe");
250 return -1;
251 }
252
253 signal_pipe_fd = ctx->signal_pipe[1];
254
255 memset(&sa, 0, sizeof(sa));
256 sa.sa_handler = signal_handler;
257
258 sigaction(SIGINT, &sa, NULL);
259 sigaction(SIGTERM, &sa, NULL);
260 sigaction(SIGCHLD, &sa, NULL);
261
262 return 0;
263}
264
265static void cleanup_signals(struct ctx *ctx)
266{
267 struct sigaction sa;
268 memset(&sa, 0, sizeof(sa));
269 sa.sa_handler = SIG_DFL;
270
271 sigaction(SIGINT, &sa, NULL);
272 sigaction(SIGTERM, &sa, NULL);
273 sigaction(SIGCHLD, &sa, NULL);
274
275 close(ctx->signal_pipe[0]);
276 close(ctx->signal_pipe[1]);
277}
278
279static int process_signal_pipe(struct ctx *ctx, bool *exit)
280{
281 int buf, rc, status;
282
283 rc = read(ctx->signal_pipe[0], &buf, sizeof(buf));
284 if (rc != sizeof(buf))
285 return -1;
286
287 *exit = false;
288
289 switch (buf) {
290 case SIGCHLD:
291 rc = waitpid(ctx->nbd_client_pid, &status, WNOHANG);
292 if (rc > 0) {
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 }
300 break;
301 case SIGINT:
302 case SIGTERM:
303 *exit = true;
304 break;
305 }
306
307 return 0;
308}
309
310static int wait_for_nbd_client(struct ctx *ctx)
311{
312 struct pollfd pollfds[2];
313 int rc;
314
315 pollfds[0].fd = ctx->sock;
316 pollfds[0].events = POLLIN;
317 pollfds[1].fd = ctx->signal_pipe[0];
318 pollfds[1].events = POLLIN;
319
320 for (;;) {
321 errno = 0;
322 rc = poll(pollfds, 2, -1);
323 if (rc < 0) {
324 if (errno == EINTR)
325 continue;
326 warn("poll failed");
327 return -1;
328 }
329
330 if (pollfds[0].revents) {
Jeremy Kerr4eeb9ed2018-08-09 13:32:19 +0800331 rc = accept4(ctx->sock, NULL, NULL, SOCK_CLOEXEC);
Jeremy Kerrf403c422018-07-26 12:14:56 +0800332 if (rc < 0) {
333 warn("can't create connection");
334 return -1;
335 }
336 ctx->sock_client = rc;
337 break;
338 }
339
340 if (pollfds[1].revents) {
341 bool exit;
342 rc = process_signal_pipe(ctx, &exit);
343 if (rc || exit)
344 return -1;
345 }
346 }
347
348 return 0;
349}
350
Jeremy Kerrc6134c12018-08-09 13:03:33 +0800351#define join_paths(p1, p2, r) \
352 (BUILD_ASSERT_OR_ZERO(sizeof(r) > PATH_MAX) + __join_paths(p1, p2, r))
353static int __join_paths(const char *p1, const char *p2, char res[])
354{
355 size_t len;
356 char *pos;
357
358 len = strlen(p1) + 1 + strlen(p2);
359 if (len > PATH_MAX)
360 return -1;
361
362 pos = res;
363 strcpy(pos, p1);
364 pos += strlen(p1);
365 *pos = '/';
366 pos++;
367 strcpy(pos, p2);
368
369 return 0;
370}
371
372static int run_state_hook(struct ctx *ctx,
373 const char *path, const char *name, const char *action)
374{
375 int status, rc, fd;
376 pid_t pid;
377
378 pid = fork();
379 if (pid < 0) {
380 warn("can't fork to execute hook %s", name);
381 return -1;
382 }
383
384 if (!pid) {
Jeremy Kerr4eeb9ed2018-08-09 13:32:19 +0800385 fd = open("/dev/null", O_RDWR | O_CLOEXEC);
Jeremy Kerrc6134c12018-08-09 13:03:33 +0800386 if (fd < 0)
387 exit(EXIT_FAILURE);
388
389 dup2(fd, STDIN_FILENO);
390 dup2(fd, STDOUT_FILENO);
391 dup2(fd, STDERR_FILENO);
392 execl(path, name, action, ctx->config->name, NULL);
393 exit(EXIT_FAILURE);
394 }
395
396 rc = waitpid(pid, &status, 0);
397 if (rc < 0) {
398 warn("wait");
399 return -1;
400 }
401
402 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
403 warnx("hook %s failed", name);
404 return -1;
405 }
406
407 return 0;
408}
409
410static int run_state_hooks(struct ctx *ctx, const char *action)
411{
412 struct dirent *dirent;
413 int rc;
414 DIR *dir;
415
416 dir = opendir(state_hook_path);
417 if (!dir)
418 return 0;
419
420 rc = 0;
421
422 for (dirent = readdir(dir); dirent; dirent = readdir(dir)) {
423 char full_path[PATH_MAX+1];
424 struct stat statbuf;
425
426 if (dirent->d_name[0] == '.')
427 continue;
428
429 rc = fstatat(dirfd(dir), dirent->d_name, &statbuf, 0);
430 if (rc)
431 continue;
432
433 if (!(S_ISREG(statbuf.st_mode) || S_ISLNK(statbuf.st_mode)))
434 continue;
435
436 if (faccessat(dirfd(dir), dirent->d_name, X_OK, 0))
437 continue;
438
439 rc = join_paths(state_hook_path, dirent->d_name, full_path);
440 if (rc)
441 continue;
442
443 rc = run_state_hook(ctx, full_path, dirent->d_name, action);
444 if (rc)
445 break;
446 }
447
448 closedir(dir);
449
450 return rc;
451}
Jeremy Kerrf403c422018-07-26 12:14:56 +0800452
453static int run_proxy(struct ctx *ctx)
454{
455 struct pollfd pollfds[3];
456 bool exit = false;
457 int rc;
458
459 /* main proxy: forward data between stdio & socket */
460 pollfds[0].fd = ctx->sock_client;
461 pollfds[0].events = POLLIN;
462 pollfds[1].fd = STDIN_FILENO;
463 pollfds[1].events = POLLIN;
464 pollfds[2].fd = ctx->signal_pipe[0];
465 pollfds[2].events = POLLIN;
466
467 for (;;) {
468 errno = 0;
469 rc = poll(pollfds, 3, -1);
470 if (rc < 0) {
471 if (errno == EINTR)
472 continue;
473 warn("poll failed");
474 break;
475 }
476
477 if (pollfds[0].revents) {
478 rc = copy_fd(ctx, ctx->sock_client, STDOUT_FILENO);
479 if (rc <= 0)
480 break;
481 }
482
483 if (pollfds[1].revents) {
484 rc = copy_fd(ctx, STDIN_FILENO, ctx->sock_client);
485 if (rc <= 0)
486 break;
487 }
488
489 if (pollfds[2].revents) {
490 rc = process_signal_pipe(ctx, &exit);
491 if (rc || exit)
492 break;
493 }
494 }
495
496 return rc ? -1 : 0;
497}
498
Jeremy Kerr13bb28f2018-08-09 10:41:10 +0800499static void print_metadata(struct ctx *ctx)
500{
501 struct json_object *md;
502 int i;
503
504 md = json_object_new_object();
505
506 for (i = 0; i < ctx->n_configs; i++) {
507 struct config *config = &ctx->configs[i];
508 json_object_object_add(md, config->name,
509 config->metadata);
510 }
511
512 puts(json_object_get_string(md));
513
514 json_object_put(md);
515}
516
Jeremy Kerr19527352018-08-03 15:04:38 +0800517static void config_free_one(struct config *config)
Jeremy Kerrf403c422018-07-26 12:14:56 +0800518{
Jeremy Kerr19527352018-08-03 15:04:38 +0800519 if (config->metadata)
520 json_object_put(config->metadata);
521 free(config->nbd_device);
522 free(config->name);
523}
524
525static int config_parse_one(struct config *config, const char *name,
526 json_object *obj)
527{
528 struct json_object *tmp, *meta;
529 json_bool jrc;
530
531 jrc = json_object_object_get_ex(obj, "nbd-device", &tmp);
532 if (!jrc) {
533 warnx("config %s doesn't specify a nbd-device", name);
534 return -1;
535 }
536
537 if (!json_object_is_type(tmp, json_type_string)) {
538 warnx("config %s has invalid nbd-device", name);
539 return -1;
540 }
541
542 config->nbd_device = strdup(json_object_get_string(tmp));
543 config->name = strdup(name);
544
Jeremy Kerr0f1a49c2018-08-03 15:41:38 +0800545 jrc = json_object_object_get_ex(obj, "default", &tmp);
546 config->is_default = jrc && json_object_get_boolean(tmp);
547
Jeremy Kerr19527352018-08-03 15:04:38 +0800548 jrc = json_object_object_get_ex(obj, "metadata", &meta);
549 if (jrc && json_object_is_type(meta, json_type_object))
550 config->metadata = json_object_get(meta);
551 else
552 config->metadata = NULL;
553
554 return 0;
555}
556
557static void config_free(struct ctx *ctx)
558{
559 int i;
560
561 for (i = 0; i < ctx->n_configs; i++)
562 config_free_one(&ctx->configs[i]);
563
564 free(ctx->configs);
565 ctx->n_configs = 0;
566}
567
568static int config_init(struct ctx *ctx)
569{
570 struct json_object *obj, *tmp;
571 json_bool jrc;
572 int i, rc;
573
574 /* apply defaults */
575 ctx->nbd_timeout = nbd_timeout_default;
576
577 obj = json_object_from_file(conf_path);
578 if (!obj) {
579 warnx("can't read configuration from %s\n", conf_path);
580 return -1;
581 }
582
583 /* global configuration */
584 jrc = json_object_object_get_ex(obj, "timeout", &tmp);
585 if (jrc) {
586 errno = 0;
587 ctx->nbd_timeout = json_object_get_int(tmp);
588 if (ctx->nbd_timeout == 0 && errno) {
589 warnx("can't parse timeout value");
590 goto err_free;
591 }
592 }
593
594 /* per-config configuration */
595 jrc = json_object_object_get_ex(obj, "configurations", &tmp);
596 if (!jrc) {
597 warnx("no configurations specified");
598 goto err_free;
599 }
600
601 if (!json_object_is_type(tmp, json_type_object)) {
602 warnx("invalid configurations format");
603 goto err_free;
604 }
605
606 ctx->n_configs = json_object_object_length(tmp);
607 ctx->configs = calloc(ctx->n_configs, sizeof(*ctx->configs));
608
609 i = 0;
Jeremy Kerr0f1a49c2018-08-03 15:41:38 +0800610 json_object_object_foreach(tmp, name, config_json) {
611 struct config *config = &ctx->configs[i];
612
613 rc = config_parse_one(config, name, config_json);
Jeremy Kerr19527352018-08-03 15:04:38 +0800614 if (rc)
615 goto err_free;
Jeremy Kerr0f1a49c2018-08-03 15:41:38 +0800616
617 if (config->is_default) {
618 if (ctx->default_config) {
619 warn("multiple configs flagged as default");
620 goto err_free;
621 }
622 ctx->default_config = config;
623 }
Jeremy Kerr19527352018-08-03 15:04:38 +0800624 i++;
625 }
626
627 json_object_put(obj);
628
Jeremy Kerr0f1a49c2018-08-03 15:41:38 +0800629 if (ctx->n_configs == 1)
630 ctx->default_config = &ctx->configs[0];
631
Jeremy Kerr19527352018-08-03 15:04:38 +0800632 return 0;
633
634err_free:
635 warnx("failed to load config from %s", conf_path);
636 json_object_put(obj);
637 return -1;
638}
639
640static int config_select(struct ctx *ctx, const char *name)
641{
642 struct config *config;
643 int i;
644
645 config = NULL;
646
Jeremy Kerr0f1a49c2018-08-03 15:41:38 +0800647 if (!name) {
648 /* no config specified: use the default */
649 if (!ctx->default_config) {
650 warnx("no config specified, and no default");
651 return -1;
Jeremy Kerr19527352018-08-03 15:04:38 +0800652 }
Jeremy Kerr0f1a49c2018-08-03 15:41:38 +0800653 config = ctx->default_config;
Jeremy Kerr19527352018-08-03 15:04:38 +0800654
Jeremy Kerr0f1a49c2018-08-03 15:41:38 +0800655 } else {
656 /* find a matching config... */
657 for (i = 0; i < ctx->n_configs; i++) {
658 if (!strcmp(ctx->configs[i].name, name)) {
659 config = &ctx->configs[i];
660 break;
661 }
662 }
663
664 if (!config) {
665 warnx("no such configuration '%s'", name);
666 return -1;
667 }
Jeremy Kerr19527352018-08-03 15:04:38 +0800668 }
669
670 /* ... and apply it */
Jeremy Kerrc6134c12018-08-09 13:03:33 +0800671 ctx->config = config;
Jeremy Kerr19527352018-08-03 15:04:38 +0800672 return 0;
673}
674
Jeremy Kerr6f9c4332018-08-03 17:11:15 +0800675static const struct option options[] = {
Jeremy Kerr13bb28f2018-08-09 10:41:10 +0800676 { .name = "help", .val = 'h' },
677 { .name = "metadata", .val = 'm' },
Jeremy Kerr6f9c4332018-08-03 17:11:15 +0800678 { 0 },
679};
680
Jeremy Kerr13bb28f2018-08-09 10:41:10 +0800681enum action {
682 ACTION_PROXY,
683 ACTION_METADATA,
684};
685
Jeremy Kerr6f9c4332018-08-03 17:11:15 +0800686static void print_usage(const char *progname)
687{
Jeremy Kerr13bb28f2018-08-09 10:41:10 +0800688 fprintf(stderr, "usage:\n");
689 fprintf(stderr, "\t%s [configuration]\n", progname);
690 fprintf(stderr, "\t%s --metadata\n", progname);
Jeremy Kerr6f9c4332018-08-03 17:11:15 +0800691}
692
Jeremy Kerr19527352018-08-03 15:04:38 +0800693int main(int argc, char **argv)
694{
Jeremy Kerr13bb28f2018-08-09 10:41:10 +0800695 enum action action = ACTION_PROXY;
Jeremy Kerr19527352018-08-03 15:04:38 +0800696 const char *config_name;
Jeremy Kerrf403c422018-07-26 12:14:56 +0800697 struct ctx _ctx, *ctx;
698 int rc;
699
Jeremy Kerr0f1a49c2018-08-03 15:41:38 +0800700 config_name = NULL;
Jeremy Kerr19527352018-08-03 15:04:38 +0800701
Jeremy Kerr6f9c4332018-08-03 17:11:15 +0800702 for (;;) {
703 int c = getopt_long(argc, argv, "h", options, NULL);
704 if (c == -1)
705 break;
706
707 switch (c) {
Jeremy Kerr13bb28f2018-08-09 10:41:10 +0800708 case 'm':
709 action = ACTION_METADATA;
710 break;
Jeremy Kerr6f9c4332018-08-03 17:11:15 +0800711 case 'h':
712 case '?':
713 print_usage(argv[0]);
714 return c == 'h' ? EXIT_SUCCESS : EXIT_FAILURE;
715 }
716 }
717
718 if (optind < argc)
719 config_name = argv[optind];
Jeremy Kerr19527352018-08-03 15:04:38 +0800720
Jeremy Kerrf403c422018-07-26 12:14:56 +0800721 ctx = &_ctx;
Jeremy Kerr19527352018-08-03 15:04:38 +0800722 memset(ctx, 0, sizeof(*ctx));
Jeremy Kerrf403c422018-07-26 12:14:56 +0800723 ctx->bufsize = bufsize;
724 ctx->buf = malloc(ctx->bufsize);
Jeremy Kerr19527352018-08-03 15:04:38 +0800725
726 rc = config_init(ctx);
727 if (rc)
728 goto out_free;
729
Jeremy Kerr13bb28f2018-08-09 10:41:10 +0800730 if (action == ACTION_METADATA) {
731 print_metadata(ctx);
732 goto out_free;
733 }
734
Jeremy Kerr19527352018-08-03 15:04:38 +0800735 rc = config_select(ctx, config_name);
736 if (rc)
737 goto out_free;
Jeremy Kerrf403c422018-07-26 12:14:56 +0800738
739 rc = open_nbd_socket(ctx);
740 if (rc)
741 goto out_free;
742
743 rc = setup_signals(ctx);
744 if (rc)
745 goto out_close;
746
747 rc = start_nbd_client(ctx);
748 if (rc)
749 goto out_stop_client;
750
751 rc = wait_for_nbd_client(ctx);
752 if (rc)
753 goto out_stop_client;
754
Jeremy Kerrc6134c12018-08-09 13:03:33 +0800755 rc = run_state_hooks(ctx, "start");
756 if (rc)
757 goto out_stop_client;
758
Jeremy Kerrf403c422018-07-26 12:14:56 +0800759 rc = run_proxy(ctx);
760
Jeremy Kerrc6134c12018-08-09 13:03:33 +0800761 run_state_hooks(ctx, "stop");
762
Jeremy Kerrf403c422018-07-26 12:14:56 +0800763out_stop_client:
764 /* we cleanup signals before stopping the client, because we
765 * no longer care about SIGCHLD from the stopping nbd-client
766 * process. stop_nbd_client will be a no-op if the client hasn't
767 * been started. */
768 cleanup_signals(ctx);
769
770 stop_nbd_client(ctx);
771 close(ctx->sock_client);
772
773out_close:
774 if (ctx->sock_path) {
775 unlink(ctx->sock_path);
776 free(ctx->sock_path);
777 }
778 close(ctx->sock);
779out_free:
Jeremy Kerr19527352018-08-03 15:04:38 +0800780 config_free(ctx);
Jeremy Kerrf403c422018-07-26 12:14:56 +0800781 free(ctx->buf);
782 return rc ? EXIT_FAILURE : EXIT_SUCCESS;
783}