]> git.proxmox.com Git - ceph.git/blame - ceph/src/tools/rbd_nbd/rbd-nbd.cc
import ceph 14.2.5
[ceph.git] / ceph / src / tools / rbd_nbd / rbd-nbd.cc
CommitLineData
7c673cae
FG
1// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2// vim: ts=8 sw=2 smarttab
3
4/*
5 * rbd-nbd - RBD in userspace
6 *
7 * Copyright (C) 2015 - 2016 Kylin Corporation
8 *
9 * Author: Yunchuan Wen <yunchuan.wen@kylin-cloud.com>
10 * Li Wang <li.wang@kylin-cloud.com>
11 *
12 * This is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Lesser General Public
14 * License version 2.1, as published by the Free Software
15 * Foundation. See file COPYING.
16 *
17*/
18
19#include "include/int_types.h"
20
21#include <stdio.h>
22#include <stdlib.h>
23#include <stddef.h>
24#include <errno.h>
25#include <fcntl.h>
26#include <string.h>
27#include <sys/types.h>
28#include <unistd.h>
29
30#include <linux/nbd.h>
31#include <linux/fs.h>
32#include <sys/ioctl.h>
33#include <sys/socket.h>
34
eafe8130
TL
35#include "nbd-netlink.h"
36#include <libnl3/netlink/genl/genl.h>
37#include <libnl3/netlink/genl/ctrl.h>
38#include <libnl3/netlink/genl/mngt.h>
39
7c673cae 40#include <fstream>
11fdf7f2
TL
41#include <iostream>
42#include <memory>
43#include <regex>
44#include <boost/algorithm/string/predicate.hpp>
7c673cae 45
11fdf7f2
TL
46#include "common/Formatter.h"
47#include "common/Preforker.h"
48#include "common/TextTable.h"
49#include "common/ceph_argparse.h"
7c673cae
FG
50#include "common/config.h"
51#include "common/dout.h"
7c673cae
FG
52#include "common/errno.h"
53#include "common/module.h"
54#include "common/safe_io.h"
224ce89b 55#include "common/version.h"
11fdf7f2 56
7c673cae
FG
57#include "global/global_init.h"
58#include "global/signal_handler.h"
59
60#include "include/rados/librados.hpp"
61#include "include/rbd/librbd.hpp"
62#include "include/stringify.h"
63#include "include/xlist.h"
64
11fdf7f2
TL
65#include "mon/MonClient.h"
66
7c673cae
FG
67#define dout_context g_ceph_context
68#define dout_subsys ceph_subsys_rbd
69#undef dout_prefix
70#define dout_prefix *_dout << "rbd-nbd: "
71
31f18b77
FG
72struct Config {
73 int nbds_max = 0;
74 int max_part = 255;
11fdf7f2 75 int timeout = -1;
31f18b77
FG
76
77 bool exclusive = false;
78 bool readonly = false;
79 bool set_max_part = false;
eafe8130 80 bool try_netlink = false;
31f18b77
FG
81
82 std::string poolname;
11fdf7f2 83 std::string nsname;
31f18b77
FG
84 std::string imgname;
85 std::string snapname;
86 std::string devpath;
11fdf7f2
TL
87
88 std::string format;
89 bool pretty_format = false;
31f18b77
FG
90};
91
7c673cae
FG
92static void usage()
93{
94 std::cout << "Usage: rbd-nbd [options] map <image-or-snap-spec> Map an image to nbd device\n"
11fdf7f2
TL
95 << " unmap <device|image-or-snap-spec> Unmap nbd device\n"
96 << " [options] list-mapped List mapped nbd devices\n"
97 << "Map options:\n"
494da23a 98 << " --device <device path> Specify nbd device path (/dev/nbd{num})\n"
7c673cae
FG
99 << " --read-only Map read-only\n"
100 << " --nbds_max <limit> Override for module param nbds_max\n"
101 << " --max_part <limit> Override for module param max_part\n"
102 << " --exclusive Forbid writes by other clients\n"
11fdf7f2 103 << " --timeout <seconds> Set nbd request timeout\n"
eafe8130 104 << " --try-netlink Use the nbd netlink interface\n"
11fdf7f2
TL
105 << "\n"
106 << "List options:\n"
107 << " --format plain|json|xml Output format (default: plain)\n"
108 << " --pretty-format Pretty formatting (json and xml)\n"
7c673cae
FG
109 << std::endl;
110 generic_server_usage();
111}
112
7c673cae 113static int nbd = -1;
eafe8130 114static int nbd_index = -1;
7c673cae 115
11fdf7f2 116enum Command {
31f18b77
FG
117 None,
118 Connect,
119 Disconnect,
120 List
11fdf7f2
TL
121};
122
123static Command cmd = None;
31f18b77 124
7c673cae
FG
125#define RBD_NBD_BLKSIZE 512UL
126
224ce89b
WB
127#define HELP_INFO 1
128#define VERSION_INFO 2
129
7c673cae
FG
130#ifdef CEPH_BIG_ENDIAN
131#define ntohll(a) (a)
132#elif defined(CEPH_LITTLE_ENDIAN)
133#define ntohll(a) swab(a)
134#else
135#error "Could not determine endianess"
136#endif
137#define htonll(a) ntohll(a)
138
11fdf7f2
TL
139static int parse_args(vector<const char*>& args, std::ostream *err_msg,
140 Command *command, Config *cfg);
eafe8130 141static int netlink_resize(int nbd_index, uint64_t size);
7c673cae
FG
142
143class NBDServer
144{
145private:
146 int fd;
147 librbd::Image &image;
148
149public:
150 NBDServer(int _fd, librbd::Image& _image)
151 : fd(_fd)
152 , image(_image)
eafe8130 153 , disconnect_lock("NBDServer::DisconnectLocker")
7c673cae
FG
154 , lock("NBDServer::Locker")
155 , reader_thread(*this, &NBDServer::reader_entry)
156 , writer_thread(*this, &NBDServer::writer_entry)
157 , started(false)
158 {}
159
160private:
eafe8130
TL
161 Mutex disconnect_lock;
162 Cond disconnect_cond;
7c673cae
FG
163 std::atomic<bool> terminated = { false };
164
165 void shutdown()
166 {
167 bool expected = false;
168 if (terminated.compare_exchange_strong(expected, true)) {
169 ::shutdown(fd, SHUT_RDWR);
170
171 Mutex::Locker l(lock);
172 cond.Signal();
173 }
174 }
175
176 struct IOContext
177 {
178 xlist<IOContext*>::item item;
11fdf7f2 179 NBDServer *server = nullptr;
7c673cae
FG
180 struct nbd_request request;
181 struct nbd_reply reply;
182 bufferlist data;
11fdf7f2 183 int command = 0;
7c673cae
FG
184
185 IOContext()
186 : item(this)
187 {}
188 };
189
190 friend std::ostream &operator<<(std::ostream &os, const IOContext &ctx);
191
192 Mutex lock;
193 Cond cond;
194 xlist<IOContext*> io_pending;
195 xlist<IOContext*> io_finished;
196
197 void io_start(IOContext *ctx)
198 {
199 Mutex::Locker l(lock);
200 io_pending.push_back(&ctx->item);
201 }
202
203 void io_finish(IOContext *ctx)
204 {
205 Mutex::Locker l(lock);
11fdf7f2 206 ceph_assert(ctx->item.is_on_list());
7c673cae
FG
207 ctx->item.remove_myself();
208 io_finished.push_back(&ctx->item);
209 cond.Signal();
210 }
211
212 IOContext *wait_io_finish()
213 {
214 Mutex::Locker l(lock);
215 while(io_finished.empty() && !terminated)
216 cond.Wait(lock);
217
218 if (io_finished.empty())
219 return NULL;
220
221 IOContext *ret = io_finished.front();
222 io_finished.pop_front();
223
224 return ret;
225 }
226
227 void wait_clean()
228 {
11fdf7f2 229 ceph_assert(!reader_thread.is_started());
7c673cae
FG
230 Mutex::Locker l(lock);
231 while(!io_pending.empty())
232 cond.Wait(lock);
233
234 while(!io_finished.empty()) {
11fdf7f2 235 std::unique_ptr<IOContext> free_ctx(io_finished.front());
7c673cae
FG
236 io_finished.pop_front();
237 }
238 }
239
240 static void aio_callback(librbd::completion_t cb, void *arg)
241 {
242 librbd::RBD::AioCompletion *aio_completion =
243 reinterpret_cast<librbd::RBD::AioCompletion*>(cb);
244
245 IOContext *ctx = reinterpret_cast<IOContext *>(arg);
246 int ret = aio_completion->get_return_value();
247
248 dout(20) << __func__ << ": " << *ctx << dendl;
249
250 if (ret == -EINVAL) {
251 // if shrinking an image, a pagecache writeback might reference
252 // extents outside of the range of the new image extents
181888fb 253 dout(0) << __func__ << ": masking IO out-of-bounds error" << dendl;
7c673cae
FG
254 ctx->data.clear();
255 ret = 0;
256 }
257
258 if (ret < 0) {
259 ctx->reply.error = htonl(-ret);
260 } else if ((ctx->command == NBD_CMD_READ) &&
261 ret < static_cast<int>(ctx->request.len)) {
262 int pad_byte_count = static_cast<int> (ctx->request.len) - ret;
263 ctx->data.append_zero(pad_byte_count);
264 dout(20) << __func__ << ": " << *ctx << ": Pad byte count: "
265 << pad_byte_count << dendl;
11fdf7f2 266 ctx->reply.error = htonl(0);
7c673cae
FG
267 } else {
268 ctx->reply.error = htonl(0);
269 }
270 ctx->server->io_finish(ctx);
271
272 aio_completion->release();
273 }
274
275 void reader_entry()
276 {
277 while (!terminated) {
11fdf7f2 278 std::unique_ptr<IOContext> ctx(new IOContext());
7c673cae
FG
279 ctx->server = this;
280
281 dout(20) << __func__ << ": waiting for nbd request" << dendl;
282
283 int r = safe_read_exact(fd, &ctx->request, sizeof(struct nbd_request));
284 if (r < 0) {
285 derr << "failed to read nbd request header: " << cpp_strerror(r)
286 << dendl;
eafe8130 287 goto signal;
7c673cae
FG
288 }
289
290 if (ctx->request.magic != htonl(NBD_REQUEST_MAGIC)) {
291 derr << "invalid nbd request header" << dendl;
eafe8130 292 goto signal;
7c673cae
FG
293 }
294
295 ctx->request.from = ntohll(ctx->request.from);
296 ctx->request.type = ntohl(ctx->request.type);
297 ctx->request.len = ntohl(ctx->request.len);
298
299 ctx->reply.magic = htonl(NBD_REPLY_MAGIC);
300 memcpy(ctx->reply.handle, ctx->request.handle, sizeof(ctx->reply.handle));
301
302 ctx->command = ctx->request.type & 0x0000ffff;
303
304 dout(20) << *ctx << ": start" << dendl;
305
306 switch (ctx->command)
307 {
308 case NBD_CMD_DISC:
309 // NBD_DO_IT will return when pipe is closed
310 dout(0) << "disconnect request received" << dendl;
eafe8130 311 goto signal;
7c673cae
FG
312 case NBD_CMD_WRITE:
313 bufferptr ptr(ctx->request.len);
314 r = safe_read_exact(fd, ptr.c_str(), ctx->request.len);
315 if (r < 0) {
316 derr << *ctx << ": failed to read nbd request data: "
317 << cpp_strerror(r) << dendl;
eafe8130 318 goto signal;
7c673cae
FG
319 }
320 ctx->data.push_back(ptr);
321 break;
322 }
323
324 IOContext *pctx = ctx.release();
325 io_start(pctx);
326 librbd::RBD::AioCompletion *c = new librbd::RBD::AioCompletion(pctx, aio_callback);
327 switch (pctx->command)
328 {
329 case NBD_CMD_WRITE:
330 image.aio_write(pctx->request.from, pctx->request.len, pctx->data, c);
331 break;
332 case NBD_CMD_READ:
333 image.aio_read(pctx->request.from, pctx->request.len, pctx->data, c);
334 break;
335 case NBD_CMD_FLUSH:
336 image.aio_flush(c);
337 break;
338 case NBD_CMD_TRIM:
339 image.aio_discard(pctx->request.from, pctx->request.len, c);
340 break;
341 default:
342 derr << *pctx << ": invalid request command" << dendl;
343 c->release();
eafe8130 344 goto signal;
7c673cae
FG
345 }
346 }
347 dout(20) << __func__ << ": terminated" << dendl;
eafe8130
TL
348
349signal:
350 Mutex::Locker l(disconnect_lock);
351 disconnect_cond.Signal();
7c673cae
FG
352 }
353
354 void writer_entry()
355 {
356 while (!terminated) {
357 dout(20) << __func__ << ": waiting for io request" << dendl;
11fdf7f2 358 std::unique_ptr<IOContext> ctx(wait_io_finish());
7c673cae
FG
359 if (!ctx) {
360 dout(20) << __func__ << ": no io requests, terminating" << dendl;
361 return;
362 }
363
364 dout(20) << __func__ << ": got: " << *ctx << dendl;
365
366 int r = safe_write(fd, &ctx->reply, sizeof(struct nbd_reply));
367 if (r < 0) {
368 derr << *ctx << ": failed to write reply header: " << cpp_strerror(r)
369 << dendl;
370 return;
371 }
372 if (ctx->command == NBD_CMD_READ && ctx->reply.error == htonl(0)) {
373 r = ctx->data.write_fd(fd);
374 if (r < 0) {
375 derr << *ctx << ": failed to write replay data: " << cpp_strerror(r)
376 << dendl;
377 return;
378 }
379 }
380 dout(20) << *ctx << ": finish" << dendl;
381 }
382 dout(20) << __func__ << ": terminated" << dendl;
383 }
384
385 class ThreadHelper : public Thread
386 {
387 public:
388 typedef void (NBDServer::*entry_func)();
389 private:
390 NBDServer &server;
391 entry_func func;
392 public:
393 ThreadHelper(NBDServer &_server, entry_func _func)
394 :server(_server)
395 ,func(_func)
396 {}
397 protected:
398 void* entry() override
399 {
400 (server.*func)();
401 server.shutdown();
402 return NULL;
403 }
404 } reader_thread, writer_thread;
405
406 bool started;
407public:
408 void start()
409 {
410 if (!started) {
411 dout(10) << __func__ << ": starting" << dendl;
412
413 started = true;
414
415 reader_thread.create("rbd_reader");
416 writer_thread.create("rbd_writer");
417 }
418 }
419
eafe8130
TL
420 void wait_for_disconnect()
421 {
422 if (!started)
423 return;
424
425 Mutex::Locker l(disconnect_lock);
426 disconnect_cond.Wait(disconnect_lock);
427 }
428
11fdf7f2 429 ~NBDServer()
7c673cae
FG
430 {
431 if (started) {
432 dout(10) << __func__ << ": terminating" << dendl;
433
434 shutdown();
435
436 reader_thread.join();
437 writer_thread.join();
438
439 wait_clean();
440
441 started = false;
442 }
443 }
7c673cae
FG
444};
445
446std::ostream &operator<<(std::ostream &os, const NBDServer::IOContext &ctx) {
447
448 os << "[" << std::hex << ntohll(*((uint64_t *)ctx.request.handle));
449
450 switch (ctx.command)
451 {
452 case NBD_CMD_WRITE:
453 os << " WRITE ";
454 break;
455 case NBD_CMD_READ:
456 os << " READ ";
457 break;
458 case NBD_CMD_FLUSH:
459 os << " FLUSH ";
460 break;
461 case NBD_CMD_TRIM:
462 os << " TRIM ";
463 break;
464 default:
11fdf7f2 465 os << " UNKNOWN(" << ctx.command << ") ";
7c673cae
FG
466 break;
467 }
468
469 os << ctx.request.from << "~" << ctx.request.len << " "
11fdf7f2 470 << std::dec << ntohl(ctx.reply.error) << "]";
7c673cae
FG
471
472 return os;
473}
474
475class NBDWatchCtx : public librbd::UpdateWatchCtx
476{
477private:
478 int fd;
eafe8130
TL
479 int nbd_index;
480 bool use_netlink;
7c673cae
FG
481 librados::IoCtx &io_ctx;
482 librbd::Image &image;
483 unsigned long size;
484public:
485 NBDWatchCtx(int _fd,
eafe8130
TL
486 int _nbd_index,
487 bool _use_netlink,
7c673cae
FG
488 librados::IoCtx &_io_ctx,
489 librbd::Image &_image,
490 unsigned long _size)
491 : fd(_fd)
eafe8130
TL
492 , nbd_index(_nbd_index)
493 , use_netlink(_use_netlink)
7c673cae
FG
494 , io_ctx(_io_ctx)
495 , image(_image)
496 , size(_size)
497 { }
498
499 ~NBDWatchCtx() override {}
500
501 void handle_notify() override
502 {
503 librbd::image_info_t info;
504 if (image.stat(info, sizeof(info)) == 0) {
505 unsigned long new_size = info.size;
eafe8130 506 int ret;
7c673cae
FG
507
508 if (new_size != size) {
b32b8144 509 dout(5) << "resize detected" << dendl;
7c673cae 510 if (ioctl(fd, BLKFLSBUF, NULL) < 0)
eafe8130
TL
511 derr << "invalidate page cache failed: " << cpp_strerror(errno)
512 << dendl;
513 if (use_netlink) {
514 ret = netlink_resize(nbd_index, new_size);
515 } else {
516 ret = ioctl(fd, NBD_SET_SIZE, new_size);
517 if (ret < 0)
7c673cae 518 derr << "resize failed: " << cpp_strerror(errno) << dendl;
eafe8130
TL
519 }
520
521 if (!ret)
7c673cae 522 size = new_size;
eafe8130 523
b32b8144
FG
524 if (ioctl(fd, BLKRRPART, NULL) < 0) {
525 derr << "rescan of partition table failed: " << cpp_strerror(errno)
526 << dendl;
527 }
7c673cae 528 if (image.invalidate_cache() < 0)
eafe8130 529 derr << "invalidate rbd cache failed" << dendl;
7c673cae
FG
530 }
531 }
532 }
533};
534
11fdf7f2
TL
535class NBDListIterator {
536public:
537 bool get(int *pid, Config *cfg) {
538 while (true) {
539 std::string nbd_path = "/sys/block/nbd" + stringify(m_index);
540 if(access(nbd_path.c_str(), F_OK) != 0) {
541 return false;
542 }
543
544 *cfg = Config();
545 cfg->devpath = "/dev/nbd" + stringify(m_index++);
546
547 std::ifstream ifs;
548 ifs.open(nbd_path + "/pid", std::ifstream::in);
549 if (!ifs.is_open()) {
550 continue;
551 }
552 ifs >> *pid;
553
554 int r = get_mapped_info(*pid, cfg);
555 if (r < 0) {
556 continue;
557 }
558
559 return true;
560 }
561 }
562
563private:
564 int m_index = 0;
565
566 int get_mapped_info(int pid, Config *cfg) {
567 int r;
568 std::string path = "/proc/" + stringify(pid) + "/cmdline";
569 std::ifstream ifs;
570 std::string cmdline;
571 std::vector<const char*> args;
572
573 ifs.open(path.c_str(), std::ifstream::in);
574 if (!ifs.is_open())
575 return -1;
576 ifs >> cmdline;
577
578 for (unsigned i = 0; i < cmdline.size(); i++) {
579 const char *arg = &cmdline[i];
580 if (i == 0) {
581 if (strcmp(basename(arg) , "rbd-nbd") != 0) {
582 return -EINVAL;
583 }
584 } else {
585 args.push_back(arg);
586 }
587
588 while (cmdline[i] != '\0') {
589 i++;
590 }
591 }
592
593 std::ostringstream err_msg;
594 Command command;
595 r = parse_args(args, &err_msg, &command, cfg);
596 if (r < 0) {
597 return r;
598 }
599
600 if (command != Connect) {
601 return -ENOENT;
602 }
603
604 return 0;
605 }
606};
607
eafe8130 608static int load_module(Config *cfg)
7c673cae 609{
eafe8130
TL
610 ostringstream param;
611 int ret;
11fdf7f2 612
eafe8130
TL
613 if (cfg->nbds_max)
614 param << "nbds_max=" << cfg->nbds_max;
615
616 if (cfg->max_part)
617 param << " max_part=" << cfg->max_part;
618
619 if (!access("/sys/module/nbd", F_OK)) {
620 if (cfg->nbds_max || cfg->set_max_part)
621 cerr << "rbd-nbd: ignoring kernel module parameter options: nbd module already loaded"
622 << std::endl;
623 return 0;
7c673cae
FG
624 }
625
eafe8130
TL
626 ret = module_load("nbd", param.str().c_str());
627 if (ret < 0)
628 cerr << "rbd-nbd: failed to load nbd kernel module: " << cpp_strerror(-ret)
7c673cae 629 << std::endl;
11fdf7f2 630
eafe8130 631 return ret;
7c673cae
FG
632}
633
634static int check_device_size(int nbd_index, unsigned long expected_size)
635{
636 // There are bugs with some older kernel versions that result in an
637 // overflow for large image sizes. This check is to ensure we are
638 // not affected.
639
640 unsigned long size = 0;
641 std::string path = "/sys/block/nbd" + stringify(nbd_index) + "/size";
642 std::ifstream ifs;
643 ifs.open(path.c_str(), std::ifstream::in);
644 if (!ifs.is_open()) {
645 cerr << "rbd-nbd: failed to open " << path << std::endl;
646 return -EINVAL;
647 }
648 ifs >> size;
649 size *= RBD_NBD_BLKSIZE;
650
651 if (size == 0) {
652 // Newer kernel versions will report real size only after nbd
653 // connect. Assume this is the case and return success.
654 return 0;
655 }
656
657 if (size != expected_size) {
658 cerr << "rbd-nbd: kernel reported invalid device size (" << size
659 << ", expected " << expected_size << ")" << std::endl;
660 return -EINVAL;
661 }
662
663 return 0;
664}
665
eafe8130
TL
666static int parse_nbd_index(const std::string& devpath)
667{
668 int index, ret;
669
670 ret = sscanf(devpath.c_str(), "/dev/nbd%d", &index);
671 if (ret <= 0) {
672 // mean an early matching failure. But some cases need a negative value.
673 if (ret == 0)
674 ret = -EINVAL;
675 cerr << "rbd-nbd: invalid device path: " << devpath
676 << " (expected /dev/nbd{num})" << std::endl;
677 return ret;
678 }
679
680 return index;
681}
682
683static int try_ioctl_setup(Config *cfg, int fd, uint64_t size, uint64_t flags)
684{
685 int index = 0, r;
686
687 if (cfg->devpath.empty()) {
688 char dev[64];
689 const char *path = "/sys/module/nbd/parameters/nbds_max";
690 int nbds_max = -1;
691 if (access(path, F_OK) == 0) {
692 std::ifstream ifs;
693 ifs.open(path, std::ifstream::in);
694 if (ifs.is_open()) {
695 ifs >> nbds_max;
696 ifs.close();
697 }
698 }
699
700 while (true) {
701 snprintf(dev, sizeof(dev), "/dev/nbd%d", index);
702
703 nbd = open(dev, O_RDWR);
704 if (nbd < 0) {
705 if (nbd == -EPERM && nbds_max != -1 && index < (nbds_max-1)) {
706 ++index;
707 continue;
708 }
709 r = nbd;
710 cerr << "rbd-nbd: failed to find unused device" << std::endl;
711 goto done;
712 }
713
714 r = ioctl(nbd, NBD_SET_SOCK, fd);
715 if (r < 0) {
716 close(nbd);
717 ++index;
718 continue;
719 }
720
721 cfg->devpath = dev;
722 break;
723 }
724 } else {
725 r = parse_nbd_index(cfg->devpath);
726 if (r < 0)
727 goto done;
728 index = r;
729
730 nbd = open(cfg->devpath.c_str(), O_RDWR);
731 if (nbd < 0) {
732 r = nbd;
733 cerr << "rbd-nbd: failed to open device: " << cfg->devpath << std::endl;
734 goto done;
735 }
736
737 r = ioctl(nbd, NBD_SET_SOCK, fd);
738 if (r < 0) {
739 r = -errno;
740 cerr << "rbd-nbd: the device " << cfg->devpath << " is busy" << std::endl;
741 close(nbd);
742 goto done;
743 }
744 }
745
746 r = ioctl(nbd, NBD_SET_BLKSIZE, RBD_NBD_BLKSIZE);
747 if (r < 0) {
748 r = -errno;
749 goto close_nbd;
750 }
751
752 r = ioctl(nbd, NBD_SET_SIZE, size);
753 if (r < 0) {
754 r = -errno;
755 goto close_nbd;
756 }
757
758 ioctl(nbd, NBD_SET_FLAGS, flags);
759
760 if (cfg->timeout >= 0) {
761 r = ioctl(nbd, NBD_SET_TIMEOUT, (unsigned long)cfg->timeout);
762 if (r < 0) {
763 r = -errno;
764 cerr << "rbd-nbd: failed to set timeout: " << cpp_strerror(r)
765 << std::endl;
766 goto close_nbd;
767 }
768 }
769
770 dout(10) << "ioctl setup complete for " << cfg->devpath << dendl;
771 nbd_index = index;
772 return 0;
773
774close_nbd:
775 if (r < 0) {
776 ioctl(nbd, NBD_CLEAR_SOCK);
777 cerr << "rbd-nbd: failed to map, status: " << cpp_strerror(-r) << std::endl;
778 }
779 close(nbd);
780done:
781 return r;
782}
783
784static void netlink_cleanup(struct nl_sock *sock)
785{
786 if (!sock)
787 return;
788
789 nl_close(sock);
790 nl_socket_free(sock);
791}
792
793static struct nl_sock *netlink_init(int *id)
794{
795 struct nl_sock *sock;
796 int ret;
797
798 sock = nl_socket_alloc();
799 if (!sock) {
800 cerr << "rbd-nbd: Could not allocate netlink socket." << std::endl;
801 return NULL;
802 }
803
804 ret = genl_connect(sock);
805 if (ret < 0) {
806 cerr << "rbd-nbd: Could not connect netlink socket. Error " << ret
807 << std::endl;
808 goto free_sock;
809 }
810
811 *id = genl_ctrl_resolve(sock, "nbd");
812 if (*id < 0)
813 // nbd netlink interface not supported.
814 goto close_sock;
815
816 return sock;
817
818close_sock:
819 nl_close(sock);
820free_sock:
821 nl_socket_free(sock);
822 return NULL;
823}
824
825static int netlink_disconnect(int index)
826{
827 struct nl_sock *sock;
828 struct nl_msg *msg;
829 int ret, nl_id;
830
831 sock = netlink_init(&nl_id);
832 if (!sock)
833 // Try ioctl
834 return 1;
835
836 nl_socket_modify_cb(sock, NL_CB_VALID, NL_CB_CUSTOM, genl_handle_msg, NULL);
837
838 msg = nlmsg_alloc();
839 if (!msg) {
840 cerr << "rbd-nbd: Could not allocate netlink message." << std::endl;
841 goto free_sock;
842 }
843
844 if (!genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, nl_id, 0, 0,
845 NBD_CMD_DISCONNECT, 0)) {
846 cerr << "rbd-nbd: Could not setup message." << std::endl;
847 goto nla_put_failure;
848 }
849
850 NLA_PUT_U32(msg, NBD_ATTR_INDEX, index);
851
852 ret = nl_send_sync(sock, msg);
853 netlink_cleanup(sock);
854 if (ret < 0) {
855 cerr << "rbd-nbd: netlink disconnect failed: " << nl_geterror(-ret)
856 << std::endl;
857 return -EIO;
858 }
859
860 return 0;
861
862nla_put_failure:
863 nlmsg_free(msg);
864free_sock:
865 netlink_cleanup(sock);
866 return -EIO;
867}
868
869static int netlink_disconnect_by_path(const std::string& devpath)
870{
871 int index;
872
873 index = parse_nbd_index(devpath);
874 if (index < 0)
875 return index;
876
877 return netlink_disconnect(index);
878}
879
880static int netlink_resize(int nbd_index, uint64_t size)
881{
882 struct nl_sock *sock;
883 struct nl_msg *msg;
884 int nl_id, ret;
885
886 sock = netlink_init(&nl_id);
887 if (!sock) {
888 cerr << "rbd-nbd: Netlink interface not supported." << std::endl;
889 return 1;
890 }
891
892 nl_socket_modify_cb(sock, NL_CB_VALID, NL_CB_CUSTOM, genl_handle_msg, NULL);
893
894 msg = nlmsg_alloc();
895 if (!msg) {
896 cerr << "rbd-nbd: Could not allocate netlink message." << std::endl;
897 goto free_sock;
898 }
899
900 if (!genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, nl_id, 0, 0,
901 NBD_CMD_RECONFIGURE, 0)) {
902 cerr << "rbd-nbd: Could not setup message." << std::endl;
903 goto free_msg;
904 }
905
906 NLA_PUT_U32(msg, NBD_ATTR_INDEX, nbd_index);
907 NLA_PUT_U64(msg, NBD_ATTR_SIZE_BYTES, size);
908
909 ret = nl_send_sync(sock, msg);
910 if (ret < 0) {
911 cerr << "rbd-nbd: netlink resize failed: " << nl_geterror(ret) << std::endl;
912 goto free_sock;
913 }
914
915 netlink_cleanup(sock);
916 dout(10) << "netlink resize complete for nbd" << nbd_index << dendl;
917 return 0;
918
919nla_put_failure:
920free_msg:
921 nlmsg_free(msg);
922free_sock:
923 netlink_cleanup(sock);
924 return -EIO;
925}
926
927static int netlink_connect_cb(struct nl_msg *msg, void *arg)
928{
929 struct genlmsghdr *gnlh = (struct genlmsghdr *)nlmsg_data(nlmsg_hdr(msg));
930 Config *cfg = (Config *)arg;
931 struct nlattr *msg_attr[NBD_ATTR_MAX + 1];
932 uint32_t index;
933 int ret;
934
935 ret = nla_parse(msg_attr, NBD_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
936 genlmsg_attrlen(gnlh, 0), NULL);
937 if (ret) {
938 cerr << "rbd-nbd: Unsupported netlink reply" << std::endl;
939 return -NLE_MSGTYPE_NOSUPPORT;
940 }
941
942 if (!msg_attr[NBD_ATTR_INDEX]) {
943 cerr << "rbd-nbd: netlink connect reply missing device index." << std::endl;
944 return -NLE_MSGTYPE_NOSUPPORT;
945 }
946
947 index = nla_get_u32(msg_attr[NBD_ATTR_INDEX]);
948 cfg->devpath = "/dev/nbd" + stringify(index);
949 nbd_index = index;
950
951 return NL_OK;
952}
953
954static int netlink_connect(Config *cfg, struct nl_sock *sock, int nl_id, int fd,
955 uint64_t size, uint64_t flags)
956{
957 struct nlattr *sock_attr;
958 struct nlattr *sock_opt;
959 struct nl_msg *msg;
960 int ret;
961
962 nl_socket_modify_cb(sock, NL_CB_VALID, NL_CB_CUSTOM, netlink_connect_cb, cfg);
963
964 msg = nlmsg_alloc();
965 if (!msg) {
966 cerr << "rbd-nbd: Could not allocate netlink message." << std::endl;
967 return -ENOMEM;
968 }
969
970 if (!genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, nl_id, 0, 0, NBD_CMD_CONNECT,
971 0)) {
972 cerr << "rbd-nbd: Could not setup message." << std::endl;
973 goto free_msg;
974 }
975
976 if (!cfg->devpath.empty()) {
977 ret = parse_nbd_index(cfg->devpath);
978 if (ret < 0)
979 goto free_msg;
980
981 NLA_PUT_U32(msg, NBD_ATTR_INDEX, ret);
982 }
983
984 if (cfg->timeout >= 0)
985 NLA_PUT_U64(msg, NBD_ATTR_TIMEOUT, cfg->timeout);
986
987 NLA_PUT_U64(msg, NBD_ATTR_SIZE_BYTES, size);
988 NLA_PUT_U64(msg, NBD_ATTR_BLOCK_SIZE_BYTES, RBD_NBD_BLKSIZE);
989 NLA_PUT_U64(msg, NBD_ATTR_SERVER_FLAGS, flags);
990
991 sock_attr = nla_nest_start(msg, NBD_ATTR_SOCKETS);
992 if (!sock_attr) {
993 cerr << "rbd-nbd: Could not init sockets in netlink message." << std::endl;
994 goto free_msg;
995 }
996
997 sock_opt = nla_nest_start(msg, NBD_SOCK_ITEM);
998 if (!sock_opt) {
999 cerr << "rbd-nbd: Could not init sock in netlink message." << std::endl;
1000 goto free_msg;
1001 }
1002
1003 NLA_PUT_U32(msg, NBD_SOCK_FD, fd);
1004 nla_nest_end(msg, sock_opt);
1005 nla_nest_end(msg, sock_attr);
1006
1007 ret = nl_send_sync(sock, msg);
1008 if (ret < 0) {
1009 cerr << "rbd-nbd: netlink connect failed: " << nl_geterror(ret)
1010 << std::endl;
1011 return -EIO;
1012 }
1013
1014 dout(10) << "netlink connect complete for " << cfg->devpath << dendl;
1015 return 0;
1016
1017nla_put_failure:
1018free_msg:
1019 nlmsg_free(msg);
1020 return -EIO;
1021}
1022
1023static int try_netlink_setup(Config *cfg, int fd, uint64_t size, uint64_t flags)
1024{
1025 struct nl_sock *sock;
1026 int nl_id, ret;
1027
1028 sock = netlink_init(&nl_id);
1029 if (!sock) {
1030 cerr << "rbd-nbd: Netlink interface not supported. Using ioctl interface."
1031 << std::endl;
1032 return 1;
1033 }
1034
1035 dout(10) << "netlink interface supported." << dendl;
1036
1037 ret = netlink_connect(cfg, sock, nl_id, fd, size, flags);
1038 netlink_cleanup(sock);
1039
1040 if (ret != 0)
1041 return ret;
1042
1043 nbd = open(cfg->devpath.c_str(), O_RDWR);
1044 if (nbd < 0) {
1045 cerr << "rbd-nbd: failed to open device: " << cfg->devpath << std::endl;
1046 return nbd;
1047 }
1048
1049 return 0;
1050}
1051
1052static void handle_signal(int signum)
1053{
1054 int ret;
1055
1056 ceph_assert(signum == SIGINT || signum == SIGTERM);
1057 derr << "*** Got signal " << sig_str(signum) << " ***" << dendl;
1058
1059 if (nbd < 0 || nbd_index < 0) {
1060 dout(20) << __func__ << ": " << "disconnect not needed." << dendl;
1061 return;
1062 }
1063
1064 dout(20) << __func__ << ": " << "sending NBD_DISCONNECT" << dendl;
1065 ret = netlink_disconnect(nbd_index);
1066 if (ret == 1)
1067 ret = ioctl(nbd, NBD_DISCONNECT);
1068
1069 if (ret != 0) {
1070 derr << "rbd-nbd: disconnect failed. Error: " << ret << dendl;
1071 } else {
1072 dout(20) << __func__ << ": " << "disconnected" << dendl;
1073 }
1074}
1075
1076static NBDServer *start_server(int fd, librbd::Image& image)
1077{
1078 NBDServer *server;
1079
1080 server = new NBDServer(fd, image);
1081 server->start();
1082
1083 init_async_signal_handler();
1084 register_async_signal_handler(SIGHUP, sighup_handler);
1085 register_async_signal_handler_oneshot(SIGINT, handle_signal);
1086 register_async_signal_handler_oneshot(SIGTERM, handle_signal);
1087
1088 return server;
1089}
1090
1091static void run_server(Preforker& forker, NBDServer *server, bool netlink_used)
1092{
1093 if (g_conf()->daemonize) {
1094 global_init_postfork_finish(g_ceph_context);
1095 forker.daemonize();
1096 }
1097
1098 if (netlink_used)
1099 server->wait_for_disconnect();
1100 else
1101 ioctl(nbd, NBD_DO_IT);
1102
1103 unregister_async_signal_handler(SIGHUP, sighup_handler);
1104 unregister_async_signal_handler(SIGINT, handle_signal);
1105 unregister_async_signal_handler(SIGTERM, handle_signal);
1106 shutdown_async_signal_handler();
1107}
1108
31f18b77 1109static int do_map(int argc, const char *argv[], Config *cfg)
7c673cae
FG
1110{
1111 int r;
1112
1113 librados::Rados rados;
1114 librbd::RBD rbd;
1115 librados::IoCtx io_ctx;
1116 librbd::Image image;
1117
1118 int read_only = 0;
1119 unsigned long flags;
1120 unsigned long size;
eafe8130 1121 bool use_netlink;
7c673cae 1122
7c673cae
FG
1123 int fd[2];
1124
1125 librbd::image_info_t info;
1126
1127 Preforker forker;
eafe8130 1128 NBDServer *server;
7c673cae
FG
1129
1130 vector<const char*> args;
1131 argv_to_vec(argc, argv, args);
11fdf7f2
TL
1132 if (args.empty()) {
1133 cerr << argv[0] << ": -h or --help for usage" << std::endl;
1134 exit(1);
1135 }
1136 if (ceph_argparse_need_usage(args)) {
1137 usage();
1138 exit(0);
1139 }
7c673cae
FG
1140
1141 auto cct = global_init(NULL, args, CEPH_ENTITY_TYPE_CLIENT,
1142 CODE_ENVIRONMENT_DAEMON,
1143 CINIT_FLAG_UNPRIVILEGED_DAEMON_DEFAULTS);
11fdf7f2 1144 g_ceph_context->_conf.set_val_or_die("pid_file", "");
7c673cae
FG
1145
1146 if (global_init_prefork(g_ceph_context) >= 0) {
1147 std::string err;
1148 r = forker.prefork(err);
1149 if (r < 0) {
1150 cerr << err << std::endl;
1151 return r;
1152 }
7c673cae 1153 if (forker.is_parent()) {
7c673cae
FG
1154 if (forker.parent_wait(err) != 0) {
1155 return -ENXIO;
1156 }
1157 return 0;
1158 }
28e407b8 1159 global_init_postfork_start(g_ceph_context);
7c673cae
FG
1160 }
1161
1162 common_init_finish(g_ceph_context);
1163 global_init_chdir(g_ceph_context);
1164
1165 if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd) == -1) {
1166 r = -errno;
1167 goto close_ret;
1168 }
1169
94b18763
FG
1170 r = rados.init_with_context(g_ceph_context);
1171 if (r < 0)
1172 goto close_fd;
1173
1174 r = rados.connect();
1175 if (r < 0)
1176 goto close_fd;
1177
1178 r = rados.ioctx_create(cfg->poolname.c_str(), io_ctx);
1179 if (r < 0)
1180 goto close_fd;
1181
11fdf7f2
TL
1182 io_ctx.set_namespace(cfg->nsname);
1183
94b18763
FG
1184 r = rbd.open(io_ctx, image, cfg->imgname.c_str());
1185 if (r < 0)
1186 goto close_fd;
1187
1188 if (cfg->exclusive) {
1189 r = image.lock_acquire(RBD_LOCK_MODE_EXCLUSIVE);
1190 if (r < 0) {
1191 cerr << "rbd-nbd: failed to acquire exclusive lock: " << cpp_strerror(r)
1192 << std::endl;
1193 goto close_fd;
1194 }
1195 }
1196
1197 if (!cfg->snapname.empty()) {
1198 r = image.snap_set(cfg->snapname.c_str());
1199 if (r < 0)
1200 goto close_fd;
1201 }
1202
1203 r = image.stat(info, sizeof(info));
1204 if (r < 0)
1205 goto close_fd;
1206
7c673cae 1207 flags = NBD_FLAG_SEND_FLUSH | NBD_FLAG_SEND_TRIM | NBD_FLAG_HAS_FLAGS;
31f18b77 1208 if (!cfg->snapname.empty() || cfg->readonly) {
7c673cae
FG
1209 flags |= NBD_FLAG_READ_ONLY;
1210 read_only = 1;
1211 }
1212
7c673cae
FG
1213 if (info.size > ULONG_MAX) {
1214 r = -EFBIG;
1adf2230
AA
1215 cerr << "rbd-nbd: image is too large (" << byte_u_t(info.size)
1216 << ", max is " << byte_u_t(ULONG_MAX) << ")" << std::endl;
eafe8130 1217 goto close_fd;
7c673cae
FG
1218 }
1219
1220 size = info.size;
1221
eafe8130
TL
1222 r = load_module(cfg);
1223 if (r < 0)
1224 goto close_fd;
1225
1226 server = start_server(fd[1], image);
1227
1228 use_netlink = cfg->try_netlink;
1229 if (use_netlink) {
1230 r = try_netlink_setup(cfg, fd[0], size, flags);
1231 if (r < 0) {
1232 goto free_server;
1233 } else if (r == 1) {
1234 use_netlink = false;
1235 }
7c673cae
FG
1236 }
1237
eafe8130
TL
1238 if (!use_netlink) {
1239 r = try_ioctl_setup(cfg, fd[0], size, flags);
1240 if (r < 0)
1241 goto free_server;
7c673cae
FG
1242 }
1243
eafe8130
TL
1244 r = check_device_size(nbd_index, size);
1245 if (r < 0)
1246 goto close_nbd;
7c673cae
FG
1247
1248 r = ioctl(nbd, BLKROSET, (unsigned long) &read_only);
1249 if (r < 0) {
1250 r = -errno;
1251 goto close_nbd;
1252 }
1253
1254 {
1255 uint64_t handle;
1256
eafe8130
TL
1257 NBDWatchCtx watch_ctx(nbd, nbd_index, use_netlink, io_ctx, image,
1258 info.size);
7c673cae
FG
1259 r = image.update_watch(&watch_ctx, &handle);
1260 if (r < 0)
1261 goto close_nbd;
1262
31f18b77 1263 cout << cfg->devpath << std::endl;
7c673cae 1264
eafe8130 1265 run_server(forker, server, use_netlink);
7c673cae
FG
1266
1267 r = image.update_unwatch(handle);
11fdf7f2 1268 ceph_assert(r == 0);
7c673cae
FG
1269 }
1270
1271close_nbd:
1272 if (r < 0) {
eafe8130
TL
1273 if (use_netlink) {
1274 netlink_disconnect(nbd_index);
1275 } else {
1276 ioctl(nbd, NBD_CLEAR_SOCK);
1277 cerr << "rbd-nbd: failed to map, status: " << cpp_strerror(-r)
1278 << std::endl;
1279 }
7c673cae
FG
1280 }
1281 close(nbd);
eafe8130
TL
1282free_server:
1283 delete server;
7c673cae
FG
1284close_fd:
1285 close(fd[0]);
1286 close(fd[1]);
1287close_ret:
1288 image.close();
1289 io_ctx.close();
1290 rados.shutdown();
1291
1292 forker.exit(r < 0 ? EXIT_FAILURE : 0);
1293 // Unreachable;
1294 return r;
1295}
1296
eafe8130 1297static int do_unmap(Config *cfg)
7c673cae 1298{
eafe8130
TL
1299 int r, nbd;
1300
1301 /*
1302 * The netlink disconnect call supports devices setup with netlink or ioctl,
1303 * so we always try that first.
1304 */
1305 r = netlink_disconnect_by_path(cfg->devpath);
1306 if (r != 1)
1307 return r;
31f18b77 1308
eafe8130 1309 nbd = open(cfg->devpath.c_str(), O_RDWR);
7c673cae 1310 if (nbd < 0) {
eafe8130 1311 cerr << "rbd-nbd: failed to open device: " << cfg->devpath << std::endl;
7c673cae
FG
1312 return nbd;
1313 }
1314
31f18b77
FG
1315 r = ioctl(nbd, NBD_DISCONNECT);
1316 if (r < 0) {
eafe8130 1317 cerr << "rbd-nbd: the device is not used" << std::endl;
7c673cae
FG
1318 }
1319
1320 close(nbd);
31f18b77 1321 return r;
7c673cae
FG
1322}
1323
11fdf7f2
TL
1324static int parse_imgpath(const std::string &imgpath, Config *cfg,
1325 std::ostream *err_msg) {
1326 std::regex pattern("^(?:([^/]+)/(?:([^/@]+)/)?)?([^@]+)(?:@([^/@]+))?$");
1327 std::smatch match;
1328 if (!std::regex_match(imgpath, match, pattern)) {
7c673cae
FG
1329 std::cerr << "rbd-nbd: invalid spec '" << imgpath << "'" << std::endl;
1330 return -EINVAL;
1331 }
1332
31f18b77
FG
1333 if (match[1].matched) {
1334 cfg->poolname = match[1];
1335 }
7c673cae 1336
11fdf7f2
TL
1337 if (match[2].matched) {
1338 cfg->nsname = match[2];
1339 }
1340
1341 cfg->imgname = match[3];
7c673cae 1342
11fdf7f2
TL
1343 if (match[4].matched)
1344 cfg->snapname = match[4];
7c673cae
FG
1345
1346 return 0;
1347}
1348
11fdf7f2 1349static int do_list_mapped_devices(const std::string &format, bool pretty_format)
7c673cae 1350{
11fdf7f2
TL
1351 bool should_print = false;
1352 std::unique_ptr<ceph::Formatter> f;
1353 TextTable tbl;
7c673cae 1354
11fdf7f2
TL
1355 if (format == "json") {
1356 f.reset(new JSONFormatter(pretty_format));
1357 } else if (format == "xml") {
1358 f.reset(new XMLFormatter(pretty_format));
1359 } else if (!format.empty() && format != "plain") {
1360 std::cerr << "rbd-nbd: invalid output format: " << format << std::endl;
1361 return -EINVAL;
7c673cae
FG
1362 }
1363
11fdf7f2
TL
1364 if (f) {
1365 f->open_array_section("devices");
1366 } else {
1367 tbl.define_column("id", TextTable::LEFT, TextTable::LEFT);
1368 tbl.define_column("pool", TextTable::LEFT, TextTable::LEFT);
1369 tbl.define_column("namespace", TextTable::LEFT, TextTable::LEFT);
1370 tbl.define_column("image", TextTable::LEFT, TextTable::LEFT);
1371 tbl.define_column("snap", TextTable::LEFT, TextTable::LEFT);
1372 tbl.define_column("device", TextTable::LEFT, TextTable::LEFT);
7c673cae 1373 }
7c673cae 1374
11fdf7f2
TL
1375 int pid;
1376 Config cfg;
1377 NBDListIterator it;
1378 while (it.get(&pid, &cfg)) {
1379 if (f) {
1380 f->open_object_section("device");
1381 f->dump_int("id", pid);
1382 f->dump_string("pool", cfg.poolname);
1383 f->dump_string("namespace", cfg.nsname);
1384 f->dump_string("image", cfg.imgname);
1385 f->dump_string("snap", cfg.snapname);
1386 f->dump_string("device", cfg.devpath);
1387 f->close_section();
1388 } else {
31f18b77
FG
1389 should_print = true;
1390 if (cfg.snapname.empty()) {
1391 cfg.snapname = "-";
1392 }
11fdf7f2
TL
1393 tbl << pid << cfg.poolname << cfg.nsname << cfg.imgname << cfg.snapname
1394 << cfg.devpath << TextTable::endrow;
31f18b77 1395 }
31f18b77
FG
1396 }
1397
11fdf7f2
TL
1398 if (f) {
1399 f->close_section(); // devices
1400 f->flush(std::cout);
1401 }
31f18b77 1402 if (should_print) {
11fdf7f2 1403 std::cout << tbl;
31f18b77
FG
1404 }
1405 return 0;
1406}
1407
11fdf7f2
TL
1408static bool find_mapped_dev_by_spec(Config *cfg) {
1409 int pid;
1410 Config c;
1411 NBDListIterator it;
1412 while (it.get(&pid, &c)) {
1413 if (c.poolname == cfg->poolname && c.imgname == cfg->imgname &&
1414 c.snapname == cfg->snapname) {
1415 *cfg = c;
1416 return true;
1417 }
1418 }
1419 return false;
1420}
1421
1422
1423static int parse_args(vector<const char*>& args, std::ostream *err_msg,
1424 Command *command, Config *cfg) {
181888fb
FG
1425 std::string conf_file_list;
1426 std::string cluster;
1427 CephInitParameters iparams = ceph_argparse_early_args(
1428 args, CEPH_ENTITY_TYPE_CLIENT, &cluster, &conf_file_list);
7c673cae 1429
11fdf7f2
TL
1430 ConfigProxy config{false};
1431 config->name = iparams.name;
1432 config->cluster = cluster;
181888fb
FG
1433
1434 if (!conf_file_list.empty()) {
1435 config.parse_config_files(conf_file_list.c_str(), nullptr, 0);
1436 } else {
1437 config.parse_config_files(nullptr, nullptr, 0);
1438 }
11fdf7f2 1439 config.parse_env(CEPH_ENTITY_TYPE_CLIENT);
31f18b77 1440 config.parse_argv(args);
181888fb
FG
1441 cfg->poolname = config.get_val<std::string>("rbd_default_pool");
1442
1443 std::vector<const char*>::iterator i;
1444 std::ostringstream err;
31f18b77 1445
7c673cae
FG
1446 for (i = args.begin(); i != args.end(); ) {
1447 if (ceph_argparse_flag(args, i, "-h", "--help", (char*)NULL)) {
224ce89b
WB
1448 return HELP_INFO;
1449 } else if (ceph_argparse_flag(args, i, "-v", "--version", (char*)NULL)) {
1450 return VERSION_INFO;
31f18b77
FG
1451 } else if (ceph_argparse_witharg(args, i, &cfg->devpath, "--device", (char *)NULL)) {
1452 } else if (ceph_argparse_witharg(args, i, &cfg->nbds_max, err, "--nbds_max", (char *)NULL)) {
7c673cae 1453 if (!err.str().empty()) {
31f18b77
FG
1454 *err_msg << "rbd-nbd: " << err.str();
1455 return -EINVAL;
7c673cae 1456 }
31f18b77
FG
1457 if (cfg->nbds_max < 0) {
1458 *err_msg << "rbd-nbd: Invalid argument for nbds_max!";
1459 return -EINVAL;
7c673cae 1460 }
31f18b77 1461 } else if (ceph_argparse_witharg(args, i, &cfg->max_part, err, "--max_part", (char *)NULL)) {
7c673cae 1462 if (!err.str().empty()) {
31f18b77
FG
1463 *err_msg << "rbd-nbd: " << err.str();
1464 return -EINVAL;
7c673cae 1465 }
31f18b77
FG
1466 if ((cfg->max_part < 0) || (cfg->max_part > 255)) {
1467 *err_msg << "rbd-nbd: Invalid argument for max_part(0~255)!";
1468 return -EINVAL;
7c673cae 1469 }
31f18b77 1470 cfg->set_max_part = true;
7c673cae 1471 } else if (ceph_argparse_flag(args, i, "--read-only", (char *)NULL)) {
31f18b77 1472 cfg->readonly = true;
7c673cae 1473 } else if (ceph_argparse_flag(args, i, "--exclusive", (char *)NULL)) {
31f18b77 1474 cfg->exclusive = true;
11fdf7f2
TL
1475 } else if (ceph_argparse_witharg(args, i, &cfg->timeout, err, "--timeout",
1476 (char *)NULL)) {
1477 if (!err.str().empty()) {
1478 *err_msg << "rbd-nbd: " << err.str();
1479 return -EINVAL;
1480 }
1481 if (cfg->timeout < 0) {
1482 *err_msg << "rbd-nbd: Invalid argument for timeout!";
1483 return -EINVAL;
1484 }
1485 } else if (ceph_argparse_witharg(args, i, &cfg->format, err, "--format",
1486 (char *)NULL)) {
1487 } else if (ceph_argparse_flag(args, i, "--pretty-format", (char *)NULL)) {
1488 cfg->pretty_format = true;
eafe8130
TL
1489 } else if (ceph_argparse_flag(args, i, "--try-netlink", (char *)NULL)) {
1490 cfg->try_netlink = true;
7c673cae
FG
1491 } else {
1492 ++i;
1493 }
1494 }
1495
11fdf7f2 1496 Command cmd = None;
7c673cae
FG
1497 if (args.begin() != args.end()) {
1498 if (strcmp(*args.begin(), "map") == 0) {
1499 cmd = Connect;
1500 } else if (strcmp(*args.begin(), "unmap") == 0) {
1501 cmd = Disconnect;
1502 } else if (strcmp(*args.begin(), "list-mapped") == 0) {
1503 cmd = List;
1504 } else {
31f18b77
FG
1505 *err_msg << "rbd-nbd: unknown command: " << *args.begin();
1506 return -EINVAL;
7c673cae
FG
1507 }
1508 args.erase(args.begin());
1509 }
1510
1511 if (cmd == None) {
31f18b77
FG
1512 *err_msg << "rbd-nbd: must specify command";
1513 return -EINVAL;
7c673cae
FG
1514 }
1515
1516 switch (cmd) {
1517 case Connect:
1518 if (args.begin() == args.end()) {
31f18b77
FG
1519 *err_msg << "rbd-nbd: must specify image-or-snap-spec";
1520 return -EINVAL;
7c673cae 1521 }
11fdf7f2 1522 if (parse_imgpath(*args.begin(), cfg, err_msg) < 0) {
31f18b77 1523 return -EINVAL;
11fdf7f2 1524 }
7c673cae
FG
1525 args.erase(args.begin());
1526 break;
1527 case Disconnect:
1528 if (args.begin() == args.end()) {
11fdf7f2 1529 *err_msg << "rbd-nbd: must specify nbd device or image-or-snap-spec";
31f18b77 1530 return -EINVAL;
7c673cae 1531 }
11fdf7f2
TL
1532 if (boost::starts_with(*args.begin(), "/dev/")) {
1533 cfg->devpath = *args.begin();
1534 } else {
1535 if (parse_imgpath(*args.begin(), cfg, err_msg) < 0) {
1536 return -EINVAL;
1537 }
1538 if (!find_mapped_dev_by_spec(cfg)) {
1539 *err_msg << "rbd-nbd: " << *args.begin() << " is not mapped";
1540 return -ENOENT;
1541 }
1542 }
7c673cae
FG
1543 args.erase(args.begin());
1544 break;
1545 default:
1546 //shut up gcc;
1547 break;
1548 }
1549
1550 if (args.begin() != args.end()) {
31f18b77
FG
1551 *err_msg << "rbd-nbd: unknown args: " << *args.begin();
1552 return -EINVAL;
1553 }
1554
11fdf7f2 1555 *command = cmd;
31f18b77
FG
1556 return 0;
1557}
1558
1559static int rbd_nbd(int argc, const char *argv[])
1560{
1561 int r;
1562 Config cfg;
1563 vector<const char*> args;
1564 argv_to_vec(argc, argv, args);
1565
1566 std::ostringstream err_msg;
11fdf7f2 1567 r = parse_args(args, &err_msg, &cmd, &cfg);
224ce89b 1568 if (r == HELP_INFO) {
31f18b77 1569 usage();
11fdf7f2 1570 return 0;
224ce89b
WB
1571 } else if (r == VERSION_INFO) {
1572 std::cout << pretty_version_to_str() << std::endl;
1573 return 0;
11fdf7f2 1574 } else if (r < 0) {
31f18b77
FG
1575 cerr << err_msg.str() << std::endl;
1576 return r;
7c673cae
FG
1577 }
1578
1579 switch (cmd) {
1580 case Connect:
31f18b77 1581 if (cfg.imgname.empty()) {
7c673cae 1582 cerr << "rbd-nbd: image name was not specified" << std::endl;
31f18b77 1583 return -EINVAL;
7c673cae
FG
1584 }
1585
31f18b77 1586 r = do_map(argc, argv, &cfg);
7c673cae 1587 if (r < 0)
31f18b77 1588 return -EINVAL;
7c673cae
FG
1589 break;
1590 case Disconnect:
eafe8130 1591 r = do_unmap(&cfg);
7c673cae 1592 if (r < 0)
31f18b77 1593 return -EINVAL;
7c673cae
FG
1594 break;
1595 case List:
11fdf7f2 1596 r = do_list_mapped_devices(cfg.format, cfg.pretty_format);
7c673cae 1597 if (r < 0)
31f18b77 1598 return -EINVAL;
7c673cae
FG
1599 break;
1600 default:
1601 usage();
d2e6a577 1602 break;
7c673cae
FG
1603 }
1604
1605 return 0;
1606}
1607
1608int main(int argc, const char *argv[])
1609{
31f18b77
FG
1610 int r = rbd_nbd(argc, argv);
1611 if (r < 0) {
1612 return EXIT_FAILURE;
1613 }
1614 return 0;
7c673cae 1615}