blob: 2fcae1285e2c696e8d12514b5f16e42b081bb36e [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
86 sd = socket(AF_UNIX, SOCK_STREAM, 0);
87 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 Kerrf403c422018-07-26 12:14:56 +0800144 fd = open("/dev/null", O_RDWR);
145 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);
152 close(ctx->sock);
153
154 execlp("nbd-client", "nbd-client",
155 "-u", ctx->sock_path,
156 "-n",
Jeremy Kerr09295f22018-07-27 16:29:38 +0800157 "-t", timeout_str,
Jeremy Kerrc6134c12018-08-09 13:03:33 +0800158 ctx->config->nbd_device,
Jeremy Kerrf403c422018-07-26 12:14:56 +0800159 NULL);
160 err(EXIT_FAILURE, "can't start ndb client");
161 }
162
163 ctx->nbd_client_pid = pid;
164 return 0;
165}
166
167static void stop_nbd_client(struct ctx *ctx)
168{
169 int rc;
170
171 if (!ctx->nbd_client_pid)
172 return;
173
174 rc = kill(ctx->nbd_client_pid, SIGTERM);
175 if (rc)
176 return;
177
178 waitpid(ctx->nbd_client_pid, NULL, 0);
179 ctx->nbd_client_pid = 0;
180}
181
182static int copy_fd(struct ctx *ctx, int fd_in, int fd_out)
183{
184#ifdef HAVE_SPLICE
185 int rc;
186
187 rc = splice(fd_in, NULL, fd_out, NULL, ctx->bufsize, 0);
188 if (rc < 0)
189 warn("splice");
190
191 return rc;
192#else
193 size_t len, pos;
194 ssize_t rc;
195
196 for (;;) {
197 errno = 0;
198 rc = read(fd_in, ctx->buf, ctx->bufsize);
199 if (rc < 0) {
200 if (errno == EINTR)
201 continue;
202 warn("read failure");
203 return -1;
204 }
205 if (rc == 0)
206 return 0;
207 break;
208 }
209
210 len = rc;
211
212 for (pos = 0; pos < len;) {
213 errno = 0;
214 rc = write(fd_out, ctx->buf + pos, len - pos);
215 if (rc < 0) {
216 if (errno == EINTR)
217 continue;
218 warn("write failure");
219 return -1;
220 }
221 if (rc == 0)
222 break;
223 pos += rc;
224 }
225
226 return pos;
227#endif
228}
229
230static int signal_pipe_fd = -1;
231
232static void signal_handler(int signal)
233{
234 int rc;
235
236 rc = write(signal_pipe_fd, &signal, sizeof(signal));
237
238 /* not a lot we can do here but exit... */
239 if (rc != sizeof(signal))
240 exit(EXIT_FAILURE);
241}
242
243static int setup_signals(struct ctx *ctx)
244{
245 struct sigaction sa;
246 int rc;
247
248 rc = pipe(ctx->signal_pipe);
249 if (rc) {
250 warn("cant setup signal pipe");
251 return -1;
252 }
253
254 signal_pipe_fd = ctx->signal_pipe[1];
255
256 memset(&sa, 0, sizeof(sa));
257 sa.sa_handler = signal_handler;
258
259 sigaction(SIGINT, &sa, NULL);
260 sigaction(SIGTERM, &sa, NULL);
261 sigaction(SIGCHLD, &sa, NULL);
262
263 return 0;
264}
265
266static void cleanup_signals(struct ctx *ctx)
267{
268 struct sigaction sa;
269 memset(&sa, 0, sizeof(sa));
270 sa.sa_handler = SIG_DFL;
271
272 sigaction(SIGINT, &sa, NULL);
273 sigaction(SIGTERM, &sa, NULL);
274 sigaction(SIGCHLD, &sa, NULL);
275
276 close(ctx->signal_pipe[0]);
277 close(ctx->signal_pipe[1]);
278}
279
280static int process_signal_pipe(struct ctx *ctx, bool *exit)
281{
282 int buf, rc, status;
283
284 rc = read(ctx->signal_pipe[0], &buf, sizeof(buf));
285 if (rc != sizeof(buf))
286 return -1;
287
288 *exit = false;
289
290 switch (buf) {
291 case SIGCHLD:
292 rc = waitpid(ctx->nbd_client_pid, &status, WNOHANG);
293 if (rc > 0) {
294 warnx("nbd client stopped (%s: %d); exiting",
295 WIFEXITED(status) ? "rc" : "sig",
296 WIFEXITED(status) ?
297 WEXITSTATUS(status) :
298 WTERMSIG(status));
299 ctx->nbd_client_pid = 0;
300 }
301 break;
302 case SIGINT:
303 case SIGTERM:
304 *exit = true;
305 break;
306 }
307
308 return 0;
309}
310
311static int wait_for_nbd_client(struct ctx *ctx)
312{
313 struct pollfd pollfds[2];
314 int rc;
315
316 pollfds[0].fd = ctx->sock;
317 pollfds[0].events = POLLIN;
318 pollfds[1].fd = ctx->signal_pipe[0];
319 pollfds[1].events = POLLIN;
320
321 for (;;) {
322 errno = 0;
323 rc = poll(pollfds, 2, -1);
324 if (rc < 0) {
325 if (errno == EINTR)
326 continue;
327 warn("poll failed");
328 return -1;
329 }
330
331 if (pollfds[0].revents) {
332 rc = accept(ctx->sock, NULL, NULL);
333 if (rc < 0) {
334 warn("can't create connection");
335 return -1;
336 }
337 ctx->sock_client = rc;
338 break;
339 }
340
341 if (pollfds[1].revents) {
342 bool exit;
343 rc = process_signal_pipe(ctx, &exit);
344 if (rc || exit)
345 return -1;
346 }
347 }
348
349 return 0;
350}
351
Jeremy Kerrc6134c12018-08-09 13:03:33 +0800352#define join_paths(p1, p2, r) \
353 (BUILD_ASSERT_OR_ZERO(sizeof(r) > PATH_MAX) + __join_paths(p1, p2, r))
354static int __join_paths(const char *p1, const char *p2, char res[])
355{
356 size_t len;
357 char *pos;
358
359 len = strlen(p1) + 1 + strlen(p2);
360 if (len > PATH_MAX)
361 return -1;
362
363 pos = res;
364 strcpy(pos, p1);
365 pos += strlen(p1);
366 *pos = '/';
367 pos++;
368 strcpy(pos, p2);
369
370 return 0;
371}
372
373static int run_state_hook(struct ctx *ctx,
374 const char *path, const char *name, const char *action)
375{
376 int status, rc, fd;
377 pid_t pid;
378
379 pid = fork();
380 if (pid < 0) {
381 warn("can't fork to execute hook %s", name);
382 return -1;
383 }
384
385 if (!pid) {
386 close(ctx->sock);
387 close(ctx->sock_client);
388 close(ctx->signal_pipe[0]);
389 close(ctx->signal_pipe[1]);
390 fd = open("/dev/null", O_RDWR);
391 if (fd < 0)
392 exit(EXIT_FAILURE);
393
394 dup2(fd, STDIN_FILENO);
395 dup2(fd, STDOUT_FILENO);
396 dup2(fd, STDERR_FILENO);
397 execl(path, name, action, ctx->config->name, NULL);
398 exit(EXIT_FAILURE);
399 }
400
401 rc = waitpid(pid, &status, 0);
402 if (rc < 0) {
403 warn("wait");
404 return -1;
405 }
406
407 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
408 warnx("hook %s failed", name);
409 return -1;
410 }
411
412 return 0;
413}
414
415static int run_state_hooks(struct ctx *ctx, const char *action)
416{
417 struct dirent *dirent;
418 int rc;
419 DIR *dir;
420
421 dir = opendir(state_hook_path);
422 if (!dir)
423 return 0;
424
425 rc = 0;
426
427 for (dirent = readdir(dir); dirent; dirent = readdir(dir)) {
428 char full_path[PATH_MAX+1];
429 struct stat statbuf;
430
431 if (dirent->d_name[0] == '.')
432 continue;
433
434 rc = fstatat(dirfd(dir), dirent->d_name, &statbuf, 0);
435 if (rc)
436 continue;
437
438 if (!(S_ISREG(statbuf.st_mode) || S_ISLNK(statbuf.st_mode)))
439 continue;
440
441 if (faccessat(dirfd(dir), dirent->d_name, X_OK, 0))
442 continue;
443
444 rc = join_paths(state_hook_path, dirent->d_name, full_path);
445 if (rc)
446 continue;
447
448 rc = run_state_hook(ctx, full_path, dirent->d_name, action);
449 if (rc)
450 break;
451 }
452
453 closedir(dir);
454
455 return rc;
456}
Jeremy Kerrf403c422018-07-26 12:14:56 +0800457
458static int run_proxy(struct ctx *ctx)
459{
460 struct pollfd pollfds[3];
461 bool exit = false;
462 int rc;
463
464 /* main proxy: forward data between stdio & socket */
465 pollfds[0].fd = ctx->sock_client;
466 pollfds[0].events = POLLIN;
467 pollfds[1].fd = STDIN_FILENO;
468 pollfds[1].events = POLLIN;
469 pollfds[2].fd = ctx->signal_pipe[0];
470 pollfds[2].events = POLLIN;
471
472 for (;;) {
473 errno = 0;
474 rc = poll(pollfds, 3, -1);
475 if (rc < 0) {
476 if (errno == EINTR)
477 continue;
478 warn("poll failed");
479 break;
480 }
481
482 if (pollfds[0].revents) {
483 rc = copy_fd(ctx, ctx->sock_client, STDOUT_FILENO);
484 if (rc <= 0)
485 break;
486 }
487
488 if (pollfds[1].revents) {
489 rc = copy_fd(ctx, STDIN_FILENO, ctx->sock_client);
490 if (rc <= 0)
491 break;
492 }
493
494 if (pollfds[2].revents) {
495 rc = process_signal_pipe(ctx, &exit);
496 if (rc || exit)
497 break;
498 }
499 }
500
501 return rc ? -1 : 0;
502}
503
Jeremy Kerr13bb28f2018-08-09 10:41:10 +0800504static void print_metadata(struct ctx *ctx)
505{
506 struct json_object *md;
507 int i;
508
509 md = json_object_new_object();
510
511 for (i = 0; i < ctx->n_configs; i++) {
512 struct config *config = &ctx->configs[i];
513 json_object_object_add(md, config->name,
514 config->metadata);
515 }
516
517 puts(json_object_get_string(md));
518
519 json_object_put(md);
520}
521
Jeremy Kerr19527352018-08-03 15:04:38 +0800522static void config_free_one(struct config *config)
Jeremy Kerrf403c422018-07-26 12:14:56 +0800523{
Jeremy Kerr19527352018-08-03 15:04:38 +0800524 if (config->metadata)
525 json_object_put(config->metadata);
526 free(config->nbd_device);
527 free(config->name);
528}
529
530static int config_parse_one(struct config *config, const char *name,
531 json_object *obj)
532{
533 struct json_object *tmp, *meta;
534 json_bool jrc;
535
536 jrc = json_object_object_get_ex(obj, "nbd-device", &tmp);
537 if (!jrc) {
538 warnx("config %s doesn't specify a nbd-device", name);
539 return -1;
540 }
541
542 if (!json_object_is_type(tmp, json_type_string)) {
543 warnx("config %s has invalid nbd-device", name);
544 return -1;
545 }
546
547 config->nbd_device = strdup(json_object_get_string(tmp));
548 config->name = strdup(name);
549
Jeremy Kerr0f1a49c2018-08-03 15:41:38 +0800550 jrc = json_object_object_get_ex(obj, "default", &tmp);
551 config->is_default = jrc && json_object_get_boolean(tmp);
552
Jeremy Kerr19527352018-08-03 15:04:38 +0800553 jrc = json_object_object_get_ex(obj, "metadata", &meta);
554 if (jrc && json_object_is_type(meta, json_type_object))
555 config->metadata = json_object_get(meta);
556 else
557 config->metadata = NULL;
558
559 return 0;
560}
561
562static void config_free(struct ctx *ctx)
563{
564 int i;
565
566 for (i = 0; i < ctx->n_configs; i++)
567 config_free_one(&ctx->configs[i]);
568
569 free(ctx->configs);
570 ctx->n_configs = 0;
571}
572
573static int config_init(struct ctx *ctx)
574{
575 struct json_object *obj, *tmp;
576 json_bool jrc;
577 int i, rc;
578
579 /* apply defaults */
580 ctx->nbd_timeout = nbd_timeout_default;
581
582 obj = json_object_from_file(conf_path);
583 if (!obj) {
584 warnx("can't read configuration from %s\n", conf_path);
585 return -1;
586 }
587
588 /* global configuration */
589 jrc = json_object_object_get_ex(obj, "timeout", &tmp);
590 if (jrc) {
591 errno = 0;
592 ctx->nbd_timeout = json_object_get_int(tmp);
593 if (ctx->nbd_timeout == 0 && errno) {
594 warnx("can't parse timeout value");
595 goto err_free;
596 }
597 }
598
599 /* per-config configuration */
600 jrc = json_object_object_get_ex(obj, "configurations", &tmp);
601 if (!jrc) {
602 warnx("no configurations specified");
603 goto err_free;
604 }
605
606 if (!json_object_is_type(tmp, json_type_object)) {
607 warnx("invalid configurations format");
608 goto err_free;
609 }
610
611 ctx->n_configs = json_object_object_length(tmp);
612 ctx->configs = calloc(ctx->n_configs, sizeof(*ctx->configs));
613
614 i = 0;
Jeremy Kerr0f1a49c2018-08-03 15:41:38 +0800615 json_object_object_foreach(tmp, name, config_json) {
616 struct config *config = &ctx->configs[i];
617
618 rc = config_parse_one(config, name, config_json);
Jeremy Kerr19527352018-08-03 15:04:38 +0800619 if (rc)
620 goto err_free;
Jeremy Kerr0f1a49c2018-08-03 15:41:38 +0800621
622 if (config->is_default) {
623 if (ctx->default_config) {
624 warn("multiple configs flagged as default");
625 goto err_free;
626 }
627 ctx->default_config = config;
628 }
Jeremy Kerr19527352018-08-03 15:04:38 +0800629 i++;
630 }
631
632 json_object_put(obj);
633
Jeremy Kerr0f1a49c2018-08-03 15:41:38 +0800634 if (ctx->n_configs == 1)
635 ctx->default_config = &ctx->configs[0];
636
Jeremy Kerr19527352018-08-03 15:04:38 +0800637 return 0;
638
639err_free:
640 warnx("failed to load config from %s", conf_path);
641 json_object_put(obj);
642 return -1;
643}
644
645static int config_select(struct ctx *ctx, const char *name)
646{
647 struct config *config;
648 int i;
649
650 config = NULL;
651
Jeremy Kerr0f1a49c2018-08-03 15:41:38 +0800652 if (!name) {
653 /* no config specified: use the default */
654 if (!ctx->default_config) {
655 warnx("no config specified, and no default");
656 return -1;
Jeremy Kerr19527352018-08-03 15:04:38 +0800657 }
Jeremy Kerr0f1a49c2018-08-03 15:41:38 +0800658 config = ctx->default_config;
Jeremy Kerr19527352018-08-03 15:04:38 +0800659
Jeremy Kerr0f1a49c2018-08-03 15:41:38 +0800660 } else {
661 /* find a matching config... */
662 for (i = 0; i < ctx->n_configs; i++) {
663 if (!strcmp(ctx->configs[i].name, name)) {
664 config = &ctx->configs[i];
665 break;
666 }
667 }
668
669 if (!config) {
670 warnx("no such configuration '%s'", name);
671 return -1;
672 }
Jeremy Kerr19527352018-08-03 15:04:38 +0800673 }
674
675 /* ... and apply it */
Jeremy Kerrc6134c12018-08-09 13:03:33 +0800676 ctx->config = config;
Jeremy Kerr19527352018-08-03 15:04:38 +0800677 return 0;
678}
679
Jeremy Kerr6f9c4332018-08-03 17:11:15 +0800680static const struct option options[] = {
Jeremy Kerr13bb28f2018-08-09 10:41:10 +0800681 { .name = "help", .val = 'h' },
682 { .name = "metadata", .val = 'm' },
Jeremy Kerr6f9c4332018-08-03 17:11:15 +0800683 { 0 },
684};
685
Jeremy Kerr13bb28f2018-08-09 10:41:10 +0800686enum action {
687 ACTION_PROXY,
688 ACTION_METADATA,
689};
690
Jeremy Kerr6f9c4332018-08-03 17:11:15 +0800691static void print_usage(const char *progname)
692{
Jeremy Kerr13bb28f2018-08-09 10:41:10 +0800693 fprintf(stderr, "usage:\n");
694 fprintf(stderr, "\t%s [configuration]\n", progname);
695 fprintf(stderr, "\t%s --metadata\n", progname);
Jeremy Kerr6f9c4332018-08-03 17:11:15 +0800696}
697
Jeremy Kerr19527352018-08-03 15:04:38 +0800698int main(int argc, char **argv)
699{
Jeremy Kerr13bb28f2018-08-09 10:41:10 +0800700 enum action action = ACTION_PROXY;
Jeremy Kerr19527352018-08-03 15:04:38 +0800701 const char *config_name;
Jeremy Kerrf403c422018-07-26 12:14:56 +0800702 struct ctx _ctx, *ctx;
703 int rc;
704
Jeremy Kerr0f1a49c2018-08-03 15:41:38 +0800705 config_name = NULL;
Jeremy Kerr19527352018-08-03 15:04:38 +0800706
Jeremy Kerr6f9c4332018-08-03 17:11:15 +0800707 for (;;) {
708 int c = getopt_long(argc, argv, "h", options, NULL);
709 if (c == -1)
710 break;
711
712 switch (c) {
Jeremy Kerr13bb28f2018-08-09 10:41:10 +0800713 case 'm':
714 action = ACTION_METADATA;
715 break;
Jeremy Kerr6f9c4332018-08-03 17:11:15 +0800716 case 'h':
717 case '?':
718 print_usage(argv[0]);
719 return c == 'h' ? EXIT_SUCCESS : EXIT_FAILURE;
720 }
721 }
722
723 if (optind < argc)
724 config_name = argv[optind];
Jeremy Kerr19527352018-08-03 15:04:38 +0800725
Jeremy Kerrf403c422018-07-26 12:14:56 +0800726 ctx = &_ctx;
Jeremy Kerr19527352018-08-03 15:04:38 +0800727 memset(ctx, 0, sizeof(*ctx));
Jeremy Kerrf403c422018-07-26 12:14:56 +0800728 ctx->bufsize = bufsize;
729 ctx->buf = malloc(ctx->bufsize);
Jeremy Kerr19527352018-08-03 15:04:38 +0800730
731 rc = config_init(ctx);
732 if (rc)
733 goto out_free;
734
Jeremy Kerr13bb28f2018-08-09 10:41:10 +0800735 if (action == ACTION_METADATA) {
736 print_metadata(ctx);
737 goto out_free;
738 }
739
Jeremy Kerr19527352018-08-03 15:04:38 +0800740 rc = config_select(ctx, config_name);
741 if (rc)
742 goto out_free;
Jeremy Kerrf403c422018-07-26 12:14:56 +0800743
744 rc = open_nbd_socket(ctx);
745 if (rc)
746 goto out_free;
747
748 rc = setup_signals(ctx);
749 if (rc)
750 goto out_close;
751
752 rc = start_nbd_client(ctx);
753 if (rc)
754 goto out_stop_client;
755
756 rc = wait_for_nbd_client(ctx);
757 if (rc)
758 goto out_stop_client;
759
Jeremy Kerrc6134c12018-08-09 13:03:33 +0800760 rc = run_state_hooks(ctx, "start");
761 if (rc)
762 goto out_stop_client;
763
Jeremy Kerrf403c422018-07-26 12:14:56 +0800764 rc = run_proxy(ctx);
765
Jeremy Kerrc6134c12018-08-09 13:03:33 +0800766 run_state_hooks(ctx, "stop");
767
Jeremy Kerrf403c422018-07-26 12:14:56 +0800768out_stop_client:
769 /* we cleanup signals before stopping the client, because we
770 * no longer care about SIGCHLD from the stopping nbd-client
771 * process. stop_nbd_client will be a no-op if the client hasn't
772 * been started. */
773 cleanup_signals(ctx);
774
775 stop_nbd_client(ctx);
776 close(ctx->sock_client);
777
778out_close:
779 if (ctx->sock_path) {
780 unlink(ctx->sock_path);
781 free(ctx->sock_path);
782 }
783 close(ctx->sock);
784out_free:
Jeremy Kerr19527352018-08-03 15:04:38 +0800785 config_free(ctx);
Jeremy Kerrf403c422018-07-26 12:14:56 +0800786 free(ctx->buf);
787 return rc ? EXIT_FAILURE : EXIT_SUCCESS;
788}